エンジニアの岸本です。 現在、総額1億500万ドル(約120億円)を調達したことで話題になった、「PlanetScale」というサーバーレスデータベースを皆さんご存知ですか? docs.planetscale.com
今回は簡易掲示板を実際に作りながら、PlanetScale
の導入から使用方法を共有したいと思います。
※アカウント作成等の手順は、以下記事を参考にすることをおすすめします。
qiita.com
構成
- ビルドツール
- Vite
- DB
- PlanetScale
- server
- Netlify
- アプリケーション
- preact, Typescript, Prisma
※ preact の代わりにNext.js
を使っても手順に大差はないので、適宜ご自身の仕様と比較しながら読み進めてもらえればと思います。
始める前に結論と所感
- 無料枠が大きいので個人開発で遊ぶのにも十分だと思う
- UXがとても良い。ほとんどのプログラマーが
github
ユーザーだと思うので、直感的に操作することが可能 - mysqlが抱えていた問題(本番DBと開発DBの情報に差分が発生して意図しないバグを生じてしまう)を上手く解決してくれそう(上記に記載したように、githubのようなユーザー体験が提供されているためschema変更のrevartなんかも可能)
始める前の準備
PlanetScale
とNetlify
はそれぞれcli
が用意されているのでインストールします
$ npm install netlify-cli -g $ netlify // インストールされているか確認 ⬥ Netlify CLI Read the docs: https://www.netlify.com/docs/cli Support and bugs: https://github.com/netlify/cli/issues VERSION netlify-cli/9.16.6 darwin-x64 node-v14.17.0 $ npm i scale -g $ which sclae
Vite プロジェクト作成
適当なディレクトリを作成します
$ mkdir ディレクトリ名 $ cd ディレクトリ名 $ npm init vite@latest .
プロジェクト名等、何を利用するか問われるので以下の項目を入力・選択します
$ npm init vite@latest npx: 6個のパッケージを3.977秒でインストールしました。 ✔ Project name: … プロジェクト名 ✔ Select a framework: › preact ✔ Select a variant: › preact-ts $ cd プロジェクト名 $ npm i
プロジェクト作成完了後、ローカルサーバを走らせます
$ netlify dev ◈ Netlify Dev ◈ ◈ Ignored general context env var: LANG (defined in process) ◈ Starting Netlify Dev with Vite ┌─────────────────────────────────────────────────┐ │ │ │ ◈ Server now ready on http://localhost:8888 │ │ │ └─────────────────────────────────────────────────┘
補足:) netlify dev
コマンドに関しては以下を参照ください
http://localhost:8888
でプロジェクトが正常に立ち上がり以下の画面が確認できるはずです。
PlanetScale の設定
ターミナルからPlanetScale
へlogin
します
$ pscale auth login Confirmation Code: ここに認証用コードが表示される
ブラウザでplanetscale
認証ページが自動で開かれるので、ターミナル上に出力される認証用コードと一致しているか確認してください。
問題なければブラウザ上に表示されている、Confirme code
ボタンをクリックします
次に、開発用のブランチを作成します
$ pscale branch create データベース名 dev // devは任意のブランチ名
※ ブランチ機能があり、GitのようにDBを管理できる所が良いですよね
shadowブランチを作成する ※ このブランチはPrismaのmigrations用に使われる
$ pscale branch create データベース名 shadow // devは任意のブランチ名
ローカルからデータベースへ接続して、正常に上記設定が行われているか確認します
$ pscale connect データベース名 dev[ブランチ名] --port 3309 $ pscale connect データベース名 shadow[ブランチ名] --port 3310
エディタで本プロジェクトコードを展開して.env
ファイルを追加して以下を記述します
DATABASE_URL="mysql://root@127.0.0.1:3309/データベース名" SHADOW_DATABASE_URL="mysql://root@127.0.0.1:3310/データベース名"
Prismaの設定
プロジェクトにprisma
を追加します
npm i -D prisma npm i @prisma/client
schemaファイルを生成します
npx prisma init
schema.prisma
を以下のように修正
generator client { provider = "prisma-client-js" previewFeatures = ["referentialIntegrity"] } datasource db { provider = "mysql" url = env("DATABASE_URL") shadowDatabaseUrl = env("SHADOW_DATABASE_URL") referentialIntegrity = "prisma" } /* 以下、投稿情報用モデルです */ model Post { id Int @id @default(autoincrement()) createdAt DateTime @default(now()) updatedAt DateTime @default(now()) title String @db.VarChar(255) content String }
上記で定義した schema を基にテーブルを作成します
$ npx prisma migrate dev
無事に作成できたらprisma studio
を使用して検証用データを作成します
$ npx prisma studio Environment variables loaded from .env Prisma schema loaded from prisma/schema.prisma Prisma Studio is up on http://localhost:5555 /* ブラウザでhttp://localhost:5555を開く */
schema
ファイルを基に prisma client
を生成します
npx prisma generate
コマンドはスキーマを取得し、typescript定義を持つprismaクライアントを作成して、データベースとのやり取りに必要なものを自動で準備してくれるコマンドです
$ npx prisma generate Environment variables loaded from .env Prisma schema loaded from prisma/schema.prisma ✔ Generated Prisma Client (3.12.0 | library) to ./node_modules/@prisma/client in 70ms You can now start using Prisma Client in your code. Reference: https://pris.ly/d/client import { PrismaClient } from '@prisma/client' const prisma = new PrismaClient()
serverless functionを定義
プロジェクトのルートディレクトリにnetlify/functions/posts.tsを作成します. ファイル内は以下のような構成にします. 処理の内容は post した情報をDBから取得して返すだけのシンプルなものになってます.
import { PrismaClient } from '@prisma/client'; const prisma = new PrismaClient(); export async function handler() { try { const posts = await prisma.post.findMany(); return { statusCode: 200, header: { 'Content-Type': 'application/json', }, body: JSON.stringify(posts), }; } catch (error) { return { statusCode: 500, body: JSON.stringify(error), }; } }
ちなみに、Netlify の Netlify function とは Netlify が提供しているアドオンの1つで、サーバーサイドの機能を簡単に公開できるサービスです。AWS Lambda をラップして使いやすいように提供されており、 AWSにアカウントを用意する必要もありません。NetlifyのプロジェクトとGitリポジトリを連携するだけでOK。Netlifyが代わりにデプロイをしてくれる上に、HTTPで呼び出せる機能がすぐに用意できる代物です。
上記コードが正常に機能するかテストしましょう. ローカルサーバを起動します(もし起動していなければ).
$ netlify dev ◈ Netlify Dev ◈ ◈ Ignored build settings env var: DATABASE_URL (defined in .env file) ◈ Injected .env file env var: DATABASE_URL ◈ Ignored general context env var: LANG (defined in process) ◈ Injected .env file env var: SHADOW_DATABASE_URL ◈ Loaded function post. ◈ Loaded function posts. ◈ Functions server is listening on 62987 ◈ Starting Netlify Dev with Vite > prisma-serverless@0.0.0 dev /Users/ryoma_kishimoto/Desktop/prisma-serverless-planetscale-netlify/prisma-serverless > vite vite v2.9.5 dev server running at: > Local: http://localhost:3000/ > Network: use `--host` to expose ready in 540ms. ┌─────────────────────────────────────────────────┐ │ │ │ ◈ Server now ready on http://localhost:8888 │ │ │ └─────────────────────────────────────────────────┘
posts を返すか確認します.
http://localhost:8888/.netlify/functions/posts
レスポンスが確認できたらOKです.
次は実際にデータを post する処理をプロジェクトのルートディレクトリ(netlify/functions/post.ts)に作成しましょう.
import { PrismaClient } from '@prisma/client'; const prisma = new PrismaClient(); export async function handler(event) { const { title, content } = JSON.parse(event.body); try { await prisma.post.create({ data: { title, content }, }); return { statusCode: 200, body: 'post created', }; } catch (error) { return { statusCode: 500, body: JSON.stringify(error), }; } }
正常に起動するか確認します. VSCodeの拡張機能でThunder Clientという優れものがあるので、検証機能としておすすめです. https://www.thunderclient.com/
簡単にですが使い方を紹介します.
ブラウザからのPOST機能実装
以下のコードをそのままsrc/app.tsxへcopy & pasteしちゃいましょう.
import { useState, useEffect } from 'preact/hooks'; type Post = { id: number; title: string; content: string; createdAt: Date; updatedAt: Date; }; export function App() { const [loadPosts, setLoadPost] = useState(true); const [title, setTitle] = useState(''); const [content, setContent] = useState(''); const [posts, setPosts] = useState([]); useEffect(() => { async function load() { if (!loadPosts) { return; } const allPosts = await fetch('/.netlify/functions/posts').then((res) => res.json() ); setPosts(allPosts); setLoadPost(false); } load(); }, [loadPosts]); async function handleSubmit(event: any) { event.preventDefault(); await fetch('/.netlify/functions/post', { method: 'POST', body: JSON.stringify({ title, content }), }); setTitle(''); setContent(''); setLoadPost(true); } return ( <> <h1>投稿画面</h1> <ul> {posts.map((post: Post) => ( <li key={post.id}> <h3>{post.title}</h3> <p>Created {new Date(post.createdAt).toLocaleString()}</p> <p>Updated {new Date(post.updatedAt).toLocaleString()}</p> </li> ))} </ul> <h2>投稿しよう</h2> <form onSubmit={handleSubmit}> <label htmlFor='title'>Title</label> <input type='text' id='title' name='title' value={title} onChange={(e) => setTitle((e.target as HTMLInputElement).value)} /> <label htmlFor='content'>content</label> <input type='content' id='content' name='content' value={content} onChange={(e) => setContent((e.target as HTMLInputElement).value)} /> <button type='submit'>Save</button> </form> </> ); }
一応、cssも記述します.
html, body { font-family: 'Helvetica Neue', arial, sans-serif; } button, label { display: block; margin-top: 1rem; }
以下画面がブラウザ上で確認できるはずなので、正常に「投稿→投稿されたPOSTが表示」されるか挙動チェックしましょう.
PlanetScaleのProduction環境DBの準備
PlanetScale のコンソール画面上からmainブランチを選択します.
mainブランチをproduction用としてpromote
任意のブランチをmainブランチにdeployするために、deploy requestを上げます.(gitのpull requestに近いイメージ) ターミナルから以下コマンドを入力
pscale deploy-request create データベース名 dev // devは任意のブランチ名
PlanetScaleのコンソール画面上にある、「Deploy requests」を選択すると、deploy requestが確認できます.
summaryタブから「Add changes to deploy queue」ボタンをクリックします.
Netlifyへアプリケーションをデプロイ
丁寧にまとめられている、以下記事を参考にして下さい. https://qiita.com/suin/items/743fe6252ad8af425c5e
「Site settings」をクリックし、画面左に表示されている「Build & deploy」タブを選択します.
「Build settings」項目で以下のように設定し、下部の「save」ボタンをクリックします.
PlanetScaleからDBへのURLを取得します. 次にコンソール画面上の「overview」タブを選択し、画面右の「connect」ボタンをクリックします.
「connect with」ドロップダウンメニューから「Prisma」を選択し、画面右のコピーアイコンをクリックしたら適当なメモ帳等にペーストしておきましょう.
Netlifyへ戻り、「Site settings」をクリック、画面左に表示されている「Build & deploy」タブを選択して、以下スクショのように設定します(valueには先ほどコピペした、DATABASE_URL情報のここから→mysql://~をペーストする)
これで終了です. もし正常にdeployされていない場合は、「Deploy」タブから「Trigger deploy」ボタンをクリックして再度、deployを行なって下さい.
補足:) 以下スクショ内のリンクをクリックすると、Netlify にデプロイされたページへ遷移することができます.