Claude Codeは「コードを書く」ツールとして知られていますが、データベース開発との相性が特に良いです。スキーマ設計の議論、マイグレーションの安全性チェック、N+1問題の自動検出、ClaudeのMCPサーバー経由での直接DB操作まで、DB開発のすべてのフェーズでAIに任せられる作業が増えています。
この記事では、TypeScriptプロジェクトで最もよく使われる2つのORM——PrismaとDrizzle——を対象に、Claude Codeを使ったデータベース開発の実践的なワークフローを解説します。CLAUDE.mdへのコピペ即使えるDB設定テンプレートも用意しました。
Claude CodeとDB開発の相性が良い理由
テキストエディタで書くコードと違い、DBスキーマには「全体像」が常に必要です。新しいテーブルを追加するとき、既存のリレーションへの影響、インデックスの是非、マイグレーションの安全性を一気に考えなければなりません。これはまさにClaude Codeが得意とする「文脈を踏まえた総合的な判断」が求められる領域です。
- スキーマ全体を一度に把握できる: .prismaファイルやdrizzle/schema.tsをコンテキストに読み込めば、全テーブルの構造を理解した上で提案できる
- 破壊的変更を自動検出できる: マイグレーションSQLを読んでテーブルロックやデータ損失リスクを事前に警告できる
- N+1問題をコードレビューで自動検出できる: ループ内のDB呼び出しパターンを発見し、最適化済みのコードを提案できる
- MCPサーバー経由でDBを直接操作できる(Prismaのみ): Claude Codeからマイグレーション実行やデータ確認が可能
Prisma vs Drizzle ── Claude Codeと使う場合の選択基準
どちらのORMもClaude Codeと組み合わせて使えますが、特性が異なります。
| 観点 | Prisma v7 | Drizzle v1 |
|---|---|---|
| バンドルサイズ | 1.6MB(v7でRust→TS再実装により90%削減) | 7.4KB(約228分の1) |
| 型チェック速度 | 高速(型インスタンス数: 約427) | やや遅い(型インスタンス数: 約59,000) |
| スキーマ記述 | 専用言語PSL(.prismaファイル) | TypeScript |
| SQLとの近さ | 高抽象(contains/startsWith) |
SQL直結(like/ilike) |
| MCPサーバー | 公式MCPあり(v6.6.0〜) | なし |
| Claude Codeとの相性 | 自然言語での設計指示がしやすい、MCP経由で直接操作可能 | SQLに近い記法で生成コードが予測しやすい |
| 推奨シーン | チーム開発・複雑なリレーション・管理ツール重視 | エッジ/サーバーレス・パフォーマンス重視・SQL習熟者 |
CLAUDE.mdへのDB設定テンプレート
Claude CodeにDB開発を依頼する前に、プロジェクトのCLAUDE.mdへDB設計の規約を記述しておくことが重要です。一度設定すれば、Claude Codeが毎回同じ規約に従ったコードを生成するようになります。
Prisma版 CLAUDE.mdテンプレート
## Database(Prisma + PostgreSQL) ### 命名規則 - テーブル名: PascalCase(Prismaの規約) → DBはsnake_caseに自動マッピング - カラム名: camelCase(Prisma)→ DBはsnake_case(@@map/@mapで明示) - モデル名をそのままAPIレスポンスに使わない(DTOを経由する) ### クエリルール - ループ内のDB呼び出し禁止(N+1問題) - 関連データはinclude/selectで一括取得する - リスト取得は必ずtake(上限)を設定する - SELECT *相当(全フィールド取得)は禁止。必要フィールドをselectで指定 - _countで集計はDBレイヤーで行う ### マイグレーションルール - 開発環境: npx prisma migrate dev --name <説明的な名前> - 本番環境: npx prisma migrate deploy(CI/CDから実行) - マイグレーションファイルは手動編集禁止 - スキーマ変更後は必ず npx prisma generate を実行 - prisma migrate reset は本番DB絶対禁止 - カラム削除は2デプロイで段階的実施: 1. コードから参照削除 → デプロイ 2. 次スプリントでカラム削除マイグレーション作成 ### 安全ルール - 大テーブルへのインデックス追加はCONCURRENTLYオプション検討 - NOT NULL追加はDEFAULT値を同時設定するか段階的実施 - prisma db pushは本番環境で使用しない
Drizzle版 CLAUDE.mdテンプレート
## Database(Drizzle ORM + PostgreSQL)
### 命名規則
- テーブル名: 複数形・snake_case(users, blog_posts, post_tags)
- カラム名: DBはsnake_case、TypeScriptはcamelCase(自動マッピング)
- インデックス名: table_column_idx形式(例: users_email_idx)
### スキーマ規約
- IDカラム: serialではなくgeneratedAlwaysAsIdentity()を使う(2025年標準)
- タイムスタンプ: timestamptz({ precision: 3 }) で統一
- 外部キーには必ずインデックスを張る
- JSONカラム: jsonb().$type<型名>() で型付きにする
### クエリルール
- ループ内のDB呼び出し禁止(N+1問題)
- 関連データはdb.query.xxx.findMany()の関連クエリAPIを使う
- 必要カラムのみcolumnsプロパティで指定する
- 頻繁なクエリはprepare()でPrepared Statementにする
### マイグレーションルール
- 開発: npx drizzle-kit push(スキーマを直接プッシュ)
- 本番: npx drizzle-kit generate → npx drizzle-kit migrate
- 生成されたSQLは適用前に必ず確認する
PrismaでのClaude Code実践ワークフロー
スキーマ設計をClaude Codeに依頼する
スキーマ設計の依頼では、システム概要・エンティティ・非機能要件を一度に伝えるのがポイントです。
schema.prismaを新規作成してください。 ## システム概要 ブログサービス(月間10万PV、検索クエリが多い) ## エンティティ - User: メール認証、ソフトデリート必要 - Post: 下書き/公開/アーカイブのステータス管理 - Tag: 多対多リレーション ## 設計基準 1. 全外部キーにインデックスを張る 2. よく使うフィルタ(status, publishedAt)に複合インデックスを追加 3. N+1が起きないリレーション設計 4. 設計理由をコメントで記載する 作成後にN+1リスクがある箇所を自己レビューしてください。
// User: ソフトデリート対応(deletedAtがある場合は削除済み)
model User {
id String @id @default(uuid())
email String @unique
name String
posts Post[]
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
deletedAt DateTime? // ソフトデリート
@@map("users")
}
model Post {
id String @id @default(uuid())
title String
content String @db.Text
status PostStatus @default(DRAFT)
authorId String
author User @relation(fields: [authorId], references: [id])
tags PostTag[]
publishedAt DateTime?
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
// 複合インデックス: statusとpublishedAtでよく絞り込むため
@@index([status, publishedAt])
// N+1対策: authorIdへのインデックス(外部キー必須)
@@index([authorId])
@@map("posts")
}
enum PostStatus {
DRAFT
PUBLISHED
ARCHIVED
}
マイグレーションの安全性チェックを依頼する
マイグレーションを実行する前に、Claude Codeに生成されたSQLを確認させる習慣をつけましょう。
npx prisma migrate devを実行し、生成されたSQLファイルを読んで 以下を確認してください: 1. テーブルロックが発生する操作がないか 2. データ損失のリスクがある変更がないか(DROP COLUMN等) 3. 大テーブルへのインデックス追加がないか(CONCURRENTLYが必要) 4. 問題があれば migrate dev を中止し、代替手順を提案してください
postsテーブルのbodyカラムをcontentにリネームし、 NOT NULL制約を追加したい(現在データ5万件)。 ダウンタイムなしで安全に移行する手順を提案してください: - 段階的な移行ステップ - 各ステップで実行するPrismaコマンド - ロールバック手順 - リスク説明
CRUD自動生成を依頼する
schema.prismaのPostモデルに対して以下を生成してください: 1. src/repositories/post.repository.ts - findById(authorとタグを含む) - findMany(ページング付き、ステータスフィルター) - create(ドラフト作成) - publish(公開 + publishedAt設定) - softDelete(deletedAtを設定) 実装規約: - 全フィールド取得しない(必要なselectを明示) - 型はPrismaが生成した型を使う(独自型は作らない) - エラーは上位層に投げる(try-catchしない)
import { prisma } from "@/lib/prisma";
import { Prisma } from "@prisma/client";
// 取得カラムを明示(SELECT *は使わない)
const postSelectFields = {
id: true,
title: true,
content: true,
status: true,
publishedAt: true,
createdAt: true,
author: { select: { id: true, name: true } },
tags: { select: { tag: { select: { id: true, name: true } } } },
} satisfies Prisma.PostSelect;
export const postRepository = {
async findById(id: string) {
return prisma.post.findUnique({
where: { id, deletedAt: null },
select: postSelectFields,
});
},
async findMany(params: { page: number; limit: number; status?: string }) {
const { page, limit, status } = params;
const [posts, total] = await prisma.$transaction([
prisma.post.findMany({
where: { deletedAt: null, ...(status ? { status } : {}) },
select: postSelectFields,
take: limit,
skip: (page - 1) * limit,
orderBy: { createdAt: "desc" },
}),
prisma.post.count({ where: { deletedAt: null } }),
]);
return { posts, total };
},
};
Prisma MCPサーバー ── Claude Codeからデータベースを直接操作する
Prisma v6.6.0(2025年)から提供されているMCPサーバーを使うと、Claude CodeからDBの状態確認やマイグレーション操作が直接できるようになります。
# ローカル開発用(prisma migrate devなどを操作) claude mcp add prisma-local -- npx -y prisma mcp # Prisma Postgresクラウド用(リモート管理) claude mcp add --transport http prisma-remote https://mcp.prisma.io/mcp
MCPサーバーを追加すると、Claude Codeが以下の操作を直接実行できるようになります:
- マイグレーション状態の確認(
migrate-status) - 開発用マイグレーションの実行(
migrate-dev) - Prisma Studioの起動
- スキーマのイントロスペクション(既存DBからスキーマ生成)
- クラウド版ではSQL実行・バックアップ作成/復元も可能
# Claude Codeのターミナルで > マイグレーションの適用状況を確認して > 現在の全テーブルのレコード数を確認して > postsテーブルに先週追加されたレコードを10件見せて # Claude Codeが prisma mcp のツールを呼び出して直接DBを確認する
migrate resetなどの破壊的操作はAIエージェントから自動実行できません。ユーザーの明示的な承認が必要な設計になっています。MCPサーバーの詳しい設定方法はClaude Code MCP完全ガイドで解説しています。
DrizzleでのClaude Code実践ワークフロー
スキーマ定義を依頼する
DrizzleはTypeScriptでスキーマを書くため、Claude Codeへの指示もTypeScriptの文脈で自然に伝えられます。
以下の仕様でDrizzle ORMのスキーマを定義してください。
## 使用DB: PostgreSQL
## テーブル: users, posts, tags(多対多)
## 必ず従う規約
- テーブル名: 複数形・snake_case(users, blog_posts)
- IDカラム: generatedAlwaysAsIdentity()を使う(serialは使わない)
- タイムスタンプ: timestamptz({ precision: 3 }) で統一
- 外部キーには必ずインデックスを張る
- JSONカラム: jsonb().$type<型名>() で型付きにする
- drizzle-zodでcreateInsertSchema/createSelectSchemaも生成する
import { pgTable, text, varchar, timestamp, integer, pgEnum, index, uniqueIndex } from "drizzle-orm/pg-core";
import { createInsertSchema, createSelectSchema } from "drizzle-zod";
import { relations } from "drizzle-orm";
export const postStatusEnum = pgEnum("post_status", ["draft", "published", "archived"]);
export const users = pgTable("users", {
id: integer("id").generatedAlwaysAsIdentity().primaryKey(),
email: varchar("email", { length: 255 }).notNull().unique(),
name: varchar("name", { length: 100 }).notNull(),
createdAt: timestamp("created_at", { precision: 3, withTimezone: true }).defaultNow().notNull(),
deletedAt: timestamp("deleted_at", { precision: 3, withTimezone: true }),
}, (t) => [
uniqueIndex("users_email_idx").on(t.email),
]);
export const posts = pgTable("posts", {
id: integer("id").generatedAlwaysAsIdentity().primaryKey(),
title: varchar("title", { length: 500 }).notNull(),
content: text("content").notNull(),
status: postStatusEnum("status").default("draft").notNull(),
authorId: integer("author_id").notNull().references(() => users.id),
publishedAt: timestamp("published_at", { precision: 3, withTimezone: true }),
createdAt: timestamp("created_at", { precision: 3, withTimezone: true }).defaultNow().notNull(),
}, (t) => [
index("posts_author_id_idx").on(t.authorId), // 外部キーのインデックス
index("posts_status_published_at_idx").on(t.status, t.publishedAt), // 複合インデックス
]);
// Drizzle-Zod統合
export const insertUserSchema = createInsertSchema(users);
export const selectUserSchema = createSelectSchema(users);
Relational APIで関連データを取得する(Drizzle v1)
Drizzle v1のRelational API v2では、PrismaのincludeライクなAPIでN+1なしに関連データを取得できます。
// drizzle/relations.ts
import { relations } from "drizzle-orm";
import { posts, users, tags, postTags } from "./schema";
export const usersRelations = relations(users, ({ many }) => ({
posts: many(posts),
}));
export const postsRelations = relations(posts, ({ one, many }) => ({
author: one(users, { fields: [posts.authorId], references: [users.id] }),
postTags: many(postTags),
}));
// 使用時: authorとtagsを1クエリで取得(N+1なし)
const result = await db.query.posts.findMany({
where: (posts, { eq }) => eq(posts.status, "published"),
with: {
author: { columns: { id: true, name: true } },
postTags: { with: { tag: true } },
},
limit: 20,
orderBy: (posts, { desc }) => [desc(posts.publishedAt)],
});
drizzle-kitのコマンド一覧
| コマンド | 用途 | 環境 |
|---|---|---|
drizzle-kit push |
スキーマを直接DBにプッシュ(マイグレーションファイルなし) | 開発 |
drizzle-kit generate |
スキーマ変更からSQLマイグレーションファイルを生成 | 両方 |
drizzle-kit migrate |
生成済みマイグレーションをDBに適用 | 本番 |
drizzle-kit pull |
既存DBをイントロスペクトしてスキーマ生成 | 開発 |
drizzle-kit studio |
GUIでDB閲覧・操作(Drizzle Studio) | 開発 |
drizzle-kit check |
マイグレーション競合チェック | CI/CD |
Claude CodeにN+1問題を自動検出させる
N+1問題はPRレビューで見落とされやすい問題です。Claude Codeに定期的にコードレビューを依頼するワークフローを作りましょう。
以下のコードをレビューしてN+1問題とスロークエリを検出してください: [コードをペースト、または /add でファイルを追加] 改善点ごとに: 1. 問題の説明(どのループで何回クエリが発生するか) 2. 改善後のコード 3. 改善前後の想定クエリ数比較 を示してください。
// NG: ループ内でDB呼び出し(投稿数が100件なら101クエリ発生)
const posts = await prisma.post.findMany({ take: 100 });
for (const post of posts) {
const author = await prisma.user.findUnique({ // ← ここでN+1!
where: { id: post.authorId }
});
console.log(post.title, author?.name);
}
// OK: includeで1クエリに(Claude Codeが提案する修正)
const posts = await prisma.post.findMany({
take: 100,
include: {
author: { select: { id: true, name: true } }
}
});
for (const post of posts) {
console.log(post.title, post.author.name); // ← クエリ発生なし
}
CI/CDへのマイグレーションチェック組み込み
PRのたびにClaude Codeがマイグレーションを自動チェックするワークフローを構築できます。
name: DB Migration Check
on:
pull_request:
paths:
- "prisma/migrations/**"
- "drizzle/**"
- "schema.prisma"
jobs:
migration-review:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Check changed migration files
run: |
CHANGED=$(git diff --name-only origin/main...HEAD -- prisma/migrations/ drizzle/)
if [ -z "$CHANGED" ]; then
echo "No migration changes detected"
exit 0
fi
echo "Changed migration files:"
echo "$CHANGED"
# Claudeにマイグレーションの安全性をチェックさせる
for f in $CHANGED; do
if [[ "$f" == *.sql ]]; then
cat "$f" | claude --bare -p \
"このマイグレーションSQLに破壊的変更(DROP/TRUNCATE/テーブルロック)があれば警告して" \
--output-format stream-json | jq -r ".text"
fi
done
env:
ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }}
DISABLE_AUTOUPDATER: 1
Claude Codeへのよくある指示テンプレート集
DB開発でよく使う指示のテンプレートをまとめました。コピペして使ってください。
インデックス最適化の依頼
以下のクエリが遅い(スローログに出ている)。 EXPLAINの出力と現在のスキーマを見て最適なインデックスを提案してください。 ## スローログのクエリ [クエリをペースト] ## EXPLAINの出力 [EXPLAIN ANALYZEの結果をペースト] ## 現在のインデックス [関連テーブルのスキーマをペースト] 追加するインデックス、削除できるインデックス、 複合インデックスへの統合ができる箇所を具体的に提案してください。
既存DBからのスキーマ生成
# Prismaの場合 > npx prisma db pull を実行して既存のDBからスキーマを生成してください。 > 生成後、以下を改善してください: > 1. 適切なリレーション定義を追加 > 2. 欠けているインデックスを追加 > 3. 命名規則に合わないカラムをmapで修正 # Drizzleの場合 > npx drizzle-kit pull を実行して既存DBからスキーマを生成してください。 > 生成後、relationsファイルも作成してください。
テストデータ(シード)の生成
prisma/seed.tsを作成してください。 要件: - User: 5件(管理者1・一般4) - Post: 各ユーザーに3件(PUBLISHED/DRAFT混在) - Tag: 10件、各投稿に2〜4件紐づける - パスワードはbcryptでハッシュ化 - 開発用なのでリセット・再実行可能な設計 実行コマンド: npx ts-node prisma/seed.ts
よくある質問
prisma-client-js→prisma-client)とBytesフィールドの型(Buffer→Uint8Array)です。schema.prismaの1行を変えてコードを検索・置換する程度で、ほとんどのプロジェクトは1〜2時間で移行できます。バンドルサイズが大幅に小さくなるため移行の価値は高いです。migrate resetなどの破壊的操作に対するセーフガードが組み込まれています。ただし、本番DBへの接続は最小権限の原則に従い、SELECT専用のユーザーを使うことを推奨します。/add src/repositories)とスキーマファイル(/add schema.prisma または /add drizzle/schema.ts)をコンテキストに追加してから依頼するのが効果的です。CLAUDE.mdに「N+1禁止」ルールを記述しておくと、コード生成時に自動的に回避されます。/addで既存のRawクエリファイルを追加し、「このRawクエリをPrismaのTyped SQL APIを使って書き直してください」と依頼します。Prismaのv6以降はTyped SQL(型安全なrawクエリ)が使えるため、パフォーマンスが重要な部分はRawクエリを保ちながら型安全性を確保できます。まとめ
Claude Codeとデータベース開発の組み合わせは、特に以下の場面で効果を発揮します。
- スキーマ設計の壁打ち: 要件を伝えれば、インデックス設計・N+1対策・マイグレーション安全性を考慮したスキーマを提案してもらえる
- CLAUDE.mdへのDB規約の一元管理: 命名規則・クエリルール・マイグレーション手順を書いておけば、生成されるコードが常に規約に従う
- マイグレーションの安全性チェック: 本番適用前に生成SQLを読んで破壊的変更を検出できる
- Prisma MCPサーバー連携: Claude Codeからマイグレーション状態の確認・スキーマ生成が直接できる
- N+1問題の自動検出: PRレビュー時にDB呼び出しパターンを解析して最適化を提案できる
DB開発はミスが本番データに直結するため、慎重に進める必要があります。Claude Codeを「第二のレビュアー」として使うことで、変更前に問題を検出できるようになります。
Prismaの基本的な使い方はTypeScript × Prisma完全ガイド、Drizzleの使い方はTypeScript × Drizzle ORM完全ガイドで詳しく解説しています。また、Claude Code全体の使い方はClaude Code入門ガイドをご覧ください。

