brust 2.0.0-alpha.5
Cloudflare / D1 recipe

D1 recipe

The pokedex example’s team roster: native uses bun:sqlite, Workers use D1. Both sit behind one async TeamDb interface.

1Migration

migrations/0001_team.sql
CREATE TABLE IF NOT EXISTS team (id INTEGER PRIMARY KEY, name TEXT NOT NULL, display_name TEXT NOT NULL,
  num TEXT NOT NULL, types TEXT NOT NULL, artwork TEXT NOT NULL, added_at INTEGER NOT NULL);

2Binding

In your wrangler.jsonc (wrangler d1 create <name> prints the database_id):

wrangler.jsonc
{ "d1_databases": [{ "binding": "TEAM_DB", "database_name": "pokedex-team", "database_id": "<paste the id>", "migrations_dir": "migrations" }] }

3The D1 module

The binding is read per call. The cap of six members is one conditional statement, because D1 has no transaction handle across awaits:

lib/team-db.d1.ts
import { env } from 'cloudflare:workers'

const INSERT =
  'INSERT INTO team (id, name, display_name, num, types, artwork, added_at) ' +
  'SELECT ?1, ?2, ?3, ?4, ?5, ?6, MAX(?7, (SELECT COALESCE(MAX(added_at), 0) + 1 FROM team)) ' +
  'WHERE (SELECT COUNT(*) FROM team) < ?8 ON CONFLICT(id) DO NOTHING'

// inside add(): one batch is one transaction. 1 row changed = added; else look the id up to tell present from full.
const [ins, has] = await env.TEAM_DB.batch([
  env.TEAM_DB.prepare(INSERT).bind(m.id, m.name, m.displayName, m.num, JSON.stringify(m.types), m.artwork, Date.now(), 6),
  env.TEAM_DB.prepare('SELECT 1 AS one FROM team WHERE id = ?1').bind(m.id),
])

4Alias

brust.toml
[cloudflare.alias]
"lib/team-db.ts" = "lib/team-db.d1.ts"

Mark the sqlite module server_only so it never reaches a browser bundle.

5Apply and run

~/pokedex
$ wrangler d1 migrations apply pokedex-team --local -c dist/cf/wrangler.jsonc    # local workerd
$ bun run dev:cf
$ wrangler d1 migrations apply pokedex-team --remote -c dist/cf/wrangler.jsonc   # before the first deploy

Build once first so dist/cf/wrangler.jsonc exists.

Note

A D1 query counts as a subrequest, and the Free plan allows 50 queries per invocation (source C6 on Workers target, read 2026-10-12).