norns

Cloudflare D1

Bind a per-request D1 handle from event.platform.env and apply migrations with wrangler.

On Workers the database binding arrives per request on event.platform.env, not at boot. Bind it transiently and read it from the request scope; the repo and service become transient too, because they close over that request's handle:

import { getScope } from '@human-synthesis/norns/server'
import type { Container } from '@human-synthesis/norns/server'
import { NotesRepo } from './repo'
import { NotesService } from './service'

export default (app: Container) =>
	app.bind 'db', =>
		db := getScope()?.event?.platform?.env?.DB
		throw new Error 'D1 binding `DB` is missing — check [[d1_databases]] in wrangler.toml' unless db
		db
	app.bind 'notes.repo', (c: Container) => new NotesRepo c.resolve('db')
	app.bind 'notes.service', (c: Container) => new NotesService c.resolve('notes.repo')

The repo then uses the D1 API directly; every call is async on Workers:

export class NotesRepo
	db: D1Database
	constructor(@db: D1Database)

	async list(): Promise<Note[]>
		result := await @db.prepare('SELECT * FROM notes ORDER BY updated_at DESC').all()
		result.results as Note[]

	async create(title: string, body: string): Promise<number>
		now := Date.now()
		stmt := @db.prepare('INSERT INTO notes (title, body, created_at, updated_at) VALUES (?, ?, ?, ?)').bind(title, body, now, now)
		result := await stmt.run()
		Number result.meta.last_row_id

#wrangler.toml

[[d1_databases]]
binding = "DB"
database_name = "my-app-notes"
database_id = "<output of wrangler d1 create>"
migrations_dir = "migrations/notes"

Under vite dev, @sveltejs/adapter-cloudflare's platform proxy reads this file and serves a local D1 from .wrangler/state/, so event.platform.env.DB resolves without a Cloudflare account.

#Migrations

norns migrate is SQLite-only; use wrangler against the same SQL files:

wrangler d1 migrations apply my-app-notes --local    # dev database under .wrangler/state/
wrangler d1 migrations apply my-app-notes --remote   # production

The demo wires these as bun run db:migrate and bun run db:migrate:remote, and its deploy workflow applies remote migrations before deploying the worker. norns migrate create still scaffolds the files.