【備忘録】Next.js でsupabaseとprismaを利用する

# はじめに
SupabaseやPrismaは、Next.jsのアプリ開発でよく使うツールなので、
これらを利用するための手順を個人的にまとめてみました。

# 1. `supabase`のセットアップ
まずはsupabaseにサインインしましょう。
特に詰まるところもないと思います。

https://supabase.com/

サインイン後、ダッシュボード画面に遷移するので、
`New Project`をクリックして、新規プロジェクトを作成します。

<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3947973/0bd489f8-79b9-026a-f533-dc5f90f3653d.jpeg" width=80%>

:::note warn
無料プランでは、プロジェクトを最大2つまでしか作成できません。
新たにプロジェクトを作成する場合は、別のアカウントを用意するか、
有料プランをご検討ください。
:::

Project name、Database Password、Regionを入力します。
Database Passwordは後で使用するため、しっかりとメモしておきましょう。

<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3947973/06b6ff92-5347-fe64-74af-14dfa40aa31e.jpeg" width=80%>

数秒ほど待機するとプロジェクトが作成されます。

ヘッダーの`Connect`をクリックします。

![iScreen Shoter - Google Chrome - 250103104312.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3947973/be53aeeb-ae9c-024b-cca8-ef76f49b1255.jpeg)

接続先のURLが表示されるので、`Session pooler`のパスをコピーします。

![iScreen Shoter - Google Chrome - 250103105450.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3947973/069ddb48-dad2-f92c-4ca3-1241b0039536.jpeg)

:::note warn
`Transaction`モードと`Session`モードがありますが、
ここではSession poolerを使用してください。
Transactionモードでは、Prismaのマイグレーションが失敗することがあります。
[【supabase】Prismaのmigrationがいつまでも終わらない](https://qiita.com/Kengo2003/items/be55b931f22e87010436)
- Transaction mode: 6543
- Session mode: 5432
:::

# 2. `Next.js`のプロジェクトを作成
次に、ターミナルからNext.jsのプロジェクトを作成します。
```bash
npx create-next-app@latest
```

# 3. prismaの設定
作成したプロジェクトをVSCodeなどのエディタで開いてください。
まずはターミナルからPrismaをインストールします。
```
npm i prisma
npx prisma init
```

:::note warn
最新のNode.jsバージョン（23系）では、Prismaがまだ対応していないため、
Prismaの初期化に失敗することがあります。
その場合は、以下の記事を参考にしてください。 
[prismaを初期化したら謎のメッセージ(Error: (0 , KSe.isError) is not a function)が表示されて失敗した話](https://qiita.com/xYasi/items/ffaf78fafba7bf467e37)
:::

無事に初期化が完了すると、プロジェクト内に`schema.prisma`が作成されます。

![iScreen Shoter - Cursor - 250103112239.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3947973/aa159c7f-b5c7-6bd7-a7c1-83d0d754bb32.jpeg)

schema.prismaには雛形が自動生成されます。
`.env`ファイルを作成し、接続情報を設定しましょう。
![iScreen Shoter - Cursor - 250103112433.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3947973/21764b99-fced-8f27-95cc-11ae6fcb31bd.jpeg)

`.env`ファイルに`supabase`のセットアップ時にメモしたパスを貼り付けましょう。
`[YOUR-PASSWORD]`の部分は自分で設定したパスワードに書き換えて下さい。
![iScreen Shoter - Cursor - 250103113133.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3947973/d4282dc9-aa7e-4f8a-2d41-247b880c9573.jpeg)

# 4. スキーマのマイグレーション
`schema.prisma`に、作成したいスキーマを追記します。
```typescript:prisma/schema.prisma
model Todo {
  id          String   @id @default(cuid())
  content     String?
  isCompleted Boolean  @default(false)
  createdAt   DateTime @default(now())
  updateAt    DateTime @updatedAt
}
```
prismaの書き方については本記事では割愛します。

スキーマを作成した後、ターミナルからマイグレーションを実行します。
```
npx prisma migrate dev --name init
```

マイグレーションに成功すると、プロジェクト内にマイグレーションファイル
（実行したSQL）が生成され、Supabaseにテーブルが自動的に作成されます。
![iScreen Shoter - Cursor - 250103114339.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3947973/d5dd2549-7194-7594-1905-3d1a594f33c9.jpeg)

![iScreen Shoter - Google Chrome - 250103114557.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3947973/9929dffa-1fb7-d874-0051-048398ca9a8e.jpeg)

これでセットアップは完了です。

# 5. prismaを利用してみる。
Prismaを利用するために、まずクライアントを作成します。
src/lib/prisma.tsを作成し、以下のコードをコピーしてください。
```typescript:src/lib/prisma.ts
import { PrismaClient } from "@prisma/client";

const globalForPrisma = global as unknown as { prisma: PrismaClient };

export const prisma =
  globalForPrisma.prisma || new PrismaClient();

if (process.env.NODE_ENV !== "production") globalForPrisma.prisma = prisma;
```
:::note warn
Next.jsはコードの変更を即座に反映するため、頻繁にリロードが発生します。
その度にPrismaクライアントが再生成されてしまうため、
上記のように単一のクライアントを生成する実装が推奨されています。
[Prisma Client の使用に関するベスト プラクティス](https://www.prisma.io/docs/orm/more/help-and-troubleshooting/nextjs-help)
:::

後は、利用したい箇所でPrismaのコードを使用してデータを取得・操作できます。
```tsx:src/app/pages.tsx
import { prisma } from "@/lib/prisma";

export default async function Home() {
  const todos = await prisma.todo.findMany();
  return (
    todos.map((todo) => (<div key={todo.id}>{todo.content}</div>))
  );
}
```