Skip to main content

CRUD endpoints

Every resource generates a standard set of REST endpoints. This page documents their request and response shapes. Filtering, pagination, ordering, and relation includes are covered in their own pages and apply to the list endpoint.

List — GET /

Returns a paginated list. Supports filter, select, include, orderBy, cursor, limit, totalCount.

GET /api/posts?filter=published==true&orderBy=createdAt:desc&limit=20&include=author
{
"items": [{ "id": "p1", "title": "Hello", "author": { "id": "u1", "name": "Ada" } }],
"nextCursor": "eyJ2YWx1ZXMiOnsuLi59fQ==",
"hasMore": true,
"totalCount": 137
}

totalCount is present only when ?totalCount=true.

Get one — GET /:id

GET /api/posts/p1?include=author,tags

Returns the single row, or 404 if not found or outside the read scope. With ETags enabled, the response carries an ETag header and honors If-None-Match (returns 304).

Create — POST /

POST /api/posts
Content-Type: application/json

{ "title": "Hello", "body": "..." }

Update (partial) — PATCH /:id

Updates only the provided fields.

PATCH /api/posts/p1
Content-Type: application/json

{ "title": "Updated title" }

Runs onBeforeUpdate → update → onAfterUpdate, emits a changed event, enforces If-Match when ETags are configured (412 on mismatch), and auto-increments the version field.

Replace (full) — PUT /:id

Same as PATCH but replaces the whole row; omitted writable fields are reset to their defaults/null. Governed by the same enableUpdate capability.

Delete — DELETE /:id

DELETE /api/posts/p1

Returns 204. Runs onBeforeDelete/onAfterDelete, emits a removed event, enforces If-Match. With softDelete, the row is marked instead of removed.

Counting — GET /count

GET /api/posts/count?filter=published==true
{ "count": 137 }

Honors the read scope and filter; excludes soft-deleted rows unless ?withDeleted=true.

Projections

The select parameter narrows returned columns:

GET /api/posts?select=id,title,createdAt
GET /api/posts/p1?select=id,title

select cannot recover columns hidden by fields.readable — masking is applied after projection. Computed fields and included relations always pass through.

The typed client narrows the return type to exactly the selected fields — see Client queries.

Error format

All errors use RFC 7807 Problem Details. See Error handling.

{
"type": "https://covara.dev/errors/not-found",
"title": "Not Found",
"status": 404,
"detail": "post p1 not found"
}