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.


These operations are registered automatically when using the MCP policy. They are not CRUD operations — they provide protocol-level functionality for AI clients.

Discovers REST upload endpoints for file resource fields. Given a model, resource ID, and field name, returns the URL, HTTP method, and content type needed to upload a file directly via REST instead of base64-encoding it through MCP tool arguments.

AspectDetail
Tool nameget_upload_instructions
Parametersmodel (required), id (required), field (required)
ResponseUpload URL, method, content type, and example curl command
FallbackSuggests the base64 set_*_value tool if no REST endpoint exists

Returns the available query/filter parameters for a model’s list endpoint as a JSON Schema object. AI clients use this to discover filtering, pagination, and search options before calling list_* tools.

AspectDetail
Tool nameget_list_query_options
Parametersmodel (required)
ResponseJSON Schema describing available query parameters

The tool description in tools/list is automatically updated with the list of available models.

Handles MCP lifecycle notifications (notifications/initialized) and ping requests.

AspectDetail
Methodnotifications/initialized or ping
Notifications response202 Accepted (empty body)
Ping responseJSON-RPC success with empty result object

The IQuery interface is used by query middleware to build database queries. It exposes:

MethodReturnDescription
where(field)IQueryAdd a field filter
limit(n)IQueryLimit result count
skip(n)IQuerySkip first N results
size()size_tExact count of matching items (respects filters)
estimatedSize()size_tEstimated collection size from metadata (no filter, no collection scan)

estimatedSize() is significantly faster than size() because it reads collection metadata rather than scanning documents. It does not account for applied filters — use it when you need a rough magnitude estimate (e.g. for AI clients to understand data scale before querying).