mcpField
D
drizzle

drizzle

github.com/bobmatnycデータ・データベース784.3k 回インストール15 週間前に更新
リポジトリ

drizzle について

drizzleは、データベースを使うアプリケーション開発者向けのTypeScript優先ORMスキルです。型安全なスキーマ、SQL風クエリ、トランザクション、PostgreSQL・MySQL・SQLiteのマイグレーションを扱います。

主な機能

  1. 01型安全なTypeScriptスキーマ
  2. 02SQL風クエリと結合
  3. 03ロールバック対応トランザクション
  4. 04マイグレーションの生成と適用
  5. 05PostgreSQL、MySQL、SQLite対応

活用シーン

  1. 01リレーショナルスキーマの設計
  2. 02絞り込みやページングのクエリ作成
  3. 03複数段階の更新の安全な実行
  4. 04データベーススキーマ変更の管理

インストール

コマンドをコピーしてターミナルで実行します。

$npx skills add https://github.com/bobmatnyc/claude-mpm-skills --skill drizzle-orm

インストール後、Claude Code で/drizzle-ormを入力して読み込まれたか確認します。

同梱ファイル

SKILL.md と一緒にインストールされる参考資料やスクリプト。

metadata.json
references/
advanced-schemas.md
performance.md
query-patterns.md
vs-prisma.md

詳細

Drizzle ORM

依存関係ゼロ、コンパイル時の型安全性、SQL に似た構文を備えた、TypeScript を第一に考えたモダンな ORM です。エッジランタイムとサーバーレス環境向けに最適化されています。

クイックスタート

インストール
# Core ORM
npm install drizzle-orm

# Database driver (choose one)
npm install pg            # PostgreSQL
npm install mysql2        # MySQL
npm install better-sqlite3 # SQLite

# Drizzle Kit (migrations)
npm install -D drizzle-kit
基本設定
// db/schema.ts
import { pgTable, serial, text, timestamp } from 'drizzle-orm/pg-core';

export const users = pgTable('users', {
  id: serial('id').primaryKey(),
  email: text('email').notNull().unique(),
  name: text('name').notNull(),
  createdAt: timestamp('created_at').defaultNow(),
});

// db/client.ts
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 });
最初のクエリ
import { db } from './db/client';
import { users } from './db/schema';
import { eq } from 'drizzle-orm';

// Insert
const newUser = await db.insert(users).values({
  email: '[email protected]',
  name: 'John Doe',
}).returning();

// Select
const allUsers = await db.select().from(users);

// Where
const user = await db.select().from(users).where(eq(users.id, 1));

// Update
await db.update(users).set({ name: 'Jane Doe' }).where(eq(users.id, 1));

// Delete
await db.delete(users).where(eq(users.id, 1));

スキーマ定義

カラム型のリファレンス
PostgreSQLMySQLSQLiteTypeScript
serial()serial()integer()number
text()text()text()string
integer()int()integer()number
boolean()boolean()integer()boolean
timestamp()datetime()integer()Date
json()json()text()unknown
uuid()varchar(36)text()string
一般的なスキーマパターン
import { pgTable, serial, text, varchar, integer, boolean, timestamp, json, unique } from 'drizzle-orm/pg-core';

export const users = pgTable('users', {
  id: serial('id').primaryKey(),
  email: varchar('email', { length: 255 }).notNull().unique(),
  passwordHash: varchar('password_hash', { length: 255 }).notNull(),
  role: text('role', { enum: ['admin', 'user', 'guest'] }).default('user'),
  metadata: json('metadata').$type<{ theme: string; locale: string }>(),
  isActive: boolean('is_active').default(true),
  createdAt: timestamp('created_at').defaultNow().notNull(),
  updatedAt: timestamp('updated_at').defaultNow().notNull(),
}, (table) => ({
  emailIdx: unique('email_unique_idx').on(table.email),
}));

// Infer TypeScript types
type User = typeof users.$inferSelect;
type NewUser = typeof users.$inferInsert;

リレーション

1 対多
import { pgTable, serial, text, integer } from 'drizzle-orm/pg-core';
import { relations } from 'drizzle-orm';

export const authors = pgTable('authors', {
  id: serial('id').primaryKey(),
  name: text('name').notNull(),
});

export const posts = pgTable('posts', {
  id: serial('id').primaryKey(),
  title: text('title').notNull(),
  authorId: integer('author_id').notNull().references(() => authors.id),
});

export const authorsRelations = relations(authors, ({ many }) => ({
  posts: many(posts),
}));

export const postsRelations = relations(posts, ({ one }) => ({
  author: one(authors, {
    fields: [posts.authorId],
    references: [authors.id],
  }),
}));

// Query with relations
const authorsWithPosts = await db.query.authors.findMany({
  with: { posts: true },
});
多対多
export const users = pgTable('users', {
  id: serial('id').primaryKey(),
  name: text('name').notNull(),
});

export const groups = pgTable('groups', {
  id: serial('id').primaryKey(),
  name: text('name').notNull(),
});

export const usersToGroups = pgTable('users_to_groups', {
  userId: integer('user_id').notNull().references(() => users.id),
  groupId: integer('group_id').notNull().references(() => groups.id),
}, (table) => ({
  pk: primaryKey({ columns: [table.userId, table.groupId] }),
}));

export const usersRelations = relations(users, ({ many }) => ({
  groups: many(usersToGroups),
}));

export const groupsRelations = relations(groups, ({ many }) => ({
  users: many(usersToGroups),
}));

export const usersToGroupsRelations = relations(usersToGroups, ({ one }) => ({
  user: one(users, { fields: [usersToGroups.userId], references: [users.id] }),
  group: one(groups, { fields: [usersToGroups.groupId], references: [groups.id] }),
}));

クエリ

フィルタリング
import { eq, ne, gt, gte, lt, lte, like, ilike, inArray, isNull, isNotNull, and, or, between } from 'drizzle-orm';

// Equality
await db.select().from(users).where(eq(users.email, '[email protected]'));

// Comparison
await db.select().from(users).where(gt(users.id, 10));

// Pattern matching
await db.select().from(users).where(like(users.name, '%John%'));

// Multiple conditions
await db.select().from(users).where(
  and(
    eq(users.role, 'admin'),
    gt(users.createdAt, new Date('2024-01-01'))
  )
);

// IN clause
await db.select().from(users).where(inArray(users.id, [1, 2, 3]));

// NULL checks
await db.select().from(users).where(isNull(users.deletedAt));
結合
import { eq } from 'drizzle-orm';

// Inner join
const result = await db
  .select({
    user: users,
    post: posts,
  })
  .from(users)
  .innerJoin(posts, eq(users.id, posts.authorId));

// Left join
const result = await db
  .select({
    user: users,
    post: posts,
  })
  .from(users)
  .leftJoin(posts, eq(users.id, posts.authorId));

// Multiple joins with aggregation
import { count, sql } from 'drizzle-orm';

const result = await db
  .select({
    authorName: authors.name,
    postCount: count(posts.id),
  })
  .from(authors)
  .leftJoin(posts, eq(authors.id, posts.authorId))
  .groupBy(authors.id);
ページネーションと並べ替え
import { desc, asc } from 'drizzle-orm';

// Order by
await db.select().from(users).orderBy(desc(users.createdAt));

// Limit & offset
await db.select().from(users).limit(10).offset(20);

// Pagination helper
function paginate(page: number, pageSize: number = 10) {
  return db.select().from(users)
    .limit(pageSize)
    .offset(page * pageSize);
}

トランザクション

// Auto-rollback on error
await db.transaction(async (tx) => {
  await tx.insert(users).values({ email: '[email protected]', name: 'John' });
  await tx.insert(posts).values({ title: 'First Post', authorId: 1 });
  // If any query fails, entire transaction rolls back
});

// Manual control
const tx = db.transaction(async (tx) => {
  const user = await tx.insert(users).values({ ... }).returning();

  if (!user) {
    tx.rollback();
    return;
  }

  await tx.insert(posts).values({ authorId: user.id });
});

マイグレーション

Drizzle Kit の設定
// drizzle.config.ts
import type { Config } from 'drizzle-kit';

export default {
  schema: './db/schema.ts',
  out: './drizzle',
  dialect: 'postgresql',
  dbCredentials: {
    url: process.env.DATABASE_URL!,
  },
} satisfies Config;
マイグレーションのワークフロー
# Generate migration
npx drizzle-kit generate

# View SQL
cat drizzle/0000_migration.sql

# Apply migration
npx drizzle-kit migrate

# Introspect existing database
npx drizzle-kit introspect

# Drizzle Studio (database GUI)
npx drizzle-kit studio
マイグレーションの例
-- drizzle/0000_initial.sql
CREATE TABLE IF NOT EXISTS "users" (
  "id" serial PRIMARY KEY NOT NULL,
  "email" varchar(255) NOT NULL,
  "name" text NOT NULL,
  "created_at" timestamp DEFAULT now() NOT NULL,
  CONSTRAINT "users_email_unique" UNIQUE("email")
);

ナビゲーション

詳細なリファレンス
  • 🏗️ 高度なスキーマ - カスタム型、複合キー、インデックス、制約、マルチテナントパターン。複雑なデータベーススキーマを設計する際に読み込んでください。

  • 🔍 クエリパターン - サブクエリ、CTE、SQL の直接記述、プリペアドステートメント、バッチ処理。クエリの最適化や複雑なフィルタリングを行う際に読み込んでください。

  • ⚡ パフォーマンス - コネクションプーリング、クエリ最適化、N+1 の防止、プリペアドステートメント、エッジランタイムとの統合。スケール拡大やデータベースのパフォーマンス最適化を行う際に読み込んでください。

  • 🔄 Prisma との比較 - 機能比較、マイグレーションガイド、Prisma より Drizzle を選ぶべき状況。ORM を評価する場合や、Prisma から移行する場合に読み込んでください。

注意すべき点

次のような場合は、いったん立ち止まって再検討してください。

  • JSON カラムに型注釈を付けずに any または unknown を使用している
  • sql テンプレートを使わずに生の SQL 文字列を組み立てている(SQL インジェクションのリスク)
  • 複数ステップにわたるデータ変更でトランザクションを使用していない
  • 本番環境のクエリでページネーションを使わずに全行を取得している
  • 外部キーまたは頻繁にクエリされるカラムにインデックスがない
  • 大きなテーブルで、取得するカラムを指定せずに select() を使用している

Prisma と比較したパフォーマンス上の利点

指標DrizzlePrisma
バンドルサイズ~35KB~230KB
コールドスタート~10ms~250ms
クエリ速度基準約 2~3 倍遅い
メモリ~10MB~50MB
型生成実行時の推論ビルド時の生成

統合

  • typescript-core:satisfies による型安全なスキーマ推論
  • nextjs-core:Server Actions、Route Handlers、Middleware との統合
  • データベースマイグレーション:安全なスキーマ進化のパターン

関連スキル

Drizzle を使用する際は、次のスキルがワークフローの強化に役立ちます。

  • prisma:代替 ORM の比較:Drizzle と Prisma のトレードオフ
  • typescript:型安全なクエリのための高度な TypeScript パターン
  • nextjs:Next.js の Server Actions および API ルートでの Drizzle の使用
  • sqlalchemy:Drizzle を学ぶ Python 開発者向けの SQLAlchemy パターン

[これらのスキルがバンドルにデプロイされている場合は、完全なドキュメントを利用できます]

コメント