drizzle-orm-v0 について
PostgreSQL、MySQL、SQLite で Drizzle ORM v0.45+ を使う TypeScript 開発者向けのスキルです。型付きスキーマやクエリ、Drizzle Kit によるマイグレーション、トランザクションの実践方法を扱います。
主な機能
- 01型付きテーブルと列の定義
- 02型安全なクエリの構築
- 03Drizzle Kit によるマイグレーション管理
- 04スキーマからレコード型を推論
- 05複数処理でのトランザクション利用
活用シーン
- 01PostgreSQL、MySQL、SQLite のスキーマ作成
- 02型安全な CRUD クエリの記述
- 03マイグレーションの生成と適用
- 04複数の DB 処理の原子性確保
インストール
コマンドをコピーしてターミナルで実行します。
$
npx skills add https:/ / github.com/ blockmatic/ basilic-skills --skill drizzle-orm-v0インストール後、Claude Code で/drizzle-orm-v0を入力して読み込まれたか確認します。
同梱ファイル
SKILL.md と一緒にインストールされる参考資料やスクリプト。
references/postgresql-patterns.mdqueries.mdtemplates/db.tsschema.ts詳細
スキル:drizzle-orm
対象範囲
- 対象:PostgreSQL、MySQL、SQLite 向けの Drizzle ORM v0.45 以降。スキーマ定義、型安全なクエリ、マイグレーション
- 対象外:データベースドライバーのセットアップ、コネクションプーリングの設定、その他の ORM
前提条件
- Drizzle ORM v0.45 以降
- マイグレーション用の Drizzle Kit v0.31 以降(開発依存関係)
- PostgreSQL、MySQL、または SQLite データベース
- strict モードを有効にした TypeScript v5 以降
- ESM モジュールシステム
原則
- 型付きカラムを使用し、テーブルビルダー(
pgTable、mysqlTable、sqliteTable)でスキーマを定義します - カラム型はデータベースの制約に合わせます(長さを指定した
varchar、モードを指定したtimestamp) index()ヘルパーを使用し、テーブル定義の第 2 引数でインデックスを定義します- テキストの主キーに外部キー用の
.references()を指定する方法は、有効なデフォルトです - クエリヘルパー(
eq、and、or、like)を使って、型安全に SQL を構築します $inferSelectと$inferInsertによる型推論で、手動の型定義を不要にします- マイグレーションは
drizzle-kit generateで生成します(本番環境ではpushを使いません) - 頻繁に実行するクエリは、プリペアドステートメントで最適化します
- スキーマはドメインごとに整理します(エンティティ/テーブルごとに 1 ファイル)
- トランザクション(
db.transaction)を使い、複数ステップの操作をアトミックに実行します
制約
必須
- マイグレーションには Drizzle Kit を使用します(
drizzle-kit generate、drizzle-kit migrate) - データベースの制約に合ったカラム型を定義します
- 生 SQL ではなく、クエリヘルパーを使用します
推奨
- 複数ステップの操作にはトランザクションを使用します
- 頻繁に実行するクエリにはプリペアドステートメントを使用します
$inferSelectと$inferInsertで型をエクスポートします- 構造化されたエラー処理のために
DrizzleQueryErrorを処理します - スキーマはドメインごとに整理します(エンティティごとに 1 ファイル)
- 必要なフィールドのみを読み込みます(行全体を取得しません)
varcharカラムには長さを指定します- テーブル定義には
index()ヘルパーを使用します - PostgreSQL スキーマのテストには PGLite を使用します
- プロジェクトですでに
relations()を使用している場合は、relations()とリレーショナルクエリビルダーを使用します
任意
- PostgreSQL では、新しいテーブルで
serialの代わりに IDENTITY カラム(generatedAlwaysAsIdentity)を使用します - リレーションが定義されている場合、複雑な結合にはリレーショナルクエリビルダー(
db.query.*)を使用します
避けること
- 必要な場合を除き、生 SQL を使用すること
- 手動で型アサーションを行うこと(推論された型を使用します)
- マイグレーション生成を省略すること
- 新しい PostgreSQL テーブルで
serialを使用すること(IDENTITY カラムを使用します) - インデックスを過剰に作成すること(クエリ上の必要性に応じて作成します)
- 必要なカラムが少数なのに、行全体を取得すること
- 本番環境で
pushを使用すること(generateとmigrateを使用します) - データベースが日付/時刻型をサポートしている場合に、文字列ベースの timestamp モードを使用すること
相互運用性
パターン
スキーマ定義
import { index, pgTable, text, timestamp, varchar } from 'drizzle-orm/pg-core'
export const users = pgTable(
'users',
{
id: text('id').primaryKey(),
email: varchar('email', { length: 255 }).notNull().unique(),
createdAt: timestamp('created_at').defaultNow().notNull(),
updatedAt: timestamp('updated_at').defaultNow().notNull(),
},
table => [index('users_email_idx').on(table.email)],
)
export type User = typeof users.$inferSelect
export type NewUser = typeof users.$inferInsert
IDENTITY カラム
import { pgTable, integer, generatedAlwaysAsIdentity } from 'drizzle-orm/pg-core'
export const posts = pgTable('posts', {
id: integer('id').primaryKey().generatedAlwaysAsIdentity(),
})
クエリビルダー
import { eq } from 'drizzle-orm'
const user = await db
.select()
.from(users)
.where(eq(users.id, userId))
.limit(1)
const userWithPosts = await db.query.users.findFirst({
where: eq(users.id, userId),
with: { posts: true },
})
const userEmail = await db
.select({ email: users.email })
.from(users)
.where(eq(users.id, userId))
トランザクション
await db.transaction(async (tx) => {
const [user] = await tx.insert(users).values(userData).returning()
await tx.insert(profiles).values({ userId: user.id, ...profileData })
})
プリペアドステートメント
import { placeholder } from 'drizzle-orm'
const getUserByEmail = db
.select()
.from(users)
.where(eq(users.email, placeholder('email')))
.prepare('get_user_by_email')
const user = await getUserByEmail.execute({ email: '[email protected]' })
エラー処理
import { DrizzleQueryError } from 'drizzle-orm'
try {
const user = await db.select().from(users).where(eq(users.id, userId))
} catch (error) {
if (error instanceof DrizzleQueryError) {
if (error.cause?.code === '23505') {
throw new Error('User already exists')
}
}
throw error
}
リレーション
import { relations } from 'drizzle-orm'
export const usersRelations = relations(users, ({ many }) => ({
posts: many(posts),
}))
export const postsRelations = relations(posts, ({ one }) => ({
author: one(users, {
fields: [posts.authorId],
references: [users.id],
}),
}))
データベース接続
import { drizzle } from 'drizzle-orm/node-postgres'
import { Pool } from 'pg'
import * as schema from './schema'
const pool = new Pool({ connectionString: process.env.DATABASE_URL })
export const db = drizzle(pool, { schema })
Drizzle Kit の設定
import { defineConfig } from 'drizzle-kit'
export default defineConfig({
dialect: 'postgresql',
schema: './src/db/schema/index.ts',
out: './src/db/migrations',
dbCredentials: { url: process.env.DATABASE_URL! },
migrations: {
table: '__drizzle_migrations',
schema: 'public',
},
verbose: true,
strict: true,
})
参考資料
- クエリパターン - CRUD 操作、結合、集計
- PostgreSQL パターン - PostgreSQL 固有のパターン
コメント
Markdown 対応