norns-demo

Notes

A CRUD feature on Cloudflare D1 with form actions, a dynamic route, an error page and a schema-mode TRON endpoint.

#The feature folder

src/lib/norns/notes/:

  • shared/schema.c defines createNoteSchema (title trimmed and required, optional body), updateNoteSchema, the row shape noteSchema, and noteWire, the TRON schema derived from it with tronSchemaFromValibot(noteSchema, { id: 'notes.v1', path: '$.data' }).
  • server/module.c binds db transiently from the request's event.platform.env.DB, then notes.repo and notes.service transiently on top of it (see Cloudflare D1 for the code).
  • server/repo.c is hand-written SQL on the raw D1 API: list, get, create, update, remove, every call async.
  • server/service.c is a thin NotesService with typed Note and NoteInput.
  • server/public.c exports notes(container) with the same five operations.

migrations/notes/20260508_001_init.sql creates the table.

#Routes

src/routes/examples/norns/notes/+page.server.c:

export load := page.load
	handler: async ({ container }) =>
		notes: await notes(container).list()

export actions := page.actions
	create:
		input: createNoteSchema
		run: async ({ input, container }) =>
			id := await notes(container).create input
			throw redirect 303, `/examples/norns/notes/${id}`

+page.n renders the create form with Form, Field, Input, Textarea and Btn, echoing form?.values back into the inputs, and lists notes as Card links.

[id]/+page.server.c loads one note (throwing error 404 when missing) and defines update and delete actions that read event.params.id. [id]/+error.n renders the 404.

#The API endpoint

src/routes/api/notes/+server.c demonstrates schema-preloaded TRON:

export GET := route
	serializer: tronSerializer({ schema: noteWire })
	handler: async ({ container }) =>
		data: await notes(container).list()

export POST := route
	input: createNoteSchema
	handler: async ({ input, container }) =>
		id: await notes(container).create input

Both ends import the same compiled contract, so no shape declarations travel and the response is tagged #notes.v1. Clients without Accept: application/tron still get JSON.

#The vanilla twin

src/lib/svelte/notes/ (db.ts, repo.ts, service.ts, schema.ts) and src/routes/examples/svelte/notes/ implement the same feature in .svelte / .ts without the runtime, for a side-by-side comparison.