Generated REST API

The shape of the REST + WebSocket API ForgeDB generates from your schema — CRUD routes, query parameters, operational routes, and realtime streams.

forgedb generate api emits an axum router tailored to your schema. Every model gets a full CRUD route set; the whole database gets operational and realtime routes. Each handler is generated for one schema — nothing is interpreted at runtime.

CRUD routes per model#

For a model Post, the route base is the kebab-cased model name, under /api:

Method + RoutePurposeSuccessErrors
GET /api/postList with filter/sort/paginate.200 {data, total, limit, offset}
POST /api/postCreate a record.201 {id}409 unique/FK, 422 field constraint
GET /api/post/{id}Fetch one by id.200 <record>404
PUT /api/post/{id}Replace the whole record.200404, 409, 422
DELETE /api/post/{id}Delete by id.204404, 409 (referenced by children)

The {id} path segment is a string in the URL; integer-PK models parse it back to their key type.

List response shape#

{ "data": [ /* records */ ], "total": 137, "limit": 20, "offset": 0 }

total counts matches before pagination.

Records serialize their fields in schema declaration order — on the list page, on the point read, and in the WebSocket event payloads alike. JSON object key order is not significant and no client should depend on it, but it is stable rather than incidental.

Query parameters (list + read)#

The list handler filters, sorts, and paginates. Filtering and sorting are generated per-model over a closed set of fields; the query string itself is parsed by the schema-agnostic forgedb-query-params substrate.

ParamApplies toMeaning
?<field>=<value>listExact-match filter on a filterable field (typed compare — ?n=3 matches a stored 3.0).
?sort=<field> / sort directionlistSort by a sortable field.
?limit= / ?offset=listPagination (limit is clamped to a maximum).
?projection=<name>get + listReturn only a declared @projection's columns. Unknown name → 400; absent → full record.
?as_of=<watermark>get + listPoint-in-time read at a row-count watermark (snapshot). Non-numeric → 400.

Filters are a closed generated set

Only fields the generator marks filterable/sortable are accepted — there is no ad-hoc ?fields= or runtime column list. json columns are not filterable/sortable (no total order). ?projection= accepts only declared projection names.

The ?as_of= token is a row-count watermark, not a wall-clock instant, and is valid only within a compaction epoch — discard pinned tokens after a detected reopen. See snapshots.

Operational routes (no auth)#

These sit outside the JWT tenant guard so load balancers and probes reach them without a token:

RouteResponseUse
GET /health{"status":"ok"}Liveness — never touches the DB.
GET /ready{"status":"ready"}Readiness — acquires a read lock.
GET /metrics{"model_count", "rows_per_model", "total_rows"}Minimal per-model row counts (JSON, not Prometheus text).
GET /snapshot{"watermarks": {"<Model>": <n>}}Current per-model watermarks (for ?as_of= reads).

Point liveness probes at /health, readiness at /ready. See deployment.

WebSocket routes#

Realtime streams. /subscribe, /live-query, and /replicate all sit behind the tenant guard.

RouteKindPayload
GET /subscribe/<model-kebab>Change feed (insert/update/delete/link events).Typed JSON events, filtered by a generated per-model matcher.
GET /live-query/<model>?<field>=<value>Stateful result-set subscription.Typed <Model>LiveDelta (Init/Added/Updated/Removed).
GET /replicate?after=<offset>Durable, resumable replication stream.Opaque binary PersistedEvent frames from a monotonic global offset.

/replicate requires the broker

The /replicate endpoint (and the browser read-replica that consumes it) only functions when the durable broker is attached. Set [runtime].replication = true — see [runtime]. /subscribe and /live-query are in-process and always available; their broadcast buffer is [runtime].changefeed_capacity.

WebSocket clients send the JWT in the Authorization header. Behind a reverse proxy, forward the Upgrade/Connection headers. See deployment.

Search documentation

Find pages across the ForgeDB docs