Prisma vs Drizzle ORM徹底比較 — 2026年どちらを選ぶべきか


TypeScriptエコシステムにおいて、Prisma と Drizzle ORM は最も人気のあるORMです。この記事では、両者の特徴を徹底比較し、プロジェクトに最適な選択をサポートします。

概要と哲学の違い

Prisma

  • スキーマファースト: 独自のスキーマ言語(PSL)でデータモデルを定義
  • 抽象化重視: SQLを隠蔽し、高レベルなAPIを提供
  • ツールチェーン: Prisma Studio、マイグレーション、シード機能を統合
  • 成熟度: 2019年リリース、大規模プロダクションでの実績多数

Drizzle ORM

  • TypeScriptファースト: スキーマをTypeScriptで直接定義
  • SQL親和性: 生SQLに近いクエリビルダー
  • 軽量・高速: バンドルサイズが小さく、エッジランタイム対応
  • 成熟度: 2022年リリース、急速に人気上昇中

スキーマ定義の比較

Prisma Schema Language

// schema.prisma
datasource db {
  provider = "postgresql"
  url      = env("DATABASE_URL")
}

generator client {
  provider = "prisma-client-js"
}

model User {
  id        String   @id @default(uuid())
  email     String   @unique
  name      String?
  posts     Post[]
  createdAt DateTime @default(now())
  updatedAt DateTime @updatedAt
}

model Post {
  id        String   @id @default(uuid())
  title     String
  content   String?
  published Boolean  @default(false)
  author    User     @relation(fields: [authorId], references: [id])
  authorId  String
  tags      Tag[]
  createdAt DateTime @default(now())
}

model Tag {
  id    String @id @default(uuid())
  name  String @unique
  posts Post[]
}

利点:

  • 直感的でわかりやすい
  • リレーションが視覚的に明確
  • IDEサポートが充実

欠点:

  • 独自言語の学習コスト
  • TypeScriptコードと分離

Drizzle Schema (TypeScript)

// schema.ts
import { pgTable, uuid, varchar, text, boolean, timestamp, pgEnum } from 'drizzle-orm/pg-core';
import { relations } from 'drizzle-orm';

export const users = pgTable('users', {
  id: uuid('id').primaryKey().defaultRandom(),
  email: varchar('email', { length: 255 }).notNull().unique(),
  name: varchar('name', { length: 255 }),
  createdAt: timestamp('created_at').notNull().defaultNow(),
  updatedAt: timestamp('updated_at').notNull().defaultNow(),
});

export const posts = pgTable('posts', {
  id: uuid('id').primaryKey().defaultRandom(),
  title: varchar('title', { length: 255 }).notNull(),
  content: text('content'),
  published: boolean('published').notNull().default(false),
  authorId: uuid('author_id').notNull().references(() => users.id),
  createdAt: timestamp('created_at').notNull().defaultNow(),
});

export const tags = pgTable('tags', {
  id: uuid('id').primaryKey().defaultRandom(),
  name: varchar('name', { length: 100 }).notNull().unique(),
});

// リレーション定義
export const usersRelations = relations(users, ({ many }) => ({
  posts: many(posts),
}));

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

利点:

  • TypeScriptネイティブ、追加言語不要
  • 型安全性が高い
  • コード内で完結

欠点:

  • やや冗長
  • 初見ではわかりにくい

クエリAPIの比較

Prisma: 高レベルな抽象化

import { PrismaClient } from '@prisma/client';

const prisma = new PrismaClient();

// 基本的なCRUD
const user = await prisma.user.create({
  data: {
    email: 'user@example.com',
    name: 'John Doe',
  },
});

// リレーションを含む取得
const userWithPosts = await prisma.user.findUnique({
  where: { id: userId },
  include: {
    posts: {
      where: { published: true },
      orderBy: { createdAt: 'desc' },
      take: 10,
    },
  },
});

// トランザクション
await prisma.$transaction([
  prisma.user.update({ where: { id: 1 }, data: { name: 'Updated' } }),
  prisma.post.create({ data: { title: 'New Post', authorId: 1 } }),
]);

// 集約クエリ
const stats = await prisma.post.aggregate({
  _count: { id: true },
  _avg: { viewCount: true },
  where: { published: true },
});

// Raw SQL(エスケープ処理)
const result = await prisma.$queryRaw`
  SELECT * FROM users WHERE email = ${email}
`;

Drizzle: SQL親和性の高いAPI

import { drizzle } from 'drizzle-orm/node-postgres';
import { eq, desc, and, count, avg } from 'drizzle-orm';
import { users, posts } from './schema';

const db = drizzle(pool);

// 基本的なCRUD
const user = await db.insert(users).values({
  email: 'user@example.com',
  name: 'John Doe',
}).returning();

// リレーションを含む取得
const userWithPosts = await db.query.users.findFirst({
  where: eq(users.id, userId),
  with: {
    posts: {
      where: eq(posts.published, true),
      orderBy: desc(posts.createdAt),
      limit: 10,
    },
  },
});

// トランザクション
await db.transaction(async (tx) => {
  await tx.update(users).set({ name: 'Updated' }).where(eq(users.id, 1));
  await tx.insert(posts).values({ title: 'New Post', authorId: 1 });
});

// 集約クエリ
const [stats] = await db
  .select({
    count: count(posts.id),
    avgViews: avg(posts.viewCount),
  })
  .from(posts)
  .where(eq(posts.published, true));

// Raw SQL
const result = await db.execute(sql`
  SELECT * FROM users WHERE email = ${email}
`);

マイグレーション

Prisma Migrate

# 開発用マイグレーション生成
npx prisma migrate dev --name add_user_roles

# 本番適用
npx prisma migrate deploy

# リセット(開発環境のみ)
npx prisma migrate reset

特徴:

  • スキーマからマイグレーションを自動生成
  • マイグレーション履歴を自動管理
  • ロールバックは手動編集が必要

Drizzle Kit

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

# マイグレーション適用
npx drizzle-kit push:pg

# Studioでスキーマ確認
npx drizzle-kit studio
// drizzle.config.ts
import type { Config } from 'drizzle-kit';

export default {
  schema: './src/schema.ts',
  out: './drizzle/migrations',
  driver: 'pg',
  dbCredentials: {
    connectionString: process.env.DATABASE_URL!,
  },
} satisfies Config;

特徴:

  • TypeScriptスキーマからSQL生成
  • Push(直接適用)とGenerate(マイグレーションファイル生成)の両対応
  • 柔軟なカスタマイズが可能

パフォーマンス比較

バンドルサイズ

ORMバンドルサイズ備考
Prisma Client~500KBクライアント生成が必要
Drizzle ORM~50KB軽量で高速

クエリパフォーマンス

ベンチマーク結果(1000件のクエリ実行):

// Prisma
console.time('Prisma');
for (let i = 0; i < 1000; i++) {
  await prisma.user.findMany({ take: 10 });
}
console.timeEnd('Prisma'); // ~2500ms

// Drizzle
console.time('Drizzle');
for (let i = 0; i < 1000; i++) {
  await db.select().from(users).limit(10);
}
console.timeEnd('Drizzle'); // ~1800ms

Drizzleの方が約30%高速(環境により変動)。

エッジランタイム対応

Prisma

// Cloudflare Workers での制限
import { PrismaClient } from '@prisma/client/edge';
import { withAccelerate } from '@prisma/extension-accelerate';

const prisma = new PrismaClient().$extends(withAccelerate());
// Prisma Accelerate(有料サービス)が必要

Drizzle

// Cloudflare Workers でネイティブ対応
import { drizzle } from 'drizzle-orm/d1';

export default {
  async fetch(request: Request, env: Env) {
    const db = drizzle(env.DB);
    const users = await db.select().from(usersTable);
    return Response.json(users);
  },
};

エッジ対応:

  • Drizzleはエッジランタイムをネイティブサポート
  • PrismaはAccelerate(有料)が必要

DX(開発者体験)

Prisma

利点:

  • Prisma Studioでデータを視覚的に確認
  • マイグレーション管理が簡単
  • ドキュメントが充実
  • エコシステムが成熟

欠点:

  • 生成されたクライアントコードが大きい
  • スキーマ変更後に再生成が必要
  • カスタムクエリの自由度が低い

Drizzle

利点:

  • TypeScriptコード内で完結
  • SQLに近い柔軟性
  • バンドルサイズが小さい
  • エッジランタイム対応

欠点:

  • ツールチェーンがまだ発展途上
  • ドキュメントがPrismaほど充実していない
  • 学習曲線がやや急

どちらを選ぶべきか

Prismaを選ぶべき場合

  • フルスタックアプリケーション: Next.js, Remix等のサーバーレンダリング
  • チーム開発: 統一されたスキーマ定義が重要
  • データモデル重視: リレーションが複雑なアプリ
  • 成熟したエコシステム: 安定性を優先
  • Prisma Studioを活用: GUI管理ツールが欲しい

Drizzleを選ぶべき場合

  • エッジデプロイ: Cloudflare Workers, Vercel Edge Functions
  • パフォーマンス重視: バンドルサイズやクエリ速度が重要
  • SQL親和性: 生SQLに近い柔軟性が欲しい
  • TypeScriptネイティブ: スキーマもTypeScriptで管理したい
  • 軽量アプリケーション: サーバーレス関数等

ハイブリッドアプローチ

// 複雑なクエリにはDrizzle、管理系にはPrisma
import { PrismaClient } from '@prisma/client';
import { drizzle } from 'drizzle-orm/node-postgres';

const prisma = new PrismaClient();
const db = drizzle(pool);

// 管理画面はPrisma
async function adminQuery() {
  return prisma.user.findMany({ include: { posts: true } });
}

// パフォーマンス重視のAPIはDrizzle
async function fastQuery() {
  return db.select().from(users).limit(100);
}

まとめ

項目PrismaDrizzle
スキーマ定義PSL(独自言語)TypeScript
バンドルサイズ大きい (~500KB)小さい (~50KB)
パフォーマンス良好優秀
エッジ対応限定的(有料)ネイティブ対応
DX優秀良好
エコシステム成熟発展中
学習曲線緩やかやや急

2026年の推奨:

  • 新規プロジェクト: Drizzle ORM(特にエッジ環境)
  • 既存Prismaプロジェクト: そのまま継続でOK
  • 大規模チーム: Prismaの統一性が有利
  • パフォーマンス最優先: Drizzle

どちらも優れたORMであり、プロジェクトの要件に応じて選択しましょう。Drizzleは急速に進化しており、2026年はさらに注目度が高まると予想されます。