はじめに
Drizzle ORMは、TypeScript/JavaScript向けの軽量ORMですね。
GitHubで31,600スター以上、月間1,050万ダウンロードという数字を見ると、急速に普及しているのがわかります。個人的には、2025年の今、TypeScriptでバックエンドを書くなら真っ先に検討すべきORMだと思っています。
正直なところ、最初は「Prismaで十分じゃん」と思っていたんですよ。でも実際にDrizzle ORMを使ってみたら、型推論の精度が高くて、SQLに近い直感的な記法で、「これ、めちゃくちゃ書きやすいな」という感覚でした。
ライセンスはApache-2.0で、安心して使えます。
Drizzle ORMとは
Drizzle ORMは「Headless TypeScript ORM」を掲げるORMです。ヘッドレスという名前の通り、軽量でありながら必要な機能はしっかり揃っている。
特筆すべきは、わずか7.4kb(minified + gzipped)で依存関係ゼロという点。これ、意外と重要で、バンドルサイズを気にするプロジェクトでも気兼ねなく導入できます。
特徴・メリット
1. 型安全なクエリビルダー
Drizzle ORMの最大の魅力は、TypeScriptとの相性の良さですね。
スキーマ定義からクエリ結果まで、すべてが型で守られています。エディタの補完も効くし、間違ったカラム名を書けばコンパイル時にエラーになる。
30代になって思うのは、型安全なツールはバグを未然に防いでくれるということ。
2. SQLライクな直感的記法
Prismaのような独自DSLではなく、SQLに近い記法でクエリを書けます。
const users = await db.select().from(usersTable).where(eq(usersTable.age, 30));
SQLを知っている人なら、学習コストがほぼゼロ。「SELECT * FROM users WHERE age = 30」がそのまま頭の中で対応する。
3. 対応データベースが豊富
Drizzle ORMは主要なデータベースをすべてサポートしています。
リレーショナルDB:
- PostgreSQL
- MySQL
- SQLite
サーバーレスDB:
- Neon
- PlanetScale
- Turso
- Vercel Postgres
- Cloudflare D1
- Supabase
その他:
- CockroachDB
- SingleStore
- TiDB
サーバーレス対応が充実しているのは、今どきのアーキテクチャに合っている。
4. 軽量で高速
依存関係ゼロ、7.4kbという軽さは伊達じゃない。
起動が速いし、メモリ消費も少ない。エッジランタイム(Cloudflare Workers、Vercel Edge Functions)でも快適に動きます。コスパ的に、サーバーレスとの相性が良い。
5. Drizzle Kit & Drizzle Studio
Drizzle ORMには強力なエコシステムがあります。
- Drizzle Kit: マイグレーション管理CLI
- Drizzle Studio: データベースブラウザUI
特にDrizzle Studioは、ブラウザ上でデータベースの中身を確認・編集できて便利。開発中のデバッグ作業がQOL上がった。
6. 多様なランタイム対応
Node.js以外でも動きます。
- Node.js
- Bun
- Deno
- Cloudflare Workers
- Vercel Functions
- その他エッジランタイム
- ブラウザ(WASM版)
Bunユーザーとしては、ネイティブ対応しているのがありがたい。
インストール方法
前提条件
Node.js 18以上を推奨。
基本インストール
# npm
npm i drizzle-orm
# pnpm
pnpm add drizzle-orm
# yarn
yarn add drizzle-orm
# bun
bun add drizzle-orm
データベースドライバーのインストール
使用するデータベースに応じてドライバーも追加します。
# PostgreSQL
npm i drizzle-orm postgres
npm i -D drizzle-kit
# MySQL
npm i drizzle-orm mysql2
npm i -D drizzle-kit
# SQLite (better-sqlite3)
npm i drizzle-orm better-sqlite3
npm i -D drizzle-kit @types/better-sqlite3
# Turso / LibSQL
npm i drizzle-orm @libsql/client
npm i -D drizzle-kit
Drizzle Kitの設定
drizzle.config.tsを作成:
import { defineConfig } from 'drizzle-kit';
export default defineConfig({
schema: './src/db/schema.ts',
out: './drizzle',
dialect: 'postgresql',
dbCredentials: {
url: process.env.DATABASE_URL!,
},
});
基本的な使い方
スキーマ定義
src/db/schema.tsを作成:
import { pgTable, serial, text, integer, timestamp, varchar } from 'drizzle-orm/pg-core';
// ユーザーテーブル
export const users = pgTable('users', {
id: serial('id').primaryKey(),
name: varchar('name', { length: 255 }).notNull(),
email: varchar('email', { length: 255 }).notNull().unique(),
age: integer('age'),
createdAt: timestamp('created_at').defaultNow().notNull(),
});
// 投稿テーブル
export const posts = pgTable('posts', {
id: serial('id').primaryKey(),
title: varchar('title', { length: 255 }).notNull(),
content: text('content'),
authorId: integer('author_id').references(() => users.id),
createdAt: timestamp('created_at').defaultNow().notNull(),
});
SQLのCREATE TABLE文に近い感覚で書けるのが良い。
データベース接続
import { drizzle } from 'drizzle-orm/postgres-js';
import postgres from 'postgres';
import * as schema from './schema';
const queryClient = postgres(process.env.DATABASE_URL!);
export const db = drizzle(queryClient, { schema });
CRUD操作
import { eq, and, gt, desc } from 'drizzle-orm';
import { db } from './db';
import { users, posts } from './schema';
// INSERT
const newUser = await db.insert(users).values({
name: '山田太郎',
email: 'taro@example.com',
age: 30,
}).returning();
// SELECT
const allUsers = await db.select().from(users);
// WHERE句
const adults = await db
.select()
.from(users)
.where(gt(users.age, 18));
// 複数条件
const targetUsers = await db
.select()
.from(users)
.where(and(
eq(users.name, '山田太郎'),
gt(users.age, 25)
));
// ORDER BY + LIMIT
const recentUsers = await db
.select()
.from(users)
.orderBy(desc(users.createdAt))
.limit(10);
// UPDATE
await db
.update(users)
.set({ age: 31 })
.where(eq(users.id, 1));
// DELETE
await db.delete(users).where(eq(users.id, 1));
SQLを書いている感覚でTypeScriptが書ける。これ一択ですね。
リレーショナルクエリ
Drizzle ORMには、JOINを使わずにリレーションを取得できる機能もあります。
// スキーマでリレーションを定義
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],
}),
}));
// リレーショナルクエリ
const usersWithPosts = await db.query.users.findMany({
with: {
posts: true,
},
});
マイグレーション
# マイグレーションファイル生成
npx drizzle-kit generate
# マイグレーション実行
npx drizzle-kit migrate
# Drizzle Studio起動
npx drizzle-kit studio
実践的なユースケース
Next.js App Routerとの統合
src/db/index.ts:
import { drizzle } from 'drizzle-orm/postgres-js';
import postgres from 'postgres';
import * as schema from './schema';
const globalForDb = globalThis as unknown as {
conn: postgres.Sql | undefined;
};
const conn = globalForDb.conn ?? postgres(process.env.DATABASE_URL!);
if (process.env.NODE_ENV !== 'production') globalForDb.conn = conn;
export const db = drizzle(conn, { schema });
Server Actionsでの使用例:
'use server';
import { db } from '@/db';
import { users } from '@/db/schema';
import { eq } from 'drizzle-orm';
export async function getUser(id: number) {
const result = await db.select().from(users).where(eq(users.id, id));
return result[0] ?? null;
}
export async function createUser(name: string, email: string) {
const result = await db.insert(users).values({ name, email }).returning();
return result[0];
}
Honoとの統合
import { Hono } from 'hono';
import { drizzle } from 'drizzle-orm/d1';
import { users } from './schema';
type Bindings = {
DB: D1Database;
};
const app = new Hono<{ Bindings: Bindings }>();
app.get('/users', async (c) => {
const db = drizzle(c.env.DB);
const result = await db.select().from(users);
return c.json(result);
});
export default app;
Cloudflare D1 + Honoの組み合わせは、サーバーレスAPIを作るときに便利。
トランザクション
await db.transaction(async (tx) => {
const [user] = await tx.insert(users).values({
name: '新規ユーザー',
email: 'new@example.com',
}).returning();
await tx.insert(posts).values({
title: '最初の投稿',
content: 'こんにちは!',
authorId: user.id,
});
});
Prismaとの比較
正直な比較をしておきます。
| 観点 | Drizzle ORM | Prisma |
|---|---|---|
| バンドルサイズ | 7.4kb | 大きめ |
| 学習コスト | SQL知識があれば低い | 独自DSLの学習が必要 |
| 型推論 | 優秀 | 優秀 |
| エッジランタイム対応 | ネイティブ対応 | Accelerate経由 |
| マイグレーション | Drizzle Kit | Prisma Migrate |
| GUI | Drizzle Studio | Prisma Studio |
| エコシステム | 成長中 | 成熟 |
| 依存関係 | ゼロ | 多め |
個人的には、新規プロジェクトでエッジランタイムを使う予定があるならDrizzle ORM、既存の大規模プロジェクトで実績重視ならPrismaという使い分けをしています。
Drizzle ORMの弱点は、Prismaほどエコシステムが成熟していないこと。ただし、急速に発展しているので、数ヶ月後には状況が変わっている可能性もあります。
まとめ
Drizzle ORMを導入して感じた変化:
- 型安全性: スキーマからクエリまで型で守られる
- 記法: SQLに近くて直感的
- 軽量: 依存関係ゼロ、7.4kb
- 速度: 起動も実行も速い
- エッジ対応: Cloudflare Workersでも快適
- QOL: 確実に上がった
TypeScriptでバックエンドを書くなら、Drizzle ORMは間違いなく検討すべき選択肢ですね。
特に「SQLは書けるけど、ORMの独自記法を覚えるのが面倒」という人には、Drizzle ORMがハマると思います。SQLの知識がそのまま活きる。
これ、意外とPrismaから乗り換えた人が多い印象です。「Prismaは便利だけど、もう少しSQL寄りの記法で書きたい」というニーズに応えてくれる。