TypeScriptの実プロジェクトで、なぜ私がPrismaではなくDrizzle ORMを選んだのか?

Development tutorial - IT technology blog
Development tutorial - IT technology blog

なぜPrismaから移行することを決めたのか?

SaaSの実プロジェクトにDrizzle ORMを導入して半年以上が経ち、一つの教訓を得ました。「シンプルさこそが至高である」ということです。以前、私たちのチームは利便性の高さからPrismaを非常に好んで使っていました。しかし、プロジェクトが拡大するにつれ、いくつかの問題が浮き彫りになりました。 VercelでのCold Startは、Prismaのバイナリファイルが重すぎるために2秒を超えることもありました。さらに、1,500行にも及ぶschema.prismaファイルは、Gitでの変更履歴の追跡を困難にさせました。

Drizzle ORMは、まさに私が求めていたものを解決してくれました。これは新しい抽象言語を作るのではなく、純粋なSQLの構文に近い形で記述でき、かつ100%のType-safeを保証します。最大の利点は、非常に軽量であることです。バイナリもオーバーヘッドもなく、クエリ速度はネイティブのドライバを使用するのとほぼ同等です。

Drizzle ORMの3つの柱

このツールを使いこなすには、次の3つのコンポーネントの仕組みを理解するだけで十分です:

  • Drizzle ORM: クエリを記述するためのコアライブラリ. スマートで柔軟なクエリビルダーとして機能します。
  • Drizzle Kit: マイグレーションを管理するCLIツール。.tsコードと現在のデータベースを比較し、SQLファイルを自動生成します。
  • Database Driver: Drizzleはデータベースに直接接続しません。postgres.jsmysql2などの既存のドライバを活用してパフォーマンスを最適化します。

Prismaのアプローチとは異なり、DrizzleはTypeScriptそのものでスキーマを定義します。これにより、変数や関数、プログラミングロジックを駆使して、スキーマを管理しやすい複数のモジュールに分割することが可能になります。

導入ガイド:インストールから最初のクエリまで

以下は、私が実際のプロジェクトでよく使用するPostgreSQLのセットアップ手順です。時間を節約するためにプロセスを簡略化しています。

ステップ1:初期化とインストール

必要なパッケージのインストールから始めましょう。古いpgライブラリよりもパフォーマンスに優れているpostgres.jsを選択します。

mkdir drizzle-demo && cd drizzle-demo
npm init -y
npm install drizzle-orm postgres
npm install -D typescript drizzle-kit @types/node
npx tsc --init

ステップ2:TypeScriptでスキーマを定義する

src/db/schema.tsファイルを作成します。IDEが即座にコード補完を行ってくれるため、スキーマの記述は非常に快適です。別途プラグインをインストールする必要もありません。

import { pgTable, serial, text, varchar, timestamp } from "drizzle-orm/pg-core";

export const users = pgTable("users", {
  id: serial("id").primaryKey(),
  fullName: text("full_name").notNull(),
  email: varchar("email", { length: 255 }).notNull().unique(),
  createdAt: timestamp("created_at").defaultNow(),
});

export const posts = pgTable("posts", {
  id: serial("id").primaryKey(),
  title: text("title").notNull(),
  content: text("content").notNull(),
  authorId: serial("author_id").references(() => users.id),
});

ここでの利点はモジュール化です。各テーブルを別々のファイルに分割してインポートすることができ、数十のテーブルを持つプロジェクトでも非常にクリーンに保てます。

ステップ3:マイグレーションの設定

ルートディレクトリにdrizzle.config.tsファイルを作成します。このファイルは、Drizzle Kitがスキーマを見つけ、変更履歴を保存する場所を指定します。

import { defineConfig } from "drizzle-kit";

export default defineConfig({
  schema: "./src/db/schema.ts",
  out: "./drizzle",
  dialect: "postgresql",
  dbCredentials: {
    url: process.env.DATABASE_URL || "postgresql://user:pass@localhost:5432/db",
  },
});

マイグレーションファイルを生成するには、次のコマンドを実行します:

npx drizzle-kit generate

Drizzleは純粋なSQLファイルを生成します。これを開いて直接確認したり修正したりすることが可能です。これは、Prismaが内部で管理する方法よりもはるかに透明性が高いです。

ステップ4:CRUDの実行

src/index.tsファイルで最初のクエリを試してみましょう。手動でgenerateコマンドを実行しなくても、すべてがType-safeな状態になっています。

import { drizzle } from "drizzle-orm/postgres-js";
import postgres from "postgres";
import { users } from "./db/schema";
import { eq } from "drizzle-orm";

const queryClient = postgres("postgresql://user:pass@localhost:5432/db");
const db = drizzle(queryClient);

async function run() {
  // 新しいデータを追加
  await db.insert(users).values({
    fullName: "田中 太郎",
    email: "[email protected]",
  });

  // TSの補完をフル活用してデータを取得
  const result = await db.select().from(users);
  console.log(result);
}

run();

本番環境で6ヶ月運用して得られた教訓

Drizzleを導入したことで、チームの作業効率は大幅に向上しました。特に強調したい実務上のポイントが3つあります:

  1. バンドルサイズが非常に軽量: Lambdaにデプロイする際のアプリケーション容量が約15MB削減されました。Cold startは2秒から300ミリ秒以下に短縮されました。
  2. デバッグが容易: クエリが遅い場合、.toSQL()関数を使うだけで実際のSQL文が出力されます。これをそのままExplain Analyzeにかけて最適化に役立てることができます。
  3. 柔軟だが注意も必要: Drizzle Kitのカラム名変更(rename column)の処理は、時としてPrismaほどスマートではない場合があります。そのような時は、データの安全性を確保するためにマイグレーションSQLファイルを手動で修正しています。

おわりに

Drizzle ORMは、Prismaを完全に置き換える「銀の弾丸」ではありません。絶対的な安定性と巨大なコミュニティを優先するなら、依然としてPrismaは優れた選択肢です。しかし、スピード、軽量さ、そしてSQLを深く制御したいのであれば、ぜひDrizzleを試してみてください。まずは小さなマイクロサービスや個人プロジェクトから始めて、その違いを実感してから本格的に導入することをお勧めします。

Share: