Prisma 7とSQLiteでリレーションを操作する - User・Post・Migration

中級 | 25分 で読める | 2025.12.02

公式ドキュメント

今回やること

Prisma 7とSQLiteで、ユーザーと投稿のリレーションを操作します。

  1. UserPostモデルを定義する
  2. migrationでSQLiteにテーブルを作る
  3. ユーザーと投稿を同時に作る
  4. findManyincludeで関連する投稿まで読む

SQLiteは1つのファイルで動くデータベースです。この記事では、外部のDBサーバーを用意せずPrismaの基本の流れに集中します。

前提条件

  • Node.js 20.19以上(または22.12以上、24以上)
  • npm
  • TypeScriptのオブジェクトとasync / awaitが読める
node --version
npm --version

プロジェクトを準備する

mkdir prisma-sqlite-practice
cd prisma-sqlite-practice
npm init -y
npm install --save-dev prisma@7 typescript tsx @types/node @types/better-sqlite3
npm install @prisma/client@7 @prisma/adapter-better-sqlite3@7 dotenv
npx tsc --init
npx prisma init --datasource-provider sqlite --output ../generated/prisma

Prisma 7では、Prisma Clientへデータベースに合うdriver adapterを渡します。今回はSQLite用の@prisma/adapter-better-sqlite3を使います。

ESMを設定する

package.jsontypeとスクリプトを追加します。依存関係の項目はnpm installが作ったまま残してください。

{
  "type": "module",
  "scripts": {
    "db:migrate": "prisma migrate dev",
    "db:generate": "prisma generate",
    "start": "tsx script.ts"
  }
}

tsconfig.jsonは次の内容に置き換えます。

{
  "compilerOptions": {
    "module": "ESNext",
    "moduleResolution": "bundler",
    "target": "ES2023",
    "strict": true,
    "esModuleInterop": true
  }
}

接続先を確認する

初期化によって、ルートの.envに次の設定が作られます。

DATABASE_URL="file:./dev.db"

prisma.config.tsは接続URLとmigrationの保存先を管理します。

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

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

Prisma 7では接続URLをschema.prismaへ書かず、prisma.config.tsで設定します。

UserとPostを定義する

prisma/schema.prismaを次の内容にします。

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

datasource db {
  provider = "sqlite"
}

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

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

User.postsPost.authorが1対多のリレーションです。実際の外部キーはPost.authorIdへ保存されます。

MigrationとClient生成を実行する

npm run db:migrate -- --name init
npm run db:generate

この操作によってmigrationファイル、SQLiteファイル、generated/prismaの型付きClientが作られます。

Prisma Clientを接続する

lib/prisma.tsを作ります。

import "dotenv/config";
import { PrismaBetterSqlite3 } from "@prisma/adapter-better-sqlite3";
import { PrismaClient } from "../generated/prisma/client";

const connectionString = process.env.DATABASE_URL;
if (!connectionString) {
  throw new Error("DATABASE_URL is not set");
}

const adapter = new PrismaBetterSqlite3({ url: connectionString });

export const prisma = new PrismaClient({ adapter });

生成先がgenerated/prismaなので、lib/prisma.tsからのimportは../generated/prisma/clientです。別の出力先へ変えた場合は、このimportも一緒に直します。

作成して取得する

ルートにscript.tsを作ります。

import { prisma } from "./lib/prisma";

async function main() {
  // このチュートリアルが作るUserだけを再実行前に消す
  await prisma.user.deleteMany({
    where: {
      email: "ada@example.com",
    },
  });

  await prisma.user.create({
    data: {
      name: "Ada",
      email: "ada@example.com",
      posts: {
        create: [
          { title: "最初の投稿", published: true },
          { title: "下書き", published: false },
        ],
      },
    },
  });

  const users = await prisma.user.findMany({
    include: {
      posts: true,
    },
  });

  console.log(JSON.stringify(users, null, 2));
}

main()
  .then(async () => {
    await prisma.$disconnect();
  })
  .catch(async (error: unknown) => {
    console.error(error);
    await prisma.$disconnect();
    process.exit(1);
  });

posts.createはユーザーと関連する投稿をまとめて作ります。include: { posts: true }は、ユーザーだけでなく関連する投稿も結果へ含めます。

再実行時は全データを消さず、チュートリアル用のada@example.comだけを削除します。Post.authoronDelete: Cascadeにより、そのユーザーに属する投稿も一緒に削除されます。この固定メールを使う削除処理はローカル練習専用です。本番データへ流用しないでください。

成功を確認する

npm start

次のように、1人のユーザーの中に2件の投稿が表示されれば成功です。IDの数値は環境によって異なります。

[
  {
    "id": 1,
    "email": "ada@example.com",
    "name": "Ada",
    "posts": [
      {
        "id": 1,
        "title": "最初の投稿",
        "published": true,
        "authorId": 1
      },
      {
        "id": 2,
        "title": "下書き",
        "published": false,
        "authorId": 1
      }
    ]
  }
]

よくあるエラー

Cannot find module '../generated/prisma/client'

Clientが未生成か、出力先とimportが一致していません。

npm run db:generate

schema.prismaoutput = "../generated/prisma"も確認します。

Environment variable not found: DATABASE_URL

ルートの.envDATABASE_URL="file:./dev.db"があるか確認します。prisma.config.tslib/prisma.tsの両方がdotenv/configを読み込む構成です。

schemaを変えてもDBへ反映されない

schemaの変更後は、新しい名前でmigrationを実行します。

npm run db:migrate -- --name add_field

練習

Postcontent String?を追加し、migrationを作成してください。その後、1件目の投稿へ本文を設定し、実行結果にcontentが含まれることを確認します。

次のステップ

基本のCRUDをSQLで確かめたい場合は、SQL SELECT文の練習へ進んでください。別のTypeScript ORMと比較したい場合は、Drizzle ORMガイドを参照してください。

参考リソース

(最終確認: 2026年7月25日)

← 一覧に戻る
PR
PR
PR
PR