Prisma vs Drizzle ORM——Cloudflareで使うならDrizzleが圧倒的
PrismaとDrizzle ORMは、どちらもTypeScriptの代表的なORM/クエリビルダーですが、設計思想はかなり異なります。Prismaはschema.prismaという独自のスキーマ定義ファイルからクライアントコードを生成する「コード生成型」のORMで、型の効いたAPI・成熟したマイグレーション機能・Prisma StudioというGUIツールまで揃った、DXの完成度の高さが特徴です。Drizzle ORMはTypeScriptの関数呼び出しだけでスキーマを定義し、そのままSQLに近い形でクエリを組み立てる「SQLライクな」クエリビルダーで、独自のランタイムをほぼ持たず、素のSQLに近い書き味を保っています。
一般的なNode.jsサーバーで使う分には、この違いはDX(開発体験)の好みの範囲に収まります。Prismaのほうがオートコンプリートやマイグレーション周りが手厚い一方、Drizzleのほうが軽量で挙動を把握しやすい、という程度の話です。ところがCloudflare Workers上で使う場合、この差は「好み」では済まなくなります。結論から言うと、AlcogyではCloudflare上のプロジェクトはすべてDrizzle ORMを採用しており、Prismaは使っていません。
Cloudflare WorkersはCPU時間で制約される
Cloudflare WorkersはV8のisolateという軽量な実行環境の上で動いており、Node.jsのようにネイティブバイナリや子プロセスを起動することができません。加えて、Workersの実行時間の制約は「リクエストが終わるまでの時間」ではなく「実際にCPUを使った時間」で計測・制限されます。fetchの待ち時間のようなI/O待ちはこの時間に含まれませんが、JavaScript(やWASM)の実行そのものに使った時間はすべてカウントされます。つまり、リクエストごとに重い処理をisolate内で実行するORMほど、この制約の影響を受けやすいということです。
Prismaがつまずくポイント
Prismaは長らく、Rustで書かれたクエリエンジンをネイティブバイナリとして子プロセスで起動し、クライアントとやり取りする構成を取っていました。これはWorkersのようにネイティブバイナリや子プロセスを扱えない実行環境とは根本的に相性が悪く、そのままでは動かせません。
現在はDriver Adaptersという仕組みが用意され、ネイティブバイナリではなくWASMにコンパイルされたクエリエンジンを使うことで、Workers上でもPrisma Clientを動かせるようになっています。
// Prisma + D1 driver adapter の例
import { PrismaD1 } from '@prisma/adapter-d1';
import { PrismaClient } from '@prisma/client';
export function getPrismaClient(db: D1Database) {
const adapter = new PrismaD1(db);
return new PrismaClient({ adapter });
}動くには動くのですが、根本的な構造は変わっていません。リクエストのたびに(あるいはisolateの初期化のたびに)WASMのクエリエンジンをロードし、そこでクエリの解析・SQLへの変換処理を行うという層が挟まる分、素のSQLを投げる場合に比べてCPU時間を余分に消費します。加えてWASMバイナリ自体がスクリプトのバンドルサイズを押し上げるため、Workersのようにサイズやリソースにシビアな環境では、この余分なオーバーヘッドがそのままレイテンシやコストに跳ね返ってきます。
DrizzleがCPU時間に効く理由
Drizzle ORMには、Prismaのような別建てのクエリエンジンが存在しません。スキーマもクエリもただのTypeScriptのコードであり、db.select().from(orders).where(...)のような呼び出しは、実行時にSQL文字列とバインドパラメータを組み立てて、Cloudflare D1のbindingにそのまま渡すだけです。
// Drizzle + D1 の例
import { drizzle } from 'drizzle-orm/d1';
import { eq } from 'drizzle-orm';
import { orders } from './schema';
export function getOrder(db: D1Database, id: string) {
const orm = drizzle(db);
return orm.select().from(orders).where(eq(orders.id, id)).get();
}このdrizzle-orm/d1は薄いラッパーで、D1のprepare().bind().all()のようなネイティブAPIとほぼ同じ経路でクエリが実行されます。WASMエンジンの初期化やクエリ解析といった余分な層がないため、Workers上でのCPU時間の消費がPrismaに比べて小さく抑えられます。バンドルサイズも軽量で、isolateの起動やコールドスタートへの影響も少なく済みます。
結論: Cloudflareで使うならDrizzle一択
一般的な環境であれば「Prismaの手厚いDXを取るか、Drizzleの軽量さを取るか」というトレードオフの話で済みますが、Cloudflare Workers/D1という実行環境に限って言えば、CPU時間という明確な制約軸がある以上、この判断はほぼ一方向に倒れます。AlcogyがCloudflare上のプロジェクトで一貫してDrizzle ORMを使っているのも、この制約を踏まえた上での判断です。Prismaの機能が必要な場面であっても、Workers上で動かす部分に関しては、素のSQLに近い形で完結するDrizzleのほうが理にかなっていると考えています。