🧊

Cloudflare D1でのトランザクションと、Drizzle ORMの対応

出オチですが、Cloudflare D1には、いわゆるSQLのトランザクションは実装されてない。

つまり、D1に対して発行されたSQL文の中に、BEGIN TRANSACTIONとか書いてあると、D1_ERROR: not authorizedとかいうエラーになる。

https://github.com/cloudflare/workerd/blob/e78561270004797ff008f17790dae7cfe4a39629/src/workerd/api/sql-test.js#L251

(もっとわかりやすいエラーメッセージにしてほしい)

代わりにbatch()

D1 client API · Cloudflare D1 docs https://developers.cloudflare.com/d1/platform/client-api/#batch-statements

絶望することなかれ、同様のことができるAPIが用意されてて、それがbatch()ってやつ。

await db.batch([
  db.prepare("UPDATE users SET rank = ?1 WHERE user_id = ?2").bind(1, 17),
  db.prepare("UPDATE users SET rank = ?1 WHERE user_id = ?2").bind(2, 19),
]);

というように、SQL文を配列で渡すだけ。

Batched statements are SQL transactions. If a statement in the sequence fails, then an error is returned for that specific statement, and it aborts or rolls back the entire sequence.

ですって。

Drizzle ORMでは

現時点でnpmにあるdrizzle-ormは、9月にリリースされた0.28.6が最新バージョン。

元からdb.transaction()ってAPIは生えてるけど、これを使うと先述のエラーでコケる。

await db.transaction(async (tx) => {
  await tx.insert(...);
  await tx.insert(...);
});

なので、なんとかしてbatch()を使う必要があるが、現状ではDrizzleのtoSQL()というAPIを使って、SQL文とパラメータを手動で入れないといけない。

const rawDb = env.DB;
const db = drizzle(rawDb);

const q1 = db.insert(...).toSQL();
const q2 = db.insert(...).toSQL();

await rawDb.batch([
  rawDb.prepare(q1.sql).bind(...q1.params),
  rawDb.prepare(q2.sql).bind(...q2.params),
]);

もっとシュッと書きたいな〜って思ったそこのあなた。

実はD1のドライバーに対してさっきのbatch()を使えるようにしたPRがあり、マージされてるけど未リリースという状態。

Add batch support for d1 driver by AndriiSherman · Pull Request #1202 · drizzle-team/drizzle-orm https://github.com/drizzle-team/drizzle-orm/pull/1202

というわけで、0.28.7なり0.29.0なりがリリースされたら、こう書けるようになる。

const db = drizzle(...);

await db.batch([
  db.insert(...),
  db.insert(...),
]);

待ちきれない私は、npm i drizzle-orm@betaしちゃった。

drizzle-orm/changelogs/drizzle-orm/0.29.0.md at beta · drizzle-team/drizzle-orm https://github.com/drizzle-team/drizzle-orm/blob/beta/changelogs/drizzle-orm/0.29.0.md#-d1-batch-api-support

2023/11/10: 追記

0.29.0リリースされた!

Release 0.29.0 · drizzle-team/drizzle-orm https://github.com/drizzle-team/drizzle-orm/releases/tag/0.29.0