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

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

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/61v5i2tn2mc2gmk8u1up.png)

こんなやつ。

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

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

見ていきましょう。

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

```jsx
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
// 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に渡します。

```javascript
// 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エンドポイントを作成します。

```javascript
// 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)
}
```

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

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

これで実装は終了です。

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

PostgreSQLサーバは[render]((https://render.com/))で用意しました。

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

[How to deploy a Prisma app to Vercel](https://www.prisma.io/docs/guides/deployment/deployment-guides/deploying-to-vercel)

```js
// package.json

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

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

renderのPostgreSQLからURLを取得して、
![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/2ceug6vaav6yerkxga0p.png)

Vercelにセットします。
![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/njb5go3nvkfusb5ma8gy.png)

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

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

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