Closed9

Next.js + Prisma + PlanetScale

isingising

やること

  1. PlanetScaleでDBのセットアップ
  2. Prismaのインストール
  3. データモデル定義
  4. API RoutesでAPI実装
isingising

HobbyプランなのでVercelのリージョンはIAD1(WashingtonDC, USA)
SSRするからPlanetScaleのリージョンはus-east-1にしとく

isingising

https://www.prisma.io/docs/getting-started

$ 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?
}
isingising

PlanetScale CLIをインストール
https://docs.planetscale.com/concepts/planetscale-environment-setup

認証

$ 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
isingising
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' })
  }
}
isingising
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>
  )
}
isingising

PlanetScaleで接続用文字列を取得

Vercelの環境変数にセット
redeployしておしまい

このスクラップは2023/01/05にクローズされました