RCMV
InstallWeb ElementsLoginDatabase formDatabase API
Implementation tutorial

Expose database data as an API

The same RCMV path works without a human-facing screen. The View still matters: it owns the public JSON representation, headers, and HTTP status.

Open the Database API demo

No human screen does not mean no View

The JSON View owns field names, status codes, headers, error envelopes, and version stability. The Model’s database row remains private; the API representation is deliberately public.

Route

src/routes/api.products.route.ts

Map GET /api/products/:id to the API Controller and JSON View.

defineRoute({ method: 'GET', path: '/api/products/:id',
  controller: getProduct, view: renderProductJson })
Controller

src/controllers/api.products.controller.ts

Request one record from the Model and choose ready or missing without shaping JSON.

const result = await findProduct(context.params.id, env.DB)
return result.status === 'found'
  ? controller.ready(result.data)
  : controller.missing()
Model

src/models/products.model.ts

Query the database, validate the row, and return the same standard Model result used by DOM features.

const row = await db.prepare('SELECT * FROM products WHERE id = ?')
  .bind(id).first()
return row ? model.found(Product.parse(row)) : model.missing()
View

src/views/product-json.view.ts

Create the stable public JSON contract. There is no visual interface, but representation remains a View responsibility.

return Response.json(
  outcome.status === 'ready'
    ? { data: outcome.data }
    : { error: { code: 'PRODUCT_NOT_FOUND' } },
  { status: outcome.status === 'ready' ? 200 : 404 })