Skip to content

Built-In Operations

These six operations are auto-generated for every model registered with add() or prepare().

GET /resources

Fetches all items from the collection, filtered and sorted by middleware.

AspectDetail
Status code200
Middleware hook@getList
ResponseSerialized list wrapped in plural key

The operation streams results via InputRange!Json to avoid loading entire collections into memory. Middleware can filter, sort, limit, and skip results through the IQuery interface.

GET /resources/:id

Fetches a single item by ID.

AspectDetail
Status code200 (or 304 if ETag matches)
Middleware hook@getItem
ResponseSerialized item wrapped in singular key

Generates an ETag from the item’s hash field (if present) or CRC32 of the JSON. If the client sends If-None-Match matching the ETag, returns 304 Not Modified with an empty body.

POST /resources

Creates a new item.

AspectDetail
Status code201 Created
Middleware hook@create
ResponseSerialized created item
Location headerSet to the new item’s URL

The flow:

  1. Deserialize request body via the protocol’s serializer
  2. Check for ID conflicts (409 Conflict if ID already exists)
  3. Wrap in Lazy!T to resolve relation references (IDs → full objects)
  4. Materialize to concrete type, then back to storage JSON
  5. Insert via crate.addItem()
  6. Set Location header to the new resource URL

PATCH /resources/:id

Partially updates an item. Client sends only the fields to change.

AspectDetail
Status code200
Middleware hook@patch, @update
ResponseSerialized updated item (full)

The operation merges client data with existing stored data — client fields overwrite, unmentioned fields are preserved. Relation fields are validated to contain IDs, not nested objects.

PUT /resources/:id

Completely replaces an item. Client sends the full object.

AspectDetail
Status code200
Middleware hook@put, @replace
ResponseSerialized replaced item

Unlike PATCH, PUT does not merge. The entire item is replaced with the client’s data.

DELETE /resources/:id

Removes an item.

AspectDetail
Status code204 No Content
Middleware hook@delete_
ResponseEmpty body

If the model has resource fields (files, images), they are cleaned up before deletion.


POST /resources/:id/:methodName

Executes a custom method defined on the model struct. Auto-discovered when the struct has methods annotated with @Action:

struct Article {
string _id;
string title;
bool published;
@Action
void publish() {
published = true;
}
}

The operation fetches the item, converts it to the native type, calls the method, and stores the updated item.

POST /resources/:id/:action

A more flexible variant. Instead of calling a method on the model, it delegates to a custom callable that receives the full OperationContext:

crateRouter.prepare(articleCrate)
.itemOperation!("export", new ExportOperation);

For models with binary resource fields (images, files, audio):

GET /resources/:id/fieldName

Streams a binary resource field. Sets Content-Type, ETag, Cache-Control: public, and Content-Length headers. Supports 304 Not Modified via If-None-Match.

POST /resources/:id/fieldName

Uploads a binary resource to a field on an item. Reads the file from the request body and updates the item.


These are generated only by the JsonApi policy, following the JSON:API specification for relationship endpoints.

GET /resources/:id/relationships/:relationship

Returns relationship data as resource identifier objects:

{
"jsonapi": {"version": "1.1"},
"data": {"type": "team", "id": "abc123"}
}

POST /resources/:id/relationships/:relationship

Appends items to a to-many relationship. Duplicates are skipped.

{
"data": [
{"type": "tag", "id": "newTag1"},
{"type": "tag", "id": "newTag2"}
]
}

PATCH /resources/:id/relationships/:relationship

Replaces the entire relationship. Send null to clear it.

{"data": {"type": "author", "id": "newAuthorId"}}

DELETE /resources/:id/relationships/:relationship

Removes specific items from a to-many relationship. Non-existent IDs are silently ignored.

{
"data": [
{"type": "tag", "id": "tagToRemove"}
]
}

All operations execute middleware in two phases:

  1. Non-query middleware — request validation, authentication, header setting
  2. Query middleware — filters, sorting, pagination via IQuery

If any middleware writes a response (e.g., 401 Unauthorized), the operation stops.

Item operations (GET, PUT, PATCH) generate ETags:

  1. If the item has a hash field, use it directly
  2. Otherwise, compute CRC32 of the JSON string

Clients can send If-None-Match to receive 304 responses.

All item operations extract the :id from the URL path using the rule’s path template. The ID is stored in OperationContext for middleware and the operation to use.

Operations use the protocol’s serializer to format responses:

  • toResponseItem() for single items
  • toResponseList() for collections

Response mappers (@mapper) run before serialization, transforming each item to add computed properties or hide fields.