🥗

Next.jsとVercel の静的サイトにPrismaでデータベースをそっと"添える"

2022/11/08に公開約3,800字

先日、自分のウェブサイトに「いいね!」ボタンを付けました。

Image description

こんなやつ。

これまでは、静的なWebサイトにちょっとしたデータベース処理を加えようとすると、アプリの実装を静的サイトジェネレータからRuby on Railsなどのサーバサイドフレームワークに変更するか、別のDBサーバを用意してAPIを叩くしかありませんでした。機能によってはその追加がアーキテクチャ全体に影響を及ぼしていたのです。

しかし、今回の実験で、Next.jsとVercelがアーキテクチャを大きく変えずにウェブサイトの機能を拡張できる、非常にアプリケーションファーストな思想を持っていることを実感しました。
これは、VercelはServerless FunctionでRest APIを受け取ることができることが大きく寄与しています。

見ていきましょう。

まず、「いいね!」のUIを実装します。

import { useState } from 'react'
import { MdThumbUp } from 'react-icons/md'

export default function Component(props) {
 // props.likeCountをserver sideから取得
  const [likeCount, setLikeCount] = useState(props.likeCount)
  const [liked, setLiked] = useState(false)
  const createLike = async () => {
    if (liked === false) {
      // TODO: create like
      setLikeCount(likeCount + 1)
      setLiked(true)
    }
  }
  return (
    <>
      <span className={liked ? 'text-blue-500' : undefined}>
        <span
          onClick={createLike}
          className="ml-4 mb-4 align-bottom cursor-pointer inline-block bg-white border-2 border-gray-800 rounded-full px-4">
          <MdThumbUp className="inline mr-2" />
          {likeCount}
        </span>
      </span>
      <span className="inline-block ml-4 mb-4 align-bottom">
        {liked && <span>Thank you!</span>}
      </span>
    </>
  )
}

今回はORMとして、Prismaを利用したいと思います。

yarn add prisma

これがスキーマファイルである prisma/schema.prisma です。
今回は非常にシンプルなモデルです。

// prisma/schema.prisma

generator client {
  provider = "prisma-client-js"
}

datasource db {
  provider = "postgresql"
  url      = env("DATABASE_URL")
}

model Like {
  id    Int     @id @default(autoincrement())
}

そして、マイグレーションを生成して適用します。

yarn run prisma migrate dev

次に、データベースにアクセスするためのprismaクライアントを生成します。

yarn run prisma generate

次に、実際にデータベースにアクセスする部分を実装します。

あるページから getServerSideProps (サーバーサイドレンダリング) という関数をエクスポートすると、Next.jsは getServerSideProps が返すデータを用いて、リクエストごとにこのページをプリレンダリングします。ここでLikeをカウントしてpropsに渡します。

// pages/index.js

import { PrismaClient } from '@prisma/client'
const prisma = new PrismaClient()

export default function Home(props) {
  //...
}

export async function getServerSideProps(context) {
  const likeCount = await prisma.like.count()
  return {
    props: { likeCount: likeCount }
  }
}

続いて、Likeのレコードを作成するためのpostエンドポイントを作成します。

// pages/api/like/create
import { PrismaClient } from '@prisma/client'
const prisma = new PrismaClient()

export default async function handler(req, res) {
  const like = await prisma.like.create()
  res.status(201).json(like)
}

で、いいねボタンを押したときに発動するceateLikecallbackにAPIを叩く処理を追加します。

  const createLike = async () => {
    if (liked === false) {
      fetch('/api/like/create')
      setLikeCount(likeCount + 1)
      setLiked(true)
    }
  }

これで実装は終了です。

次に、VercelでPrismaを使うための設定と、PostgreSQLサーバーを用意しましょう。

PostgreSQLサーバはrenderで用意しました。

VercelのドキュメントにPrismaをVercelへのデプロイ方法をまとめてくれているので、これに沿ってやってみます。

How to deploy a Prisma app to Vercel

// package.json

{
  "scripts": {
    "vercel-build": "prisma generate && prisma migrate deploy && next build" 
  }
}

次に、PrismaからVercelへの環境変数を設定します。

renderのPostgreSQLからURLを取得して、
Image description

Vercelにセットします。
Image description

あとは環境変数のセットを契機に勝手にデプロイが走るので、それで終わりです。

大きなアーキテクチャの変更をすることなく、データベースの機能を追加することができました。とても自由な開発体験で、Next.jsとVercelがやろうとしていることが少し分かったような気がします。

お読みいただきありがとうございました。

Discussion

ログインするとコメントできます