Drizzle ORM

Drizzle ORMは、SQL中心のTypeScript ORMです。軽量で依存関係ゼロ、Serverless環境に最適化されており、「SQLを知っていればDrizzleも知っている」をコンセプトに、高いパフォーマンスと直感的なAPI設計を両立したHeadless ORMです。

ORMTypeScriptSQLデータベースServerlessドリズル

GitHub概要

drizzle-team/drizzle-orm

Headless TypeScript ORM with a head. Runs on Node, Bun and Deno. Lives on the Edge and yes, it's a JavaScript ORM too 😅

スター29,260
ウォッチ55
フォーク936
作成日:2021年6月24日
言語:TypeScript
ライセンス:Apache License 2.0

トピックス

bunjsd1libsqllitefsmysqlmysql2neonnodejsormpostgrespostgresjspostgresqlsqlsqlitesqlite3sqljstursotypescriptvercel-postgres

スター履歴

drizzle-team/drizzle-orm Star History
データ取得日時: 2025/7/17 10:32

ライブラリ

Drizzle ORM

概要

Drizzle ORMは、SQL中心のTypeScript ORMです。軽量で依存関係ゼロ、Serverless環境に最適化されており、「SQLを知っていればDrizzleも知っている」をコンセプトに、高いパフォーマンスと直感的なAPI設計を両立したHeadless ORMです。

詳細

Drizzleは従来のORMとは異なり、SQL-firstアプローチを採用しています。SQLを抽象化するのではなく、SQLの力を活用しながらTypeScriptの型安全性を提供します。これにより、パフォーマンスを犠牲にすることなく、開発者の生産性を向上させます。

主な特徴

  • SQL-first設計: 生のSQLに近い記述で高性能なクエリ
  • ゼロ依存関係: 軽量で高速、バンドルサイズを最小化
  • 型安全性: 完全なTypeScript統合
  • Serverless最適化: Edge Computing環境での優秀な動作
  • マイグレーション: スキーマドリブンな安全なデータベース変更

メリット・デメリット

メリット

  • 軽量で高速、特にServerless環境での優秀なパフォーマンス
  • SQLに慣れた開発者にとって学習コストが非常に低い
  • 完全な型安全性でランタイムエラーを削減
  • 依存関係なしでプロジェクトのセキュリティリスクを軽減
  • Edge ComputingやCloudflare Workersで最適化された動作

デメリット

  • まだ比較的新しいため、エコシステムが発展途上
  • ActiveRecordパターンに慣れた開発者には学習が必要
  • ORM機能は最小限でよりSQLの知識が求められる
  • 複雑なリレーションシップ操作では手動でのクエリ構築が必要

参考ページ

書き方の例

インストールと基本セットアップ

npm install drizzle-orm drizzle-kit
npm install @types/pg pg # PostgreSQLの場合
// drizzle.config.ts
import { defineConfig } from 'drizzle-kit';

export default defineConfig({
  schema: './src/schema.ts',
  dialect: 'postgresql',
  dbCredentials: {
    url: process.env.DATABASE_URL!,
  },
  verbose: true,
  strict: true,
});

基本的なCRUD操作

// schema.ts
import { pgTable, serial, text, timestamp } from 'drizzle-orm/pg-core';

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

export const posts = pgTable('posts', {
  id: serial('id').primaryKey(),
  title: text('title').notNull(),
  content: text('content'),
  authorId: integer('author_id').references(() => users.id),
  createdAt: timestamp('created_at').defaultNow(),
});

// データベース操作
import { drizzle } from 'drizzle-orm/node-postgres';
import { Client } from 'pg';
import { users, posts } from './schema';

const client = new Client({
  connectionString: process.env.DATABASE_URL,
});

const db = drizzle(client);

// 作成
const newUser = await db.insert(users).values({
  name: '田中太郎',
  email: '[email protected]'
}).returning();

// 読み取り
const allUsers = await db.select().from(users);

// 更新
await db.update(users)
  .set({ name: '田中次郎' })
  .where(eq(users.id, 1));

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

高度なクエリとリレーションシップ

import { eq, sql } from 'drizzle-orm';

// JOINクエリ
const usersWithPosts = await db
  .select({
    userId: users.id,
    userName: users.name,
    postTitle: posts.title,
    postCount: sql<number>`count(${posts.id})`,
  })
  .from(users)
  .leftJoin(posts, eq(users.id, posts.authorId))
  .groupBy(users.id)
  .having(sql`count(${posts.id}) > 0`);

// 複雑な条件クエリ
const activeUsers = await db
  .select()
  .from(users)
  .where(
    and(
      gt(users.createdAt, sql`NOW() - INTERVAL '30 days'`),
      like(users.email, '%@company.com')
    )
  )
  .orderBy(desc(users.createdAt))
  .limit(10);

// サブクエリ
const sq = db
  .select({ count: sql<number>`count(*)` })
  .from(posts)
  .where(eq(posts.authorId, users.id))
  .as('post_count');

const usersWithPostCounts = await db
  .select({
    user: users,
    postCount: sq.count,
  })
  .from(users)
  .leftJoin(sq, eq(users.id, sq.authorId));

マイグレーションとスキーマ管理

# マイグレーション生成
npx drizzle-kit generate

# マイグレーション適用
npx drizzle-kit migrate
// 手動マイグレーション実行
import { migrate } from 'drizzle-orm/node-postgres/migrator';

await migrate(db, { migrationsFolder: './drizzle' });

パフォーマンス最適化と高度な機能

// プリペアドステートメント
const getUserById = db
  .select()
  .from(users)
  .where(eq(users.id, placeholder('id')))
  .prepare();

const user = await getUserById.execute({ id: 1 });

// トランザクション
await db.transaction(async (tx) => {
  const user = await tx.insert(users).values({
    name: '山田花子',
    email: '[email protected]'
  }).returning();

  await tx.insert(posts).values({
    title: '最初の投稿',
    content: 'Drizzleを使った投稿です',
    authorId: user[0].id
  });
});

// バッチ操作
await db.insert(users).values([
  { name: 'ユーザー1', email: '[email protected]' },
  { name: 'ユーザー2', email: '[email protected]' },
  { name: 'ユーザー3', email: '[email protected]' },
]);

フレームワーク統合と実用例

// Next.js App Router統合
// app/api/users/route.ts
import { NextRequest, NextResponse } from 'next/server';
import { db } from '@/lib/db';
import { users } from '@/lib/schema';

export async function GET() {
  try {
    const allUsers = await db.select().from(users);
    return NextResponse.json(allUsers);
  } catch (error) {
    return NextResponse.json(
      { error: 'ユーザーの取得に失敗しました' },
      { status: 500 }
    );
  }
}

// Cloudflare Workers統合
export default {
  async fetch(request: Request, env: Env): Promise<Response> {
    const db = drizzle(env.DB);
    
    const users = await db.select().from(usersTable);
    
    return Response.json(users);
  }
};