Models & CRUD
Model Struct Conventions
Section titled “Model Struct Conventions”A Crate model is a D struct that follows a few conventions:
Required: The _id Field
Section titled “Required: The _id Field”Every model must have a string _id field. This is the primary key used for item-level operations:
struct User { string _id; string name; string email;}Optional Fields
Section titled “Optional Fields”Use the @optional attribute (from vibe.d serialization) to mark fields that don’t need to be provided on creation:
struct Article { string _id; string title; string body; @optional string category = ""; @optional int viewCount = 0;}Optional fields are excluded from serialization when they have their default value, and they don’t need to be included in POST requests.
Nested Structs
Section titled “Nested Structs”Models can contain nested structs:
struct Point { string type = "Point"; float[2] coordinates;}
struct Site { string _id; string name; Point position;}When serialized, nested structs appear inline in the JSON:
{ "site": { "_id": "000000000000000000000001", "name": "Central Park", "position": { "type": "Point", "coordinates": [40.785091, -73.968285] } }}References to Other Models
Section titled “References to Other Models”When a model references another model (a struct with its own _id), it’s stored as an ID string. You must register a crateGetter for the referenced model:
struct Team { string _id; string name;}
struct Campaign { string _id; string title; Team team; // Reference to a Team}auto teamCrate = new MongoCrate!Team("teams");auto campaignCrate = new MongoCrate!Campaign("campaigns");
crateGetters["Team"] = &teamCrate.getItem;
crateRouter.add(teamCrate);crateRouter.add(campaignCrate);In POST/PATCH requests, references are sent as ID strings:
{ "campaign": { "title": "Save the park", "team": "000000000000000000000001" }}Without the crateGetter registration, POST and PATCH requests to the Campaign endpoint will fail with a 500 error.
Auto-Generated CRUD Operations
Section titled “Auto-Generated CRUD Operations”When you call add() or prepare(), Crate generates six endpoints:
GET List — GET /models
Section titled “GET List — GET /models”Returns all items as a JSON array:
{ "users": [ {"_id": "...", "name": "Alice", "email": "alice@example.com"}, {"_id": "...", "name": "Bob", "email": "bob@example.com"} ]}GET Item — GET /models/:id
Section titled “GET Item — GET /models/:id”Returns a single item:
{ "user": { "_id": "000000000000000000000001", "name": "Alice", "email": "alice@example.com" }}Returns 404 if the item doesn’t exist.
POST Create — POST /models
Section titled “POST Create — POST /models”Creates a new item. The request body wraps the data in a key matching the model name:
{ "user": { "name": "Charlie", "email": "charlie@example.com" }}The response includes the generated _id.
PUT Replace — PUT /models/:id
Section titled “PUT Replace — PUT /models/:id”Replaces an entire item. All non-optional fields must be provided:
{ "user": { "name": "Alice Updated", "email": "newalice@example.com" }}PATCH Update — PATCH /models/:id
Section titled “PATCH Update — PATCH /models/:id”Partially updates an item. Only include the fields you want to change:
{ "user": { "email": "newemail@example.com" }}Unspecified fields remain unchanged.
DELETE — DELETE /models/:id
Section titled “DELETE — DELETE /models/:id”Deletes an item. Returns 204 on success, 404 if the item doesn’t exist.
Custom Serialization
Section titled “Custom Serialization”For fine-grained control over JSON representation, implement toJson and fromJson on your struct:
struct Site { string _id; Point position;
Json toJson() const @safe { Json data = Json.emptyObject; data["_id"] = _id; data["position"] = position.serializeToJson; return data; }
static Site fromJson(Json src) @safe { Site site; site._id = src["_id"].get!string; // custom deserialization logic return site; }}Data Collections
Section titled “Data Collections”Crate provides several collection backends:
| Collection | Use Case |
|---|---|
MemoryCrate!T | Testing and prototyping. Data lives in memory. |
MongoCrate!T | Production MongoDB storage. |
CacheCrate!T | Wraps another crate with an in-memory cache. |
ProxyCrate!T | Delegates to another crate, useful for decoration. |
All collections implement the same Crate!T interface, so switching backends requires changing only the construction:
// Developmentauto products = new MemoryCrate!Product;
// Productionauto products = new MongoCrate!Product("products");Next Steps
Section titled “Next Steps”- Add middleware for filtering, authentication, and validation
- Create custom operations beyond standard CRUD