Creating a Custom Crate
Implement Crate!T and provide a matching IQuery implementation. The framework handles everything else — routing, middleware, serialization.
Minimal Example
Section titled “Minimal Example”A crate backed by an associative array:
import crate.base;import vibe.data.json;
class HashMapCrate(T) : Crate!T { private { Json[string] store; ulong nextId; }
IQuery get() @trusted { return new ArrayQuery(store.values); }
IQuery getItem(const string id) @trusted { if (id in store) return new ArrayQuery([store[id]]); return new ArrayQuery([]); }
Json addItem(const Json item) @trusted { auto data = item.clone; auto id = (++nextId).to!string; data["_id"] = id; store[id] = data; return data; }
Json updateItem(const Json item) @trusted { auto id = item["_id"].get!string; store[id] = item.clone; return store[id]; }
void deleteItem(const string id) @trusted { store.remove(id); }}The Query Side
Section titled “The Query Side”Your get() and getItem() must return an IQuery. For simple backends, use MemoryQuery from crate.collection.memory:
import crate.collection.memory : MemoryQuery;
IQuery get() @trusted { return new MemoryQuery(store.values);}This gives you filtering, sorting, pagination, and projections for free.
For a database-backed crate, implement IQuery to translate calls into native queries:
class RedisQuery : IQuery { @safe: IFieldQuery where(string field) { /* Build Redis query */ } IQuery sort(string field, int order) { /* SORT command */ } IQuery limit(size_t nr) { /* LIMIT */ } IQuery skip(size_t nr) { /* OFFSET */ } InputRange!Json exec() { /* Execute and stream results */ } size_t size() { /* Count */ }}CrateConfig
Section titled “CrateConfig”Accept an optional CrateConfig!T in your constructor to let users disable specific operations:
class HashMapCrate(T) : Crate!T { private CrateConfig!T _config;
this(CrateConfig!T config = CrateConfig!T()) { _config = config; }}When config.deleteItem is false, no DELETE endpoint is generated.
ID Generation
Section titled “ID Generation”Your addItem must assign a string _id before returning:
Json addItem(const Json item) @trusted { auto data = item.clone; data["_id"] = randomUUID().toString; // Or ObjectId, auto-increment, etc. // ... store it ... return data;}Model Introspection
Section titled “Model Introspection”Use describeModel!T for compile-time information about the model’s fields:
class SmartCrate(T) : Crate!T { enum description = describeModel!T; enum idField = getIdField!description.name;
Json addItem(const Json item) @trusted { auto data = item.clone; data[idField] = generateId(); // ... }}Using Your Crate
Section titled “Using Your Crate”Register it the same way as any built-in crate:
auto crate = new HashMapCrate!User;router.crateSetup!RestApi.add(crate, authMiddleware);