Built-In Operations
CRUD Operations
Section titled “CRUD Operations”These six operations are auto-generated for every model registered with add() or prepare().
GetList
Section titled “GetList”GET /resources
Fetches all items from the collection, filtered and sorted by middleware.
| Aspect | Detail |
|---|---|
| Status code | 200 |
| Middleware hook | @getList |
| Response | Serialized 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.
GetItem
Section titled “GetItem”GET /resources/:id
Fetches a single item by ID.
| Aspect | Detail |
|---|---|
| Status code | 200 (or 304 if ETag matches) |
| Middleware hook | @getItem |
| Response | Serialized 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.
CreateItem
Section titled “CreateItem”POST /resources
Creates a new item.
| Aspect | Detail |
|---|---|
| Status code | 201 Created |
| Middleware hook | @create |
| Response | Serialized created item |
| Location header | Set to the new item’s URL |
The flow:
- Deserialize request body via the protocol’s serializer
- Check for ID conflicts (409 Conflict if ID already exists)
- Wrap in
Lazy!Tto resolve relation references (IDs → full objects) - Materialize to concrete type, then back to storage JSON
- Insert via
crate.addItem() - Set
Locationheader to the new resource URL
UpdateItem (PATCH)
Section titled “UpdateItem (PATCH)”PATCH /resources/:id
Partially updates an item. Client sends only the fields to change.
| Aspect | Detail |
|---|---|
| Status code | 200 |
| Middleware hook | @patch, @update |
| Response | Serialized 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.
ReplaceItem (PUT)
Section titled “ReplaceItem (PUT)”PUT /resources/:id
Completely replaces an item. Client sends the full object.
| Aspect | Detail |
|---|---|
| Status code | 200 |
| Middleware hook | @put, @replace |
| Response | Serialized replaced item |
Unlike PATCH, PUT does not merge. The entire item is replaced with the client’s data.
DeleteItem
Section titled “DeleteItem”DELETE /resources/:id
Removes an item.
| Aspect | Detail |
|---|---|
| Status code | 204 No Content |
| Middleware hook | @delete_ |
| Response | Empty body |
If the model has resource fields (files, images), they are cleaned up before deletion.
Action Operations
Section titled “Action Operations”ItemMethodApiOperation
Section titled “ItemMethodApiOperation”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.
ItemApiOperation
Section titled “ItemApiOperation”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);Resource Operations
Section titled “Resource Operations”For models with binary resource fields (images, files, audio):
GetResource
Section titled “GetResource”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.
SetResource
Section titled “SetResource”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.
JSON:API Relationship Operations
Section titled “JSON:API Relationship Operations”These are generated only by the JsonApi policy, following the JSON:API specification for relationship endpoints.
RelationshipGet
Section titled “RelationshipGet”GET /resources/:id/relationships/:relationship
Returns relationship data as resource identifier objects:
{ "jsonapi": {"version": "1.1"}, "data": {"type": "team", "id": "abc123"}}{ "jsonapi": {"version": "1.1"}, "data": [ {"type": "tag", "id": "tag1"}, {"type": "tag", "id": "tag2"} ]}RelationshipPost
Section titled “RelationshipPost”POST /resources/:id/relationships/:relationship
Appends items to a to-many relationship. Duplicates are skipped.
{ "data": [ {"type": "tag", "id": "newTag1"}, {"type": "tag", "id": "newTag2"} ]}RelationshipPatch
Section titled “RelationshipPatch”PATCH /resources/:id/relationships/:relationship
Replaces the entire relationship. Send null to clear it.
{"data": {"type": "author", "id": "newAuthorId"}}{"data": null}RelationshipDelete
Section titled “RelationshipDelete”DELETE /resources/:id/relationships/:relationship
Removes specific items from a to-many relationship. Non-existent IDs are silently ignored.
{ "data": [ {"type": "tag", "id": "tagToRemove"} ]}Shared Behavior
Section titled “Shared Behavior”Middleware Execution
Section titled “Middleware Execution”All operations execute middleware in two phases:
- Non-query middleware — request validation, authentication, header setting
- Query middleware — filters, sorting, pagination via
IQuery
If any middleware writes a response (e.g., 401 Unauthorized), the operation stops.
ETag Caching
Section titled “ETag Caching”Item operations (GET, PUT, PATCH) generate ETags:
- If the item has a
hashfield, use it directly - Otherwise, compute CRC32 of the JSON string
Clients can send If-None-Match to receive 304 responses.
Path Variable Extraction
Section titled “Path Variable Extraction”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.
Response Serialization
Section titled “Response Serialization”Operations use the protocol’s serializer to format responses:
toResponseItem()for single itemstoResponseList()for collections
Response mappers (@mapper) run before serialization, transforming each item to add computed properties or hide fields.