Next.jsとPrismaで作る型安全なデータベース設計入門
〜App Router時代のスキーマ駆動開発〜
はじめに
Next.jsでデータベースを扱う場合、
SQLを直接書くよりもORM(Object Relational Mapper)を使うのが一般的です。
中でも Prisma は、型安全・補完・マイグレーション管理のすべてを高次元で両立したモダンORMです。
この記事では、Next.js × Prismaを使った型安全なデータベース設計と開発フローを紹介します。
環境構成
src/
app/
users/
page.tsx
lib/
prisma.ts
prisma/
schema.prisma
Prisma CLIを使って初期化します。
pnpm add prisma @prisma/client
npx prisma init
これにより prisma/schema.prisma が作成され、DB接続設定も自動生成されます。
Prismaのスキーマ定義
Prismaでは、RDB(MySQL / PostgreSQL / SQLiteなど)のスキーマを
宣言的に定義し、prisma migrate コマンドでDBに反映します。
// prisma/schema.prisma
generator client {
provider = "prisma-client-js"
}
datasource db {
provider = "postgresql"
url = env("DATABASE_URL")
}
model User {
id String @id @default(uuid())
name String
email String @unique
createdAt DateTime @default(now())
}
これで npx prisma migrate dev --name init を実行すると、
自動でテーブルが作成されます。
Prisma Clientの初期化
Next.jsでは、App Routerでサーバー側コードが頻繁に実行されるため、
Prisma Clientを使い回す設計が重要です。
// src/lib/prisma.ts
import { PrismaClient } from "@prisma/client";
const globalForPrisma = globalThis as unknown as { prisma: PrismaClient };
export const prisma =
globalForPrisma.prisma ||
new PrismaClient({
log: ["query", "error", "warn"],
});
if (process.env.NODE_ENV !== "production") globalForPrisma.prisma = prisma;
これにより、ホットリロード時の再生成によるエラーを防げます。
データの取得・登録を実装する
Next.js App Routerでは、Server Components内で直接DBアクセス可能です。
// src/app/users/page.tsx
import { prisma } from "@/lib/prisma";
export default async function UsersPage() {
const users = await prisma.user.findMany({
orderBy: { createdAt: "desc" },
});
return (
<div className="p-6">
<h1 className="text-2xl font-bold mb-4">ユーザー一覧</h1>
<ul>
{users.map((u) => (
<li key={u.id}>
{u.name}({u.email})
</li>
))}
</ul>
</div>
);
}
これだけで、DBからデータを型安全に取得できます。users は自動的に User[] 型として補完されます。
データ作成用のServer Action
App Routerでは、Server Actionを使ってDB操作を行うのが自然です。
// src/server/actions/createUser.ts
"use server";
import { prisma } from "@/lib/prisma";
import { revalidatePath } from "next/cache";
export async function createUser(formData: FormData) {
const name = formData.get("name")?.toString();
const email = formData.get("email")?.toString();
if (!name || !email) return { success: false };
await prisma.user.create({
data: { name, email },
});
revalidatePath("/users");
return { success: true };
}
フォームからServer Actionを呼び出す
// src/app/users/new/page.tsx
import { createUser } from "@/server/actions/createUser";
export default function NewUserPage() {
return (
<form action={createUser} className="max-w-md mx-auto flex flex-col gap-4 mt-8">
<label>
名前
<input name="name" className="border p-2 w-full" />
</label>
<label>
メール
<input name="email" className="border p-2 w-full" />
</label>
<button type="submit" className="bg-blue-600 text-white py-2 rounded">
追加
</button>
</form>
);
}
このフォームを送信すると、自動でcreateUser()がサーバーで実行され、
新しいユーザーがDBに登録されます。
Prismaの型安全性を活かす
Prismaは、スキーマから型定義を自動生成します。
つまり、DBの変更が即座にTypeScriptの型に反映されるため、
リファクタリング時の事故を防げます。
const users = await prisma.user.findMany();
// → users: User[]
また、select や include の型補完もサポートされています。
Prisma Studioでデータ確認
開発中は、Prisma公式のGUIツール「Prisma Studio」を使うと便利です。
npx prisma studio
ブラウザ上でDBを操作でき、開発中のデータ確認や手動編集が簡単になります。
まとめ
Next.js × Prismaを組み合わせることで、次のようなメリットがあります。
- スキーマ駆動:モデル定義がDBと型に自動反映
- 型安全:全てのクエリがTypeScriptで補完・検証される
- 簡潔なデータアクセス:Server Componentsで直接利用可能
App Router時代のNext.jsでは、Prismaはもはや必須レベルのORMです。
堅牢かつ安全なDBアクセスを、型の力で支える設計をぜひ導入してみてください。