RCMV
InstallWeb ElementsLoginDatabase formDatabase API
Implementation tutorial

Read and write database records

A contact form that reads existing records and writes validated submissions without allowing SQL or storage concerns into the Controller or View.

Open the Database form demo

Create and connect Cloudflare D1

  1. Run npx wrangler d1 create rcmv-contacts.
  2. Copy the returned database binding into wrangler.jsonc as DB.
  3. Create schema.sql, then run npx wrangler d1 execute rcmv-contacts --file=schema.sql --local.
  4. Use --remote only when you are ready to update the production database.

Cloudflare D1 getting started ↗ · D1 Wrangler command reference ↗

"d1_databases": [{
  "binding": "DB",
  "database_name": "rcmv-contacts",
  "database_id": "paste-generated-id"
}]
Route

src/routes/contacts.route.ts

Use GET /contacts for reading and POST /contacts for writing through separate Controllers.

defineRoute({ method: 'POST', path: '/contacts',
  controller: createContact, view: renderContacts })
Controller

src/controllers/contacts.controller.ts

Turn the submitted form into a command for the Model and choose the result to present.

const input = Object.fromEntries(await context.request.formData())
const result = await saveContact(input, context.environment.DB)
return controller.ready(result.data)
Model

src/models/contacts.model.ts

Validate with Zod, perform the parameterized database operation, and normalize the returned record.

const contact = ContactInput.parse(input)
const row = await db.prepare('INSERT INTO contacts ... RETURNING *')
  .bind(contact.name, contact.email).first()
return model.found(Contact.parse(row))
View

src/views/contacts.view.ts

Render the form, current records, errors, empty states, and confirmation—all without knowing SQL.

return html(<ContactsPage
  contacts={outcome.data.contacts}
  saved={outcome.data.saved}
/>)