Drizzle ORM vs Prisma — 2024-2025年版 徹底比較
1. 結論
SQL に近い柔軟な制御と軽量さを求めるなら Drizzle ORM、スキーマ駆動の開発体験と成熟したエコシステムを重視するなら Prisma を選んでください。Drizzle は「TypeScript で SQL を書く」感覚に近く、Prisma は「スキーマファーストで型安全な抽象化」を提供します。プロジェクトの規模・チームの SQL 習熟度・デプロイ先(Edge 環境かどうか)によって最適解が変わります。
2. 比較表
| 観点 | Drizzle ORM | Prisma |
|---|---|---|
| 初回リリース | 2022年 | 2019年(v1: 2021年) |
| npm 週間DL数 | 約 100万+ | 約 300万+ |
| GitHub Stars | ≈ 26k | ≈ 40k |
| アプローチ | コードファースト(TypeScript DSL) | スキーマファースト(.prisma ファイル) |
| クエリ記法 | SQL ライクな関数チェーン + リレーショナルクエリ API | 独自のクライアント API |
| TypeScript 対応 | ネイティブ(100% TypeScript) | 自動生成型(Prisma Client) |
| バンドルサイズ | ≈ 50–80 KB(ドライバ別) | ≈ 2–10 MB(Query Engine バイナリ含む) |
| Edge Runtime 対応 | ◎(Cloudflare Workers, Bun 等) | △(Prisma Accelerate / Driver Adapters 経由) |
| マイグレーション | drizzle-kit(SQL 生成 / push) | prisma migrate(成熟した履歴管理) |
| GUI ツール | Drizzle Studio(ブラウザベース) | Prisma Studio(デスクトップ / ブラウザ) |
| 対応 DB | PostgreSQL, MySQL, SQLite, Turso, Neon, PlanetScale 等 | PostgreSQL, MySQL, SQLite, SQL Server, MongoDB, CockroachDB 等 |
| Raw SQL | sql テンプレートリテラルで自在 | $queryRaw / $executeRaw |
| リレーション定義 | TypeScript コード内で relations() | .prisma スキーマ内で宣言 |
| 学習コスト | SQL を知っていれば低い | Prisma スキーマ言語の習得が必要 |
| N+1 対策 | リレーショナルクエリ API で JOIN / サブクエリ自動選択 | include / select で Eager Loading |
| コミュニティ・エコシステム | 急成長中、プラグインは少なめ | 成熟、Auth.js / tRPC 等との統合豊富 |
| ライセンス | Apache 2.0 | Apache 2.0 |
3. それぞれの強み
Drizzle ORM の強み
- SQL との距離が近い: クエリビルダーが SQL の構文をほぼそのまま TypeScript に写し取った設計です。
SELECT,WHERE,JOIN,GROUP BYなどを関数として組み立てるため、生成される SQL が予測しやすく、パフォーマンスチューニングが容易です。 - バンドルサイズが極めて小さい: ネイティブバイナリを持たず、純粋な JavaScript/TypeScript で動作します。Cloudflare Workers や Vercel Edge Functions など、サイズ制限の厳しい Edge 環境で大きなアドバンテージがあります。
- ゼロ依存に近い設計: DB ドライバ(
postgres,better-sqlite3,@libsql/client等)を直接利用するため、抽象化レイヤーが薄く、ドライバの機能をフル活用できます。 - スキーマ定義がそのまま型になる:
.prismaのような中間言語を介さず、TypeScript のコードがそのまま Single Source of Truth になります。コード生成ステップが不要です。 - サーバーレス・Edge ファースト: コールドスタートが高速で、Bun / Deno との相性も良好です。
Prisma の強み
- 成熟したマイグレーションシステム:
prisma migrate dev/prisma migrate deployによる履歴管理は本番運用で信頼性が高く、チーム開発でのスキーマ変更管理が洗練されています。 - スキーマファーストの明快さ:
.prismaファイルを見ればデータモデルの全体像が一目で把握できます。バックエンドエンジニア以外(フロントエンド、PM)にも読みやすい DSL です。 - 豊富なエコシステム統合: Auth.js (NextAuth)、tRPC、Pothos (GraphQL)、Keystoneなど、多くのフレームワーク・ライブラリが Prisma をファーストクラスでサポートしています。
- MongoDB 対応: NoSQL データベースにも対応しており、SQL 以外の選択肢があります。
- Prisma Accelerate / Pulse: 接続プーリング(Accelerate)やリアルタイムイベント(Pulse)など、マネージドサービスとの統合が進んでいます。
- 圧倒的なドキュメント量: 公式ドキュメントが非常に充実しており、エッジケースの解説やベストプラクティスが豊富です。
4. コード例で比較
以下では、PostgreSQL を使った「ユーザーと投稿」の典型的な CRUD を両方で実装します。
4-1. スキーマ定義
Drizzle ORM — src/db/schema.ts
import { pgTable, serial, text, timestamp, integer } from "drizzle-orm/pg-core";
import { relations } from "drizzle-orm";
// テーブル定義
export const users = pgTable("users", {
id: serial("id").primaryKey(),
name: text("name").notNull(),
email: text("email").notNull().unique(),
createdAt: timestamp("created_at").defaultNow().notNull(),
});
export const posts = pgTable("posts", {
id: serial("id").primaryKey(),
title: text("title").notNull(),
content: text("content"),
authorId: integer("author_id")
.notNull()
.references(() => users.id),
createdAt: timestamp("created_at").defaultNow().notNull(),
});
// リレーション定義(クエリ API 用)
export const usersRelations = relations(users, ({ many }) => ({
posts: many(posts),
}));
export const postsRelations = relations(posts, ({ one }) => ({
author: one(users, {
fields: [posts.authorId],
references: [users.id],
}),
}));
Prisma — prisma/schema.prisma
generator client {
provider = "prisma-client-js"
}
datasource db {
provider = "postgresql"
url = env("DATABASE_URL")
}
model User {
id Int @id @default(autoincrement())
name String
email String @unique
createdAt DateTime @default(now()) @map("created_at")
posts Post[]
@@map("users")
}
model Post {
id Int @id @default(autoincrement())
title String
content String?
authorId Int @map("author_id")
createdAt DateTime @default(now()) @map("created_at")
author User @relation(fields: [authorId], references: [id])
@@map("posts")
}
4-2. DB クライアントの初期化
Drizzle ORM
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 });
Prisma
import { PrismaClient } from "@prisma/client";
export const prisma = new PrismaClient();
4-3. CRUD 操作
ユーザー作成(INSERT)
// --- Drizzle ---
import { db } from "./db";
import { users } from "./db/schema";
const newUser = await db
.insert(users)
.values({ name: "田中太郎", email: "tanaka@example.com" })
.returning();
console.log(newUser[0]); // { id: 1, name: "田中太郎", ... }
// --- Prisma ---
import { prisma } from "./db";
const newUser = await prisma.user.create({
data: { name: "田中太郎", email: "tanaka@example.com" },
});
console.log(newUser); // { id: 1, name: "田中太郎", ... }
条件付き取得(SELECT + WHERE)
// --- Drizzle ---
import { eq } from "drizzle-orm";
const user = await db
.select()
.from(users)
.where(eq(users.email, "tanaka@example.com"))
.limit(1);
// --- Prisma ---
const user = await prisma.user.findUnique({
where: { email: "tanaka@example.com" },
});
リレーション込みの取得
// --- Drizzle(リレーショナルクエリ API)---
const usersWithPosts = await db.query.users.findMany({
with: {
posts: true,
},
});
// --- Prisma ---
const usersWithPosts = await prisma.user.findMany({
include: {
posts: true,
},
});
複雑なクエリ(集計 + JOIN)
// --- Drizzle ---
import { eq, count, desc } from "drizzle-orm";
const authorPostCounts = await db
.select({
authorName: users.name,
postCount: count(posts.id),
})
.from(users)
.leftJoin(posts, eq(users.id, posts.authorId))
.groupBy(users.id, users.name)
.orderBy(desc(count(posts.id)));
// --- Prisma ---
const authorPostCounts = await prisma.user.findMany({
select: {
name: true,
_count: {
select: { posts: true },
},
},
orderBy: {
posts: { _count: "desc" },
},
});
ポイント: Drizzle は SQL の
GROUP BY/LEFT JOINをそのまま書けるため、生成 SQL が明確です。Prisma は_countのような抽象化で簡潔に書けますが、内部で生成される SQL を意識しにくい場面があります。
更新(UPDATE)
// --- Drizzle ---
await db
.update(users)
.set({ name: "田中次郎" })
.where(eq(users.id, 1));
// --- Prisma ---
await prisma.user.update({
where: { id: 1 },
data: { name: "田中次郎" },
});
トランザクション
// --- Drizzle ---
await db.transaction(async (tx) => {
const [user] = await tx
.insert(users)
.values({ name: "佐藤花子", email: "sato@example.com" })
.returning();
await tx
.insert(posts)
.values({ title: "初投稿", content: "こんにちは!", authorId: user.id });
});
// --- Prisma ---
await prisma.$transaction(async (tx) => {
const user = await tx.user.create({
data: { name: "佐藤花子", email: "sato@example.com" },
});
await tx.post.create({
data: { title: "初投稿", content: "こんにちは!", authorId: user.id },
});
});
5. どちらを選ぶべきか — ユースケース別推奨
Drizzle ORM を選ぶべきケース
| ユースケース | 理由 |
|---|---|
| Edge / サーバーレス環境 (Cloudflare Workers, Vercel Edge) | バイナリ不要・軽量で、コールドスタートが高速 |
| SQL に精通したチーム | SQL の知識がそのまま活きる。学習コストがほぼゼロ |
| 複雑な SQL を多用するプロジェクト | Window 関数、CTE、サブクエリなどを型安全に組み立てられる |
| バンドルサイズに制約がある | 数十 KB で収まるため、Lambda のパッケージサイズ制限にも余裕 |
| Turso / Neon / PlanetScale など新興 DB | ドライバレベルで直接対応しており、設定が簡単 |
| コード生成ステップを避けたい | prisma generate のようなビルドステップが不要 |
Prisma を選ぶべきケース
| ユースケース | 理由 |
|---|---|
| 大規模チーム・長期運用プロジェクト | マイグレーション履歴管理が堅牢で、スキーマの変更管理が安心 |
| SQL にあまり詳しくないメンバーがいる | 直感的な API で SQL を意識せずに開発でき |