mcpField
D
drizzle-orm-v0

drizzle-orm-v0

github.com/blockmaticデータ・データベース1222 回インストール4 週間前に更新
リポジトリ

drizzle-orm-v0 について

PostgreSQL、MySQL、SQLite で Drizzle ORM v0.45+ を使う TypeScript 開発者向けのスキルです。型付きスキーマやクエリ、Drizzle Kit によるマイグレーション、トランザクションの実践方法を扱います。

主な機能

  1. 01型付きテーブルと列の定義
  2. 02型安全なクエリの構築
  3. 03Drizzle Kit によるマイグレーション管理
  4. 04スキーマからレコード型を推論
  5. 05複数処理でのトランザクション利用

活用シーン

  1. 01PostgreSQL、MySQL、SQLite のスキーマ作成
  2. 02型安全な CRUD クエリの記述
  3. 03マイグレーションの生成と適用
  4. 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.md
queries.md
templates/
db.ts
schema.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 モードを使用すること

相互運用性

  • next の Server Components および API ルートと連携します
  • API 開発では fastify と組み合わせて使用できます

パターン

スキーマ定義
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,
})

参考資料

コメント