api-database-drizzle について
Drizzle ORMとNeonでPostgreSQLアプリを構築するTypeScript開発者向けの実践ガイドです。型安全なスキーマとクエリ、効率的な関連データ取得、トランザクションやマイグレーションの扱いを説明します。
主な機能
- 01スキーマ先行の型安全な設計
- 02Neon HTTP・WebSocket接続の指針
- 03`.with()`によるリレーショナルクエリ
- 04トランザクションとマイグレーション
- 05Drizzle Kitによるシードの指針
活用シーン
- 01サーバーレスアプリの型安全なクエリ作成
- 02N+1を避けた関連レコードの取得
- 03アトミックなデータ更新
- 04スキーマ移行と開発データの管理
インストール
コマンドをコピーしてターミナルで実行します。
npx skills add https:/ / github.com/ agents-inc/ skills --skill api-database-drizzleインストール後、Claude Code で/api-database-drizzleを入力して読み込まれたか確認します。
同梱ファイル
SKILL.md と一緒にインストールされる参考資料やスクリプト。
metadata.yamlreference.mdexamples/core.mdmigrations.mdqueries.mdrelations-v2.mdseeding.mdtransactions.md詳細
Drizzle ORM + Neon によるデータベース
クイックガイド: 型安全なクエリには Drizzle ORM を、エッジ対応の接続には Neon のサーバーレス Postgres を使用します。スキーマファーストの設計により、TypeScript の型が自動生成されます。
defineRelations()とオブジェクトベースのwhere構文を使用する RQB v2 を使います。.with()を使ったリレーショナルクエリにより、N+1 問題を回避できます。アトミックな操作にはトランザクションを使用します。
<critical_requirements>
重要:このスキルを使用する前に
(camelCase の JS を snake_case の SQL にマッピングするため、Drizzle の設定で必ず casing: 'snake_case' を設定してください)
(アトミック性を確保するため、トランザクションのコールバック内では必ず tx パラメーターを使用してください(db は使用しないでください))
(N+1 問題を回避するため、リレーショナルクエリでは必ず .with() を使用してください。すべてのデータを単一の SQL クエリで取得できます)
(RQB v2 では必ず defineRelations() を使用してください。テーブルごとに relations() を定義する従来の構文は非推奨です)
</critical_requirements>
詳細なリソース:
- コード例は examples/ フォルダーを参照してください:
- core.md - 接続の設定とスキーマの定義(常に読み込まれます)
- queries.md - リレーショナルクエリとクエリビルダー
- relations-v2.md -
defineRelations()を使った RQB v2(NEW) - transactions.md - アトミックな操作
- migrations.md - Drizzle Kit のワークフロー
- seeding.md - 開発用データの投入(drizzle-seed を含む)
- 判断フレームワークとアンチパターンについては、reference.md を参照してください
自動検出: drizzle-orm、@neondatabase/serverless、neon-http、db.query、db.transaction、drizzle-kit、pgTable、defineRelations、drizzle-seed
使用する場合:
- 型安全なデータベースクエリが必要なサーバーレス関数
- マイグレーションを伴うスキーマファースト開発
- API ルートを備えたサーバーレンダリングアプリの構築
使用しない場合:
- フレームワークのサーバーアクションを直接使うシンプルなアプリ(オーバーヘッドに見合う利点がありません)
- 従来の TCP 接続プーリングのみが必要なアプリ(標準の Postgres クライアントを使用してください)
- TypeScript 以外のプロジェクト(型安全性という主な利点が失われます)
- WebSocket 接続を必要とするエッジ関数(エッジランタイムではサポートされていません)
主なパターン
パターン 1:データベース接続(Neon HTTP)
サーバーレス/エッジとの互換性を確保するため、Drizzle を Neon 用に設定します。主な設定要件:
export const db = drizzle(sql, {
schema,
casing: "snake_case", // Maps camelCase JS to snake_case SQL
});
- 使用前に
DATABASE_URLを検証します(未設定の場合は例外をスローします) - フィールド名の不一致を防ぐため、常に
casing: "snake_case"を設定します - HTTP(エッジ対応)には
neon()を、WebSocket(長時間のクエリ)にはPoolを使用します
接続の完全な設定、WebSocket の設定、Drizzle Kit の設定については、examples/core.md を参照してください。
パターン 2:スキーマ定義
Drizzle のスキーマビルダーを使って、TypeScript の型とともにテーブルを定義します:
export const companies = pgTable("companies", {
id: uuid("id").primaryKey().defaultRandom(),
name: varchar("name", { length: 255 }).notNull(),
slug: varchar("slug", { length: 255 }).unique(),
deletedAt: timestamp("deleted_at"), // Soft delete
createdAt: timestamp("created_at").defaultNow(),
});
- varchar の代わりに、値を制限する
pgEnum()を使用します createdAt/updatedAtのタイムスタンプを必ず含めます- 論理削除には
deletedAtを追加します - 孤立したレコードを防ぐため、外部キーに
onDelete: "cascade"を設定します - 主キーには
uuid().defaultRandom()またはinteger().generatedAlwaysAsIdentity()を使用します
スキーマの完全な例(enum、リレーション、ジャンクションテーブル、identity 列)については、examples/core.md を参照してください。
パターン 3:.with() を使ったリレーショナルクエリ
.with() を使用して、関連データを単一の SQL クエリで効率的に取得します:
const job = await db.query.jobs.findFirst({
where: and(eq(jobs.id, jobId), isNull(jobs.deletedAt)),
with: {
company: { with: { locations: true } },
jobSkills: { with: { skill: true } },
},
});
// Result is fully typed: job.company.name, job.jobSkills[0].skill.name
- 関連データの取得には、
.with()を使ったdb.queryを使用します -- 単一の SQL クエリで取得でき、N+1 問題も発生しません - カスタムの列選択、複雑な JOIN、集計には、クエリビルダー(
db.select())を使用します - 論理削除されたテーブルでは、WHERE 条件に必ず
isNull(deletedAt)を含めます
リレーショナルクエリの完全な例、N+1 アンチパターン、動的フィルタリングについては、examples/queries.md を参照してください。
追加のパターン
以下のパターンは、examples/ に完全な例とともに記載されています。
- クエリビルダー - 複雑なフィルター、動的な条件、カスタム JOIN - queries.md を参照してください
- トランザクション - アトミックな操作、エラー処理、ロールバック - transactions.md を参照してください
- データベースのマイグレーション - Drizzle Kit のワークフロー、
generateとpushの違い - migrations.md を参照してください - データベースのシーディング - 開発用データ、安全なクリーンアップ - seeding.md を参照してください
パフォーマンスの最適化(インデックス、プリペアドステートメント、ページネーション)については、reference.md に記載されています。
<red_flags>
重大な注意事項
- ❌ トランザクション内で
txではなくdbを使用する - トランザクションのコンテキストを迂回し、アトミック性が損なわれます - ❌ リレーションで N+1 クエリを発生させる - 1 回のクエリで取得するには
.with()を使用してください - ❌
casing: 'snake_case'を設定しない - JS と SQL の間でフィールド名が一致しなくなります - ❌ v1 のテーブルごとの
relations()構文を使用する - 非推奨です。defineRelations()を使用してください - ❌ コールバックベースの
where/orderByを使用する - v1 の構文は非推奨です。オブジェクトベースの構文を使用してください - ⚠️ ソフトデリートのチェック(
isNull(deletedAt))を含まないクエリ - ⚠️ リストクエリにページネーションの上限がない
注意点とエッジケース:
- Neon HTTP のクエリタイムアウトは 30 秒です。長時間かかるクエリには WebSocket が必要です
- トランザクションの外部で作成されたプリペアドステートメントは、トランザクション内では使用できません
enableRLS()は v1.0.0-beta.1 で非推奨になりました。代わりにpgTable.withRLS()を使用してください- バリデーターパッケージは統合されました。
drizzle-zodはdrizzle-orm/zodになりました(v1 beta 以降)
アンチパターンと注意点の完全な一覧については、reference.md を参照してください。
</red_flags>
<critical_reminders>
重要な注意事項
(camelCase の JS を snake_case の SQL にマッピングするには、Drizzle の設定で casing: 'snake_case' を必ず設定してください)
(アトミック性を確保するには、トランザクションのコールバック内で db ではなく tx パラメーターを必ず使用してください)
(N+1 の問題を避けるには、リレーショナルクエリで .with() を必ず使用してください。すべてのデータを単一の SQL クエリで取得できます)
(RQB v2 では defineRelations() を必ず使用してください。テーブルごとの従来の relations() 構文は非推奨です)
これらのルールに従わないと、フィールド名の不一致、トランザクションのアトミック性の破綻、N+1 によるパフォーマンスの問題が発生し、非推奨の API を使用することになります。
</critical_reminders>