出オチですが、Cloudflare D1には、いわゆるSQLのトランザクションは実装されてない。
つまり、D1に対して発行されたSQL文の中に、BEGIN TRANSACTION
とか書いてあると、D1_ERROR: not authorized
とかいうエラーになる。
(もっとわかりやすいエラーメッセージにしてほしい)
代わりに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