Closed9
Next.js + Prisma + PlanetScale
やること
- PlanetScaleでDBのセットアップ
- Prismaのインストール
- データモデル定義
- API RoutesでAPI実装
HobbyプランなのでVercelのリージョンはIAD1(WashingtonDC, USA)
SSRするからPlanetScaleのリージョンはus-east-1にしとく
$ npm install prisma typescript ts-node @types/node --save-dev
$ npx prisma init
.envとschema.prismaが生成されるので、↓で修正
// .env
DATABASE_URL="mysql://root@127.0.0.1:3309/<DB名>"
// schema.prisma
generator client {
provider = "prisma-client-js"
previewFeatures = ["referentialIntegrity"]
}
datasource db {
provider = "mysql"
url = env("DATABASE_URL")
referentialIntegrity = "prisma"
}
model User {
id Int @default(autoincrement()) @id
name String
email String?
}
referentialIntegrity
PlanetScaleは外部キー制約をサポートしてないが、Prismaはリレーションを表現するのにデフォで外部キー制約を使ってるため必要になるプロパティ
参考
PlanetScale CLIをインストール
認証
$ pscale auth login
Confirmation Code: XXXXXXXX
If something goes wrong, copy and paste this URL into your browser: https://auth.planetscale.com/oauth/device?user_code=XXXXXXXX
Successfully logged in.
DBへのローカルプロキシを実行する
$ pscale connect <DB名> main --port 3309
Secure connection to database xxxxx and branch main is established!.
Local address to connect your application: 127.0.0.1:3309 (press ctrl-c to quit)
PlanetScaleのスキーマに反映
$ npx prisma db push
ちゃんと同期されたか確認
$ pscale shell prisma-playground main
productionにpromote
$ pscale branch promote <DB名> main
import { PrismaClient } from '@prisma/client'
import type { NextApiRequest, NextApiResponse } from 'next'
const prisma = new PrismaClient()
export default async function handler(
req: NextApiRequest,
res: NextApiResponse
) {
if (req.method === 'GET') {
return await getUsers(req, res)
} else if (req.method === 'POST') {
return await createUser(req, res)
} else {
return res.status(405).json({ message: 'Method not allowed' })
}
}
async function getUsers(req: NextApiRequest, res: NextApiResponse) {
try {
const users = await prisma.user.findMany()
return res.status(200).json(users)
} catch (error) {
res.status(500).json({ error: 'Error' })
}
}
async function createUser(req: NextApiRequest, res: NextApiResponse) {
const { name, email } = req.body
try {
const newEntry = await prisma.user.create({
data: { name, email },
})
return res.status(200).json(newEntry)
} catch (error) {
res.status(500).json({ error: 'Error' })
}
}
import { User } from '@prisma/client'
import { useEffect, useState } from 'react'
export default function Test() {
const handleClick = async () => {
const body = { name, email }
try {
const response = await fetch('/api/user', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(body),
})
if (response.status !== 200) {
console.log('error')
} else {
setName('')
setEmail('')
console.log('success')
}
} catch (error) {
console.log(error)
}
}
const getUsers = async () => {
const response = await fetch('/api/user')
const users = await response.json()
setUsers(users)
}
const [users, setUsers] = useState<User[]>([])
const [name, setName] = useState('')
const [email, setEmail] = useState('')
useEffect(() => {
getUsers()
}, [])
return (
<div>
<ul>
{users ? users.map((user) => <li key={user.id}>{user.name}</li>) : null}
</ul>
<input
type='text'
name='name'
onChange={(e) => setName(e.target.value)}
value={name}
/>
<button onClick={handleClick}>click</button>
</div>
)
}
PlanetScaleで接続用文字列を取得
Vercelの環境変数にセット
redeployしておしまい
このスクラップは2023/01/05にクローズされました