Drizzle ORMは、TypeScriptでテーブルを定義し、SQLとほぼ同じ形のクエリを型安全に書けるORMです。読み方は「ドリズル」。2026年10月23日時点のnpmでは、latestタグが0.45.4(2026年10月8日公開)、rcタグが1.0.0-rc.5(2026年10月21日公開)で、安定版とv1候補版が並行しています。
本記事では、0.45.4と1.0.0-rc.4・rc.5をPGlite(WASM版PostgreSQL)で実際に動かし、インストールからCRUD・JOIN・シード投入、drizzle-kitによるマイグレーション、チーム開発でのマイグレーション衝突、v1への移行、Prismaとの選び分けまでをまとめます。基本操作のコードは0.45.4とPGlite用ドライバで検証し、掲載時に接続部分をnode-postgres向けへ変更しています。
まとめ:Drizzle ORMの要点と2026年10月時点の版
- Drizzle ORMはTypeScriptで書かれたORMで(JavaScriptからも使えます)、
select().from().where()のようにSQLの語順で書け、戻り値の型がスキーマから推論されます。drizzle-ormパッケージのランタイム依存(dependencies)は0件で、公式READMEはminify+gzip後の大きさを約7.4KBとしています。 - 本番で使う安定版は0.45.4(drizzle-kitは0.31.11)です。1.0はRC段階で、rc.5でもリレーショナルクエリの絞り込みやエラーの中身に破壊的変更が入りました。v1を追うなら、RCを上げるたびに挙動を確かめる前提で使います。
- マイグレーションは「
drizzle-kit generateでSQLを生成し、migrateで適用」が基本です。pushは履歴を残さずDBへ直接反映する方式で、ダウンマイグレーション(自動ロールバック)の仕組みはありません。 - 複数ブランチで
generateすると、0.31.11では_journal.jsonとスナップショットがGitで衝突し、両方を残すと別々の列を足しただけでもcheckが止まります。v1 RCのcheckは衝突する変更(同じ列の追加など)だけを終了コード1で止め、rc.5ではgenerate --fix-conflictsで作り直せます。 - Prismaとの差は、スキーマをTypeScriptで書くか専用DSLで書くか、SQLをどこまで見せるかです。2026年10月時点ではnpmの
prismaのlatestがPrisma ORM 8のRCになっており、Prisma 7で使うならprisma@7の固定が要ります。
Drizzle ORMの定義と読み方・現行バージョン
読み方「ドリズル」と名前の意味
drizzleは英語で「霧雨」を意味し、発音はカタカナで「ドリズル」です。開発元はDrizzle Team、ライセンスはApache-2.0で、GitHubのdrizzle-team/drizzle-ormはスター数約3.6万(2026年10月23日時点で36,116)です。公式サイトはorm.drizzle.teamで、ドキュメントもここにあります。
ORM(Object-Relational Mapping)は、データベースのテーブルとプログラムのオブジェクトを対応づけ、SQLを直接書かずに読み書きできるようにする仕組みです。Drizzleはその中でも抽象化を薄くとる設計で、発行されるSQLをtoSQL()でそのまま確認できます。ORMそのものの仕組みやN+1問題はORMとは?O/Rマッパーの仕組み・メリットとデメリットをSQL比較と実行結果で解説で扱っています。
安定版0.45系とv1.0 RCの位置づけ
| 項目 | 0.45系(安定版) | 1.0 RC |
|---|---|---|
| npmタグ | latest=0.45.4 | rc=1.0.0-rc.5 |
| 公開日 | 2026年10月8日 | 2026年10月21日 |
| drizzle-kit | 0.31.11 | 1.0.0-rc.5 |
| リレーション定義 | relations() | defineRelations() |
| マイグレーション出力 | 連番SQL+meta/_journal.json | タイムスタンプ付きフォルダ |
| ブランチ間の衝突検知 | スナップショットの分岐 | 変更内容の衝突 |
| バリデータ連携 | drizzle-zod等の別パッケージ | drizzle-orm/zod等を同梱 |
0.45.4の変更は、postgres.jsドライバで.prepare(name)を明示しても準備済みクエリが使われなかった不具合の修正1件です(0.45.4のリリースノート)。インストールコマンドにタグを付けなければ0.45系が入ります。v1を試すならnpm i drizzle-orm@rc drizzle-kit@rcのように明示します。業務システムでは、RCが外れるまで0.45系を使い、v1の差分は検証用ブランチで確かめる進め方が安全です。
対応データベースとドライバ
0.45.4のパッケージにはPostgreSQL・MySQL・SQLite・SingleStore・Gelのコア(pg-coreなど)が入っています。1.0 RCではMSSQL(SQL Server、mssqlドライバ)とCockroachDBが加わりました。一方、1.0.0-rc.5のnpmパッケージのexportsにはgel関連のエントリが1件も無く、0.45.4には./gel/driverがあります。Gelを使うなら0.45系のままにします。
ドライバはnode-postgres・postgres.js・Neon・Supabase・PlanetScale・Turso(libSQL)・Cloudflare D1・Bun SQLiteなどに対応し、0.45.3ではNetlify DBドライバが追加されました。Tursoと組み合わせる場合の料金と構成はTursoとは?料金・使い方とlibSQL/Turso Database(Rust)の違いを実測解説【2026年10月】にあります。Pythonなど他言語向けの版は無いため、「drizzle python」で探しているならSQLAlchemyのようなPython向けORMが代わりになります。
インストールからdrizzle.config.tsまでの導入手順
Drizzle ORM・KitとPostgreSQLドライバのインストール
PostgreSQLをnode-postgresで使う場合の構成です。drizzle-ormは本体、drizzle-kitはマイグレーション用CLIなので開発依存に入れます。
npm i [email protected] pg
npm i -D [email protected] @types/pg
バージョンを指定しないと、v1がlatestになった時点でv1が入り、本記事のrelations()の書き方が動かなくなります。接続先はDATABASE_URLのような環境変数で渡し、接続文字列をコードに書かないようにします。Supabaseと組み合わせる場合の接続文字列の選び方は、SupabaseにPrismaとDrizzleをつなぐ:接続文字列とマイグレーション二重管理の設計で扱っています。
スキーマ定義(テーブル・外部キー・インデックス)
テーブルはTypeScriptの関数で定義します。以下はusersとpostsの1対多で、postsのauthor_idに外部キーとインデックスを付けています。
// src/schema.ts
import { pgTable, serial, text, integer, timestamp, index } 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(),
authorId: integer('author_id').notNull().references(() => users.id, { onDelete: 'cascade' }),
}, (t) => [index('posts_author_idx').on(t.authorId)]);
// リレーショナルクエリ(with)用の関係定義。0.45系の書き方
export const usersRelations = relations(users, ({ many }) => ({ posts: many(posts) }));
export const postsRelations = relations(posts, ({ one }) => ({
author: one(users, { fields: [posts.authorId], references: [users.id] }),
}));
references()はDB上の外部キー制約を作り、relations()はクエリ時の関連づけだけを定義します。withによる関連取得に必要なのはrelations()で、外部キー制約のreferences()は必須ではありません。参照整合性をDBで守りたいならreferences()も付けます。
drizzle.config.tsの必須項目
// drizzle.config.ts
import { defineConfig } from 'drizzle-kit';
export default defineConfig({
dialect: 'postgresql',
schema: './src/schema.ts',
out: './drizzle',
dbCredentials: { url: process.env.DATABASE_URL! },
});
公式ドキュメントが最低限必要とするのはdialectとschemaの2つで、outは省略するとdrizzleフォルダになります。dialectは0.31.11ではpostgresql・mysql・sqlite・turso・singlestore・gelから選びます。dbCredentialsはmigrate・push・pull・studioが使う接続情報で、generateとcheckはDBに接続しません。
基本操作:CRUD・JOIN・リレーショナルクエリ・シード投入
usersテーブルのCRUD(作成・取得・更新・削除)と条件指定
実行前にPostgreSQLのデータベースを用意してDATABASE_URLを設定し、npx drizzle-kit generate、npx drizzle-kit migrateの順に実行してテーブルを作成しておきます。以下は空のテーブルで実行する例です。TypeScriptの実行環境が無ければnpm i -D tsx typescriptで追加し、package.jsonに"type": "module"を設定したうえで、コードを保存したファイルをnpx tsx src/crud.tsのように実行します。
import { drizzle } from 'drizzle-orm/node-postgres';
import { eq } from 'drizzle-orm';
import * as schema from './schema';
import { users } from './schema';
const db = drizzle({ connection: process.env.DATABASE_URL!, schema });
// Create:returning() で挿入後の行を受け取る(PostgreSQL・SQLite)
const [alice] = await db.insert(users).values({ name: 'Alice', email: '[email protected]' }).returning();
// Read:選んだ列だけが型に残る
const rows = await db.select({ id: users.id, name: users.name }).from(users).where(eq(users.email, '[email protected]'));
// Update:where を必ず付ける
await db.update(users).set({ name: 'Alice2' }).where(eq(users.id, alice.id));
// Delete
await db.delete(users).where(eq(users.id, alice.id));
0.45.4とPGliteで実行すると、rowsは[ { id: 1, name: 'Alice' } ]でした。postsの外部キーにonDelete: 'cascade'を付けているため、usersの行を消すとその人のpostsも消えます。
JOINと集計のSQL確認
import { count, desc } from 'drizzle-orm';
import { posts } from './schema';
// CRUD例の後、JOIN用のデータを登録する
const [a] = await db.insert(users).values({ name: 'Alice', email: '[email protected]' }).returning();
await db.insert(users).values({ name: 'Bob', email: '[email protected]' });
await db.insert(posts).values([{ title: 'p1', authorId: a.id }, { title: 'p2', authorId: a.id }]);
const q = db
.select({ name: users.name, n: count(posts.id) })
.from(users)
.leftJoin(posts, eq(posts.authorId, users.id))
.groupBy(users.id)
.orderBy(desc(count(posts.id)));
console.log(q.toSQL().sql);
const result = await q; // [ { name: 'Alice', n: 2 }, { name: 'Bob', n: 0 } ]
toSQL()が返したSQLはselect "users"."name", count("posts"."id") from "users" left join "posts" on "posts"."author_id" = "users"."id" group by "users"."id" order by count("posts"."id") descで、書いたメソッドチェーンと1対1で対応します。innerJoin・rightJoin・fullJoinも同じ形で書けます。
withによるリレーショナルクエリ
const list = await db.query.users.findMany({
columns: { name: true },
with: { posts: { columns: { title: true } } },
});
// [{"name":"Alice","posts":[{"title":"p1"},{"title":"p2"}]},{"name":"Bob","posts":[]}]
JOINの例で登録したデータに対する結果です。ネストしたJSONをそのまま返したいときはdb.queryを使います。drizzle()にschemaを渡していないとdb.query.usersが型に現れません。
drizzle-seedによるテストデータ投入と件数指定
シード投入には公式の別パッケージdrizzle-seed(latest 0.3.1、peerDependenciesはdrizzle-orm >=0.36.4)を使います。実行前にnpm i -D [email protected]で追加します。スキーマを渡すと、列の型と外部キーをたどってデータを作ります。
import { seed, reset } from 'drizzle-seed';
// db・users・postsは前のCRUD・JOIN例で定義・importしたものを使う
await reset(db, { users, posts }); // 対象テーブルを空にする
await seed(db, { users, posts }, { count: 5 }); // 各テーブル5行
// 件数を親子で指定する:ユーザー3人に投稿4件ずつ
await reset(db, { users, posts });
await seed(db, { users, posts }).refine(() => ({
users: { count: 3, with: { posts: 4 } },
}));
0.45.4で実行すると、最初のseedでusers・postsが5行ずつ、refineの例でusers 3行・posts 12行が入りました。resetしてから同じseedを2回流すと、名前もメールアドレスも1回目と同じ値でした。乱数の種が既定で固定されているためで、テストの期待値を固定できます。データを変えたいときは{ count: 5, seed: 42 }のようにseedオプションを渡します。
drizzle-kitのコマンドとマイグレーション運用
generate・migrate・push・pull・check・studioの役割
| コマンド | 処理 | DB接続 |
|---|---|---|
| generate | スキーマ差分からSQLを生成 | 不要 |
| migrate | 未適用のSQLを適用 | 必要 |
| push | スキーマをDBへ直接反映 | 必要 |
| pull | 既存DBからスキーマを生成 | 必要 |
| check | マイグレーション履歴の整合を検査 | 不要 |
| studio | ブラウザでデータを閲覧・編集 | 必要 |
| up | 古い形式のスナップショットを更新 | 不要 |
0.31.11の--helpでは、既存DBからの取り込みがintrospect、履歴の削除がdropという名前で残っています。1.0.0-rc.5の--helpではintrospectがpullに統一され、dropは一覧に無く、export・skills・mcpが並びます。
本番のgenerate・migrateと試作のpushの使い分け
最初のスキーマでnpx drizzle-kit generateを実行すると、0.31.11ではdrizzle/0000_<ランダム名>.sqlとdrizzle/meta/_journal.jsonなどが生成されました。SQLには--> statement-breakpointの区切りが入り、外部キーはALTER TABLE ... ADD CONSTRAINTで後から付けられます。
生成されたSQLはレビューしてからコミットし、デプロイ時にdrizzle-kit migrate、またはアプリ側でmigrate(db, { migrationsFolder: './drizzle' })を呼んで適用します。公式ドキュメントはpushを「rapid prototyping」に最適な方法と説明しつつ、本番でも主要なマイグレーション手段として使うチームがあると紹介しています。それでもpushは履歴を残さずにDBを書き換えるので、複数環境で同じ変更を再現する必要がある本番DBには使わないのが無難です。
ロールバック(down migration)が無い前提での戻し方
drizzle-kitにはdownマイグレーションを生成・適用する機能がありません。GitHubの機能要望「Reverse/Down Migrations」(issue #4005、2025年1月起票)は2026年10月23日時点でもOpenです。戻すときは、スキーマを元の形に直してgenerateを再実行し、逆向きのSQLを新しいマイグレーションとして積みます。削除した列のデータは戻らないため、列削除を含む変更の前にはバックアップを取ります。
Supabase CLIのdb diffでマイグレーションを管理している場合は、どちらを正とするかを先に決めます。両方で履歴を持つと二重管理になります(Supabase CLIとローカル開発環境:db diffで進めるマイグレーションとCI)。
チーム開発のマイグレーション衝突:0.31.11とv1 RCを実行して比較
2人が別々のブランチでスキーマを変え、それぞれgenerateしてからmainへマージする場面を、Gitの一時リポジトリで再現しました。ブランチAがnickname列、ブランチBがwebsite列を追加するケースと、両ブランチが同じnickname列をtext型とinteger型で別々に追加するケースの2通りです。
| 確認項目 | 0.31.11 | 1.0.0-rc.4 | 1.0.0-rc.5 |
|---|---|---|---|
| 別々の列:履歴ファイルのGit衝突 | _journal.json等 | なし | なし |
| 別々の列:両方残してcheck | 終了コード1 | 通る | 通る |
| 同じ列:両方残してcheck | 終了コード1 | 終了コード1 | 終了コード1 |
| 片方の履歴だけ採用してcheck | Everything's fine |
- | - |
| 衝突の自動解消 | なし | なし | --fix-conflicts |
| generate忘れ:check | Everything's fine |
未検証 | 警告(終了コード0) |
0.31.11:_journal.jsonの衝突とスナップショット分岐の検知
0.31.11では、2本のブランチがどちらも0001_<ランダム名>.sqlとmeta/0001_snapshot.jsonを作るため、マージ時に_journal.jsonと0001_snapshot.jsonがGitの衝突になりました。手早く片方の_journal.jsonとスナップショットを採用して解決すると、checkはEverything's fineを返しますが、もう片方のSQLファイルは履歴に載らないまま残り、その後のgenerateは同じ列を追加する0002を新たに作りました。先にブランチAを検証環境へ適用していた場合、この0002は列の重複で失敗します。
一方、片方を0002へ改名して両方のSQLとスナップショットを残すと、checkは[drizzle/meta/0001_snapshot.json, drizzle/meta/0002_snapshot.json] are pointing to a parent snapshot: ... which is a collision.で終了コード1になりました。同じ親から分岐したこと自体を検知するため、別々の列を追加しただけの衝突しない変更でも止まります。0.45系で運用するなら、マイグレーションを含むブランチは1本ずつマージし、後からマージする側は未適用の自分のマイグレーションを捨て、mainを取り込んでからgenerateし直します。共有環境へ適用済みのマイグレーションは削除せず、修正用のマイグレーションを追加して調整します。
v1 RC:checkによる衝突検知とfix-conflictsでの作り直し
v1の形式はマイグレーションごとにタイムスタンプ付きのフォルダを作り、_journal.jsonを持ちません。別々の列を追加したケースはGitの衝突がschema.tsだけになり、両方の履歴を残したままcheckが通り、migrateで両方の列が作られました。0.31.11と違い、分岐していても変更同士がぶつからなければ止めません。同じ列を別の型で追加したケースでは、rc.5のcheckが次のように終了コード1で止まりました(rc.4も見出しが「Non-commutative migrations detected」で同じく検知)。
Conflicting migrations detected Found 1 conflict across 2 migrations
drizzle/20261023062635_even_shiver_man
├── drizzle/20261023062637_charming_madame_hydra
│ └─ ⚠ add_column: nickname on users table (20261023062637_charming_madame_hydra)
└── drizzle/20261023062641_cooing_mercury
└─ ⚠ add_column: nickname on users table (20261023062641_cooing_mercury)
To fix the conflicts, drizzle-kit can delete the conflicting migration branches, which is 2 migrations in total, and generate one new migration from your schema. It deletes these folders:
drizzle/20261023062637_charming_madame_hydra
drizzle/20261023062641_cooing_mercury
Every database that already ran a deleted migration must be reset.
To apply this fix, run:
drizzle-kit generate --fix-conflicts
検知の土台は各フォルダのsnapshot.jsonにあるprevIds(親のスナップショットID)です。rc.5ではスナップショットのversionが8から9に上がり、2本目以降は"type":"diff"の差分形式になりました(実測で4,438バイトから946バイト)。
generate --fix-conflicts(--helpの説明は「postgresql only」)を実行すると、衝突した2つのフォルダが削除され、現在のschema.tsから1本のマイグレーションが作り直されました。checkの出力にあるとおり、削除されたマイグレーションを適用済みのDBはリセットが必要です。共有の検証環境へ適用する前にcheckをCIで回すのが前提になります。
rc.5のcheckは、スキーマを変えてgenerateを忘れた状態も「The schema differs from the migrations」と警告しましたが、終了コードは0でした。CIで止めたいなら、出力のWarningを検出して失敗させる処理を足します。generate --explainは、ファイルを書かずに生成されるSQLだけを表示します。
v1.0 RCへの移行:変更点とdrizzle-kit up
relations()からdefineRelations()への書き換え
v1ではリレーション定義が「Relational Queries v2」に変わり、スキーマ全体を1か所で宣言します。1.0 RCには旧来のrelations()が無いため、先ほどのschema.tsからrelationsのimportとusersRelations・postsRelationsを削除し、テーブル定義だけを残します。そのうえで次のrelations.tsを追加し、rc.4とrc.5でAliceと投稿を取得できることを確認しました。
// src/relations.ts(1.0 RC)
import { defineRelations } from 'drizzle-orm';
import * as schema from './schema';
export const relations = defineRelations(schema, (r) => ({
users: { posts: r.many.posts() },
posts: { author: r.one.users({ from: r.posts.authorId, to: r.users.id }) },
}));
// src/db.ts:drizzle() には schema ではなく relations を渡す
import { drizzle } from 'drizzle-orm/node-postgres';
import { relations } from './relations';
const db = drizzle({ connection: process.env.DATABASE_URL!, relations });
await db.query.users.findMany({ with: { posts: true }, where: { name: 'Alice' } });
whereがコールバックではなくオブジェクトで書ける点も変わりました。公式の0.xからv1への変更一覧は、drizzle({ casing: 'camelCase' })がテーブル・ビュー・スキーマ単位のcasing指定に置き換わること、多次元配列が.array('[][]')の文字列指定になること、drizzle-kitのpush・pullが既定で全スキーマを対象にすることを挙げています。zodなどのバリデータ連携もdrizzle-orm本体に同梱されました。
rc.5の破壊的変更:undefinedのフィルタとエラーのparams
rc.5のリリースノートは[BREAKING]の項目を24件並べています。アプリのコードに直接効く3点を、同じスクリプトでrc.4とrc.5に流して比べました。
| 書き方 | rc.4の結果 | rc.5の結果 |
|---|---|---|
| where: { isActive: undefined } | 絞り込まず全3行 | Errorを送出 |
| where: { posts: {} } | 投稿のある1行 | 絞り込まず全3行 |
| where: { posts: true } | 投稿のある1行 | 投稿のある1行 |
| DrizzleQueryErrorのparams | 値が入る | undefined |
1行目は、検索条件をparams.isActiveのような省略可能な値から渡すコードで起きます。rc.4では条件が黙って外れて全行が返り、rc.5ではUnexpected 'undefined' in filter value. Use 'EmptyFilter' if you want the filter field to be skipped.で失敗しました。意図して条件を外したいときはparams.isActive ?? EmptyFilter(import { EmptyFilter } from 'drizzle-orm/relations')と書きます。
2行目の{ posts: {} }は、rc.4では「投稿が1件以上あるユーザー」を返していましたが、rc.5では空の条件として無視されます。リレーションの有無で絞るならtrue、無いものに絞るならfalseを書きます。4行目は、一意制約違反のメッセージからparams: X,a@xの行が消えたという変更です。値をログに出したいときはdrizzle({ ..., paramsInErrors: true })を指定します。PostgreSQLのエラーコード23505は、どちらの版でもe.cause.codeで取れました。
マイグレーションフォルダの新形式とdrizzle-kit up
1.0 RCでgenerateすると、drizzle/20261023062635_even_shiver_man/migration.sqlと同じフォルダのsnapshot.jsonが作られ、_journal.jsonは生成されません。0.31.11で作ったdrizzleフォルダにRCのdrizzle-kit upをかけると、同じ新形式へ変換されました。
rc.4で作った履歴にrc.5のcheckやgenerateをかけると、snapshot.json is not of the latest version, please run "drizzle-kit up"で止まりました。upを実行すると3本のスナップショットがversion 9へ書き換わり、checkが通りました。RCの間はRC同士でも履歴の形式が変わりうるので、RCを更新したらcheckを流し、旧形式のエラーが出たらupを実行します。
既存プロジェクトをv1へ上げる手順は、(1)0.45系で未生成の差分が無い状態にする、(2)drizzle-ormとdrizzle-kitを同じRC版にそろえる、(3)drizzle-kit upで履歴を変換する、(4)relations()をdefineRelations()へ書き換え、rc.5のundefinedの扱いに合わせて検索条件を直す、の順です。(3)の前にdrizzleフォルダをコミットしておけば、変換結果を差分で確認できます。
drizzle-kit skillsとmcp:AIエージェント向けの新コマンド
1.0 RCのdrizzle-kitにはskillsとmcpの2コマンドがあり、0.31.11にはありません。skillsはパッケージに同梱されたAgent Skillsを、skills@latest add経由でAIコーディングエージェントに追加します。mcpはMCPサーバーを起動するコマンドです。
同梱の中身はRCの間に変わりました。rc.4はdrizzle・drizzle-generate・drizzle-migrations・drizzle-pushなど8個のSKILL.mdでしたが、rc.5ではskills/drizzle/SKILL.mdの1本に、generate.md・hints.md・migrations.md・pull.md・push.mdなど7本の参照ファイルが付く構成になっています。SKILL.mdの仕様はAgent Skillsとは?SKILL.md仕様と46製品で動かす移植性の実装手順で解説しています。Drizzleのv1 RCを既定構成に含むスターターとしては、Better-T-StackがdefineRelationsの構成を生成します(Better-T-Stackとは?使い方・既定構成・T3 Stackとの違い【v3.44実測】)。
実行して確認したDrizzle ORMの落とし穴4つ
where無しのupdate・deleteは全行が対象
db.update(users).set({ name: 'X' })をwhere無しで実行すると、0.45.4でも1.0 RCでもエラーや警告は出ず、全行が更新されました(0.45.4で2行がXに変わることを確認)。SQLと同じ挙動なので、ESLintのeslint-plugin-drizzleにあるenforce-update-with-where・enforce-delete-with-whereルールで機械的に止めるのが確実です。
PostgreSQLのクエリエラーとDrizzleQueryErrorのcause
一意制約違反を起こすと、0.45.4で捕まえた例外はDrizzleQueryErrorで、e.codeはundefinedでした。PostgreSQLのエラーコード23505はe.cause.codeにあります。e.codeで重複を判定する既存コードは、0.44系以降ではcauseを見るように直します。
tx.rollback()による例外送出と呼び出し元の処理
db.transaction()の中でtx.rollback()を呼ぶと、ロールバックを引き起こす例外TransactionRollbackError(メッセージはRollback)が送出され、トランザクション内で挿入した行は残りませんでした。意図したロールバックでも呼び出し元でtry/catchしないと、未処理の例外として扱われます。
drizzle-kit 0.31.11の列名変更と非TTY環境の制約
列名nameをfull_nameへ変えて非TTY環境でgenerateすると、0.31.11はInteractive prompts require a TTY terminalで終了しました。drizzle-kitは列の削除と追加を「名前の変更」とみなすか対話で確認するためです。リネームを含む差分はローカルの端末でgenerateし、生成したSQLをコミットしてからCIでmigrateします。1.0 RCのgenerateには--output json・--hints・--hints-fileがあり、リネームの判断をファイルで渡して非対話で実行できます。
PrismaとDrizzle ORMの比較と選び方
DrizzleとPrismaのスキーマ・型生成・クエリ設計比較
| 観点 | Drizzle ORM 0.45系 | Prisma ORM 7 |
|---|---|---|
| スキーマ | TypeScriptで定義 | schema.prisma(専用DSL) |
| 型の生成 | 推論(生成手順なし) | prisma generateが必要 |
| クエリ | SQLに近いビルダー+db.query | オブジェクト形式のAPI |
| 依存 | dependencies 0件 | 生成クライアント+DBアダプタ |
| 現行安定版 | 0.45.4 | @prisma/client 7.10.1 |
| 次期版 | 1.0 RC(rc.5) | Prisma ORM 8 RC |
| 週間DL(10/12〜18) | 約1,834万 | 約1,169万(prisma) |
Prismaは7.0.0(2025年11月19日)でRustエンジンを使わないクライアントが新規プロジェクトの既定になりました。Prismaのリリース状況ページは、Prisma ORM 8をRCとし、一般提供を2026年10月の見込み、Prisma 7の保守を8の一般提供から18か月としています。同じページによると、npmのprismaのlatestはすでにPrisma 8のCLIで、このCLIはschema.prismaを読まず、generate・migrate dev・db pushもありません。Prisma 7で比較・検証するならnpm install --save-dev prisma@7で固定します。
ダウンロード数はnpmの公式APIで2026年10月23日に取得した値で、同じ週のkyselyは約1,278万でした。テーブル定義を持たずSQLを型付きで組み立てるKyselyの導入とマイグレーションはKyselyとは:0.29系の導入コードとマイグレーションで扱っています。Drizzleの数字には他ツールの依存として入る分も含まれるため、採用企業数の比較には使えません。Prismaの版ごとの変更点と7への固定方法はPrismaのバージョン一覧|最新は7.10.1、Prisma 8はRC(2026年10月)確認方法と7への固定にまとめています。
Drizzleを選ぶ条件・選ばない条件
Drizzleが向くのは、発行されるSQLをレビューで確認したいチーム、Cloudflare Workers・Vercel Edgeなどバンドルサイズが効く環境、既存DBをpullで取り込んで段階的に型を付けたい案件です。tRPCと組み合わせてDBからUIまで型をつなぐ構成は、T3 Stackとは?構成技術・型安全の仕組み・メリット/デメリットと始め方でも選択肢になっています。
一方、SQLに不慣れなメンバーが多い、専用DSLのスキーマと生成クライアントを中心にした開発フローで揃えたい、という条件ならPrismaを選びます。DBの閲覧・編集画面はDrizzle Studio(drizzle-kit studio)にもあるため、管理画面の有無は決め手になりません。v1の破壊的変更を今後吸収する余力が無いプロジェクトは、RCが外れるまでは0.45系に固定してください。既存システムのスキーマ設計やDB移行と合わせてORMを選び直す場合は、データベース設計・移行支援でご相談を受けています。デコレータでエンティティを書くTypeORMとの比較はTypeORMとPrismaを比較|2026年版の違いと選び方(型安全・マイグレーション・パフォーマンス)が参考になります。
よくある質問
Drizzle ORMの読み方と意味は?
「ドリズル」と読みます。drizzleは英語で霧雨を意味します。
Drizzle ORMの最新バージョンは?
2026年10月23日時点で、安定版は0.45.4(2026年10月8日公開)、v1の候補版は1.0.0-rc.5(2026年10月21日公開)です。npm view drizzle-orm dist-tagsで最新のタグを確認できます。
drizzle-kit checkは何を確認するコマンド?
マイグレーション履歴のスナップショット同士に矛盾がないかを検査します。0.31.11は同じ親から分岐したスナップショットを、変更内容に関係なく終了コード1で止めました。1.0 RCは、別々のブランチで同じ列を変更したような衝突する変更だけを終了コード1で止めます。どちらも、片方の履歴だけを採用してマージした状態では検知できません。
Drizzle ORMとSupabase JSはどちらを使う?
Supabase JS(supabase-js)はPostgREST経由でHTTPのAPIを呼び、行レベルセキュリティ(RLS)のポリシーで認可します。DrizzleはPostgreSQLへ直接接続してSQLを発行し、JOINやトランザクションを型付きで書けます。単一テーブルの読み書きが中心で認可をRLSに任せられるならSupabase JS、複雑なクエリやトランザクションが要るならDrizzleです。接続ロールがSUPERUSERまたはBYPASSRLS属性を持つとRLSを迂回し、テーブル所有者も通常はRLSの対象外になる点を含め、併用の設計はSupabaseにPrismaとDrizzleをつなぐ:接続文字列とマイグレーション二重管理の設計で扱っています。
Drizzle ORMでSQL Serverは使える?
1.0 RCでmssql-coreとnode-mssqlドライバが追加され、SQL Serverに対応しました。安定版の0.45系には含まれていないため、SQL Serverで使うならRC版を選ぶことになります。