Open8

Prisma

kouichi.hoshikouichi.hoshi

構築済みのNext.js環境にPrismaをインストールする

npx prisma init --datasource-provider sqlite

--datasource-provider sqliteをつけると、SQLiteの設定が追加される。
prisma.config.tsschema.prismaが生成される。

prisma.config.tsimport "dotenv/config"を追記する。
https://www.prisma.io/docs/guides/nextjs

// prisma.config.ts

import "dotenv/config"
import { defineConfig, env } from "prisma/config";

export default defineConfig({
  schema: "prisma/schema.prisma",
  migrations: {
    path: "prisma/migrations",
  },
  engine: "classic",
  datasource: {
    url: env("DATABASE_URL"),
  },
});
// prisma/schema.prisma

// This is your Prisma schema file,
// learn more about it in the docs: https://pris.ly/d/prisma-schema

generator client {
  provider = "prisma-client"
  output   = "../src/generated/prisma"
}

datasource db {
  provider = "sqlite"
}
kouichi.hoshikouichi.hoshi

.envファイルも生成された。

# Environment variables declared in this file are NOT automatically loaded by Prisma.
# Please add `import "dotenv/config";` to your `prisma.config.ts` file, or use the Prisma CLI with Bun
# to load environment variables from .env files: https://pris.ly/prisma-config-env-vars.

# Prisma supports the native connection string format for PostgreSQL, MySQL, SQLite, SQL Server, MongoDB and CockroachDB.
# See the documentation for all the connection string options: https://pris.ly/d/connection-strings

DATABASE_URL="file:./dev.db"

※Turbopackを使っていると、開発環境実行時にdev.dbにアクセスできない問題ががあるらしい。
DATABASE_URLをフルパスにするとひとまずエラーが回避できたが、これが正しいかどうかはわからない。練習なので、ひとまずTurbopack無しのNext.js v15を使う。
https://www.prisma.io/docs/guides/nextjs#25-seed-the-database

kouichi.hoshikouichi.hoshi

schema.prismaにモデルを定義する。

// prisma/schema.prisma

model Note {
  id        String   @id @default(cuid())
  title     String
  content   String
  createdAt DateTime @default(now())
  updatedAt DateTime @updatedAt
}

npx prisma db pushを実行するとdev.dbが作成され、テーブルが反映される。

npx prisma studioを実行するとPrisma Studioが起動する。

ブラウザでhttp://localhost:5555/にアクセスするとUIが表示されデータベースの中身を確認できる。

kouichi.hoshikouichi.hoshi

いったんまとめ。

https://www.prisma.io/docs/guides/nextjs を参考に、Next.jsにローカルのSQLiteをセットアップする手順を以下にまとめ。
Next.js v15.5.6(Turbopack未使用)
Prisma v6.18.0

  • npx create-next-app@latest nextjs-prisma 実行
  • cd nextjs-prisma 実行してディレクトリ移動
  • npm install prisma tsx --save-dev 実行
  • npm install @prisma/client dotenv 実行
  • npx prisma init --datasource-provider sqlite 実行
  • prisma.config.tsにimport "dotenv/config" 追記
  • schema.prismaにモデルを追加
    • npx prisma generate 実行
  • npx prisma db push 実行

ここまでで、データベースにスキーマが定義された状態になる。

kouichi.hoshikouichi.hoshi

プロジェクトの直下にlib/prisma.tsを作成。

import { PrismaClient } from '../app/generated/prisma/client'

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

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

if (process.env.NODE_ENV !== 'production') globalForPrisma.prisma = prisma

export default prisma

このファイルの役目は、

このファイルはPrisma Clientを作成し、グローバルオブジェクトにアタッチします。
これにより、アプリケーション内でクライアントのインスタンスが1つだけ作成されるようになります。
これは、開発モードでNext.jsとPrisma ORMを使用する際に発生する可能性のある、ホットリロードに関する問題を解決するのに役立ちます。

とのこと。

kouichi.hoshikouichi.hoshi

seed dataの投入
https://www.prisma.io/docs/guides/nextjs#25-seed-the-database

データベースにサンプルデータを投入する。

//prisma/seed.ts

import { PrismaClient, Prisma } from "../app/generated/prisma/client";

const prisma = new PrismaClient();

const userData: Prisma.UserCreateInput[] = [
  {
    name: "Alice",
    email: "alice@prisma.io",
    posts: {
      create: [
        {
          title: "Join the Prisma Discord",
          content: "https://pris.ly/discord",
          published: true,
        },
        {
          title: "Prisma on YouTube",
          content: "https://pris.ly/youtube",
        },
      ],
    },
  },
  {
    name: "Bob",
    email: "bob@prisma.io",
    posts: {
      create: [
        {
          title: "Follow Prisma on Twitter",
          content: "https://www.twitter.com/prisma",
          published: true,
        },
      ],
    },
  },
];

export async function main() {
  for (const u of userData) {
    await prisma.user.create({ data: u });
  }
}

main();

userData: Prisma.UserCreateInput[]
これはuserDataに対する型を定義している。
この型定義に基づいて、AliceやBobなどのユーザーを定義し、

posts: {
  create: [
    {
      title: "Follow Prisma on Twitter",
      content: "https://www.twitter.com/prisma",
      published: true,
    },
  ],
},

とすることでユーザーに対するpostsデータを定義する。

export async function main() {
  for (const u of userData) {
    await prisma.user.create({ data: u });
  }
}

この処理が実行されれば、データベースにユーザーデータとポストデータが登録される。
実行方法については後述。

ユーザーIDが自動的に発行されるらしく、Prismaが裏側でよしなにリレーショナルな処理を実行してくれるっぽい(この辺はまだ理解してない)。

kouichi.hoshikouichi.hoshi

seed.tsのサンプルデータをデータベースに登録する。

prisma.config.tsに以下を追記

export default defineConfig({
  migrations: {
    seed: `tsx prisma/seed.ts`,
  }
});

npx prisma db seedを実行。登録される。
npx prisma studio UI画面がhttp://localhost:5555/で起動するので登録内容を確認する。

kouichi.hoshikouichi.hoshi

seed.tsを使ってデータを登録するとき、ユーザーとそれにひもつくポストがどんな仕組みで登録されるか気になった。

schema.prismaのモデル定義には以下のように書いてある。

model User {
  id    Int     @id @default(autoincrement())
  email String  @unique
  name  String?
  posts Post[]
}

model Post {
  id        Int     @id @default(autoincrement())
  title     String
  content   String?
  published Boolean @default(false)
  authorId  Int
  author    User    @relation(fields: [authorId], references: [id])
}

そして、seed.tsは以下。

  {
    name: "Alice",
    email: "alice@prisma.io",
    posts: {
      create: [
        {
          title: "Join the Prisma Discord",
          content: "https://pris.ly/discord",
          published: true,
        },
        {
          title: "Prisma on YouTube",
          content: "https://pris.ly/youtube",
        },
      ],
    },
  },

Postのモデル定義にauthor User @relation(fields: [authorId], references: [id])と書いてある。postsの配下のcreateにより、authorに対して親のUserのidを参照し、authorIdに挿入せよ、という意味になるのだろう。