Drizzle ORM を使い始めたら、SQL と TypeScript の型が自然につながった話
Prisma から Drizzle ORM に乗り換えたところ、SQL に近い直感的な書き心地と TypeScript の型推論精度の高さに驚きました。セットアップ手順と実際に使ってみた感想をまとめます。
🙌 結論から
Drizzle ORM は「SQL っぽい書き心地」と「TypeScript の型安全性」を両立してくれる ORM です!
スキーマを定義すると型が自動で付いてきて、クエリを書いていても「どんな型が返ってくるか」が一目でわかります(^^)
Prisma のような自動生成ステップに依存しない分、動きが予測しやすくて仕事でもかなり使いやすいです。
🤔 Drizzle ORM を選んだ理由
これまで Prisma を使っていたのですが、いくつかの点で不満が出てきました。
prisma generateを走らせないと型が更新されない- スキーマが Prisma 独自形式なので、SQL の知識と直接対応させにくい
- エッジ環境(Cloudflare Workers など)でのサポートに制約がある場面があった
Drizzle ORM はこのあたりがかなりスッキリしています。スキーマを TypeScript で直接書くので、生成ステップが不要です!
また、クエリの書き方が SQL に近いため、「どんな SQL が発行されるか」をイメージしながらコードを書けます(`・ω・´)
調べていくうちに「これは合いそう」と感じて、個人プロジェクトで試してみることにしました。
💡 基本的な使い方
drizzle-orm と、接続する DB に合わせたアダプターをインストールします(例: PostgreSQL の場合)。
npm install drizzle-orm postgres
npm install -D drizzle-kit
スキーマはこんな感じで書けます:
import { pgTable, serial, text, timestamp } from 'drizzle-orm/pg-core';
export const users = pgTable('users', {
id: serial('id').primaryKey(),
name: text('name').notNull(),
email: text('email').notNull().unique(),
createdAt: timestamp('created_at').defaultNow(),
});
クエリはこう書きます:
// 全件取得
const allUsers = await db.select().from(users);
// 条件付き取得
const user = await db.select().from(users).where(eq(users.id, 1));
SELECT の結果が typeof users.$inferSelect[] 型で返ってくるので、型アサーション不要です!
この「スキーマを書いたらすぐ型が使える」という感覚が、かなり気持ちよかったです(^o^)/
😊 実際に使ってみた感想
個人の Hono + Cloudflare Workers プロジェクトに導入してみました。
Prisma のときは「クエリを書いて → generate して → 型が更新されるのを待つ」という手順がありましたが、Drizzle ではスキーマを書いたら即座に型が使えます!
特によかったのは、エディタの補完がしっかり働く点です。
クエリを書いていて「where の条件にどのカラムが使えるか」「返ってくるオブジェクトのキーは何か」が補完で出てくるのは、かなり快適でした(^^)
マイグレーションも drizzle-kit で管理できて、drizzle-kit generate でマイグレーションファイルを生成 → drizzle-kit migrate で適用、という流れが直感的です。
気になった点は、Prisma に比べてリレーション周りの書き方に若干の慣れが必要な場面があったことです。
ただ、それ以上に「動作が予測しやすい」「生成ファイルへの依存がない」という安心感は相当大きかったです。
🔗 リレーショナルクエリでJOINを書かずに関連取得する
「リレーション周りに慣れが必要」と書いた部分、その後 リレーショナルクエリ(db.query) という書き方を知ってからかなり解消されました。
まず relations() でテーブル同士の関係を定義しておきます。
import { relations } from 'drizzle-orm';
import { users, posts } from './schema';
export const usersRelations = relations(users, ({ many }) => ({
posts: many(posts),
}));
これで db.query から、JOINを自分で書かずにネストした関連データを取得できるようになります。
const usersWithPosts = await db.query.users.findMany({
with: {
posts: true,
},
});
// 各 user オブジェクトに posts 配列がそのままぶら下がってくる!
with はネストもできるので、「投稿とそのコメント」のように2階層以上先まで一度に取得することも可能です。
以前 select().from(users).innerJoin(posts, ...) と書いていた部分が、with: { posts: true } の1行で済むようになったのはかなり快適でした(・∀・)
一点だけ注意点があって、リレーショナルクエリの中でカラムを参照するときは、users.id のようにインポートしたテーブルオブジェクトを直接使うのではなく、コールバック引数を経由して参照する必要があります。
const result = await db.query.posts.findMany({
where: (posts, { eq }) => eq(posts.published, true),
orderBy: (posts, { desc }) => [desc(posts.createdAt)],
});
最初はこの書き方に慣れず何度かエラーを出しましたが、慣れてしまえば「関連データを含めた読み取り専用のクエリはリレーショナルクエリ、書き込みや複雑な条件は通常のクエリビルダー」と使い分けるのがしっくりきています。
パパ系Webエンジニア〜愛用ガジェット一覧 🛍️
この記事を書いている作業環境のガジェットを楽天ROOMにまとめています ✨ 在宅エンジニアのデスク作りの参考にどうぞ 🙌
✅ まとめ
Drizzle ORM、SQL に慣れているエンジニアにはかなりハマる選択肢だと思います!
特に以下のような方には試してみる価値があります:
- Prisma の生成ステップが煩わしいと感じている
- エッジ環境(Cloudflare Workers など)で ORM を使いたい
- SQL に近い書き方が好みで、型安全も確保したい
Prisma に慣れている方も、一度触ってみると「こういうアプローチもあるんだ」と発見があると思います🙌
※ Drizzle ORMのAPIは今も活発に更新されています。最新情報は Drizzle公式ドキュメント をご確認ください👀