drizzle-orm vs prisma 徹底比較

drizzle-orm の詳細prisma の詳細
AI生成コンテンツ

この記事はAIによって生成されました。内容の正確性は保証されません。最新の情報は公式ドキュメントをご確認ください。

Drizzle ORM vs Prisma — 2024-2025年版 徹底比較

1. 結論

SQL に近い柔軟な制御と軽量さを求めるなら Drizzle ORM、スキーマ駆動の開発体験と成熟したエコシステムを重視するなら Prisma を選んでください。Drizzle は「TypeScript で SQL を書く」感覚に近く、Prisma は「スキーマファーストで型安全な抽象化」を提供します。プロジェクトの規模・チームの SQL 習熟度・デプロイ先(Edge 環境かどうか)によって最適解が変わります。


2. 比較表

観点Drizzle ORMPrisma
初回リリース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(デスクトップ / ブラウザ)
対応 DBPostgreSQL, MySQL, SQLite, Turso, Neon, PlanetScale 等PostgreSQL, MySQL, SQLite, SQL Server, MongoDB, CockroachDB 等
Raw SQLsql テンプレートリテラルで自在$queryRaw / $executeRaw
リレーション定義TypeScript コード内で relations().prisma スキーマ内で宣言
学習コストSQL を知っていれば低いPrisma スキーマ言語の習得が必要
N+1 対策リレーショナルクエリ API で JOIN / サブクエリ自動選択include / select で Eager Loading
コミュニティ・エコシステム急成長中、プラグインは少なめ成熟、Auth.js / tRPC 等との統合豊富
ライセンスApache 2.0Apache 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 ORMsrc/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],
  }),
}));

Prismaprisma/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 を意識せずに開発でき