Examples
Crate ships with twelve example applications in the examples/ directory. Most are self-contained vibe.d servers that serve both an API and a browser-based client at http://localhost:9090/.
Quick Start
Section titled “Quick Start”Pick an example, build it, and run it:
cd examples/testRestApiMemorydub runThen open http://localhost:9090/ in your browser.
In-Memory Examples
Section titled “In-Memory Examples”These examples use MemoryCrate and have no external dependencies — no database required. Data lives in memory and resets when the server restarts. This is the fastest way to try Crate.
testRestApiMemory
Section titled “testRestApiMemory”REST API with in-memory storage.
cd examples/testRestApiMemorydub runWhat it demonstrates:
MemoryCrate!BookandMemoryCrate!Categoryfor zero-setup storagerouter.crateSetupwith the defaultRestApipolicy- Serving a static HTML client alongside the API via
serveStaticFiles
API format — standard JSON with model-name wrapper:
// POST /books{"book": {"name": "Dune", "author": "Frank Herbert", "price": 12.99, "inStock": true}}
// GET /books{"books": [{"_id": "1", "name": "Dune", ...}]}testJsonApiMemory
Section titled “testJsonApiMemory”JSON:API with in-memory storage.
cd examples/testJsonApiMemorydub runWhat it demonstrates:
MemoryCratewith theJsonApiprotocol policyrouter.crateSetup!JsonApito use JSON:API serialization- The difference in request/response format compared to REST
API format — JSON:API data/attributes envelope:
// POST /books (Content-Type: application/vnd.api+json){"data": {"type": "books", "attributes": {"name": "Dune", "author": "Frank Herbert", "price": 12.99, "inStock": true}}}
// GET /books{"data": [{"type": "books", "id": "1", "attributes": {"name": "Dune", ...}}]}testMcpMemory
Section titled “testMcpMemory”MCP (Model Context Protocol) with in-memory storage.
cd examples/testMcpMemorydub runWhat it demonstrates:
MemoryCratewith theMcpprotocol policyrouter.crateSetup!Mcpto expose models as MCP tools- JSON-RPC 2.0 over a single
/mcpendpoint - Tool discovery via
tools/list
API format — JSON-RPC 2.0 with tool calls:
// POST /mcp — list available tools{"jsonrpc": "2.0", "id": 1, "method": "tools/list"}
// POST /mcp — create a book{"jsonrpc": "2.0", "id": 2, "method": "tools/call", "params": {"name": "create_book", "arguments": {"name": "Dune", "author": "Frank Herbert", "price": 12.99, "inStock": true}}}testGraphQLMemory
Section titled “testGraphQLMemory”GraphQL with in-memory storage.
cd examples/testGraphQLMemorydub runWhat it demonstrates:
MemoryCratewith theGraphQLprotocol policyrouter.crateSetup!GraphQLto expose models via GraphQL queries and mutations- Auto-generated schema from model definitions
- Field selection — only requested fields are returned
API format — GraphQL queries and mutations:
// POST /graphql — list books{"query": "{ books { _id name author price inStock } }"}
// POST /graphql — create a book{"query": "mutation { createBook(input: { name: \"Dune\", author: \"Frank Herbert\", price: 12.99, inStock: true }) { _id } }"}MongoDB Examples
Section titled “MongoDB Examples”These examples use MongoCrate and require a MongoDB instance running on 127.0.0.1:27017. Data persists in the test database across restarts.
testRestApiModel
Section titled “testRestApiModel”REST API with MongoDB storage and OpenAPI spec generation.
cd examples/testRestApiModeldub runWhat it demonstrates:
MongoCrate!Bookbacked by a real MongoDB collection- OpenAPI specification generation via
crateRouter.toOpenApi - The same
RestApiprotocol as the in-memory example, but with persistent data
testJsonApiModel
Section titled “testJsonApiModel”JSON:API with MongoDB storage and OpenAPI spec generation.
cd examples/testJsonApiModeldub runWhat it demonstrates:
MongoCratewithJsonApiprotocol policy- OpenAPI specification generation for JSON:API endpoints
- The same models as the REST example, but with JSON:API serialization
CSV Storage
Section titled “CSV Storage”testCsvMemory
Section titled “testCsvMemory”REST API with CSV file storage. Models use string IDs instead of ObjectId.
cd examples/testCsvMemorydub runWhat it demonstrates:
CsvCrate!Bookbacked bybooks.csvandCsvCrate!Categorybacked bycategories.csv- Using
string _idinstead ofObjectIdfor plain-text identifiers - Data persists in CSV files across restarts
Source:
import crate.collection.csv;
struct Book { string _id; string name; string author; double price; bool inStock;}
auto bookCrate = new CsvCrate!Book("books.csv");auto categoryCrate = new CsvCrate!Category("categories.csv");
router.crateSetup .add(bookCrate) .add(categoryCrate);Feature Examples
Section titled “Feature Examples”These examples add cross-cutting features on top of the basic REST API.
testAuthMemory
Section titled “testAuthMemory”REST API with OAuth2 authentication middleware.
cd examples/testAuthMemorydub runWhat it demonstrates:
PublicDataMiddlewareto protect API endpoints with token-based authUserCrateCollectionfor user management- A custom
/api/loginendpoint that validates credentials and returns a token - Creating a default admin user on startup (
admin@example.com/admin123)
Source:
import crate.auth.middleware;import crate.auth.usercollection;import vibeauth.data.usermodel;import vibeauth.authenticators.OAuth2;
auto userCrate = new MemoryCrate!UserModel;auto userCollection = new UserCrateCollection([], userCrate);
// Create an admin userUserModel adminUser;adminUser.email = "admin@example.com";adminUser.username = "admin";adminUser.isActive = true;userCollection.createUser(adminUser, "admin123");
auto authMiddleware = new PublicDataMiddleware(userCollection, null, OAuth2Configuration());
router.post("/api/login", loginHandler(userCollection));
router.crateSetup .add(bookCrate, authMiddleware) .add(categoryCrate, authMiddleware);Usage:
# Login to get a tokencurl -X POST http://localhost:9090/api/login \ -H "Content-Type: application/json" \ -d '{"email": "admin@example.com", "password": "admin123"}'
# Use the token for write operationscurl -X POST http://localhost:9090/books \ -H "Authorization: Bearer <token>" \ -H "Content-Type: application/json" \ -d '{"book": {"name": "Dune", "author": "Frank Herbert", "price": 12.99, "inStock": true}}'testCacheMemory
Section titled “testCacheMemory”REST API with a caching layer wrapping in-memory storage.
cd examples/testCacheMemorydub runWhat it demonstrates:
CrateCache!Bookwrapping aMemoryCrate!Bookto add caching- Transparent caching — the API behaves identically, but repeated reads are served from cache
Source:
import crate.collection.cache;
auto bookCrate = new MemoryCrate!Book;auto cachedBooks = new CrateCache!Book(bookCrate);
auto categoryCrate = new MemoryCrate!Category;auto cachedCategories = new CrateCache!Category(categoryCrate);
router.crateSetup .add(cachedBooks) .add(cachedCategories);testNotificationsMemory
Section titled “testNotificationsMemory”REST API with change notification callbacks.
cd examples/testNotificationsMemorydub runWhat it demonstrates:
NotificationCrate!Bookwrapping aMemoryCrate!Bookto emit change events- A
CrateNotificationcallback that logs before/after state on every create, update, or delete - Using
CrateChangeto inspect the change type, model name, and old/new values
Source:
import crate.collection.notifications;import std.functional : toDelegate;
CrateNotification callback = toDelegate(&onChange);
auto notifiedBooks = new NotificationCrate!Book(bookCrate, callback);auto notifiedCategories = new NotificationCrate!Category(categoryCrate, callback);
router.crateSetup .add(notifiedBooks) .add(notifiedCategories);
void onChange(CrateChange change) @safe { logInfo(" [%s] %s", change.type, change.modelName);
if (change.before.type != Json.Type.undefined && change.before.type != Json.Type.null_) logInfo(" before: %s", change.before); if (change.after.type != Json.Type.undefined && change.after.type != Json.Type.null_) logInfo(" after: %s", change.after);}testAltcha
Section titled “testAltcha”ALTCHA bot prevention with challenge-response verification. This example does not use Crate models — it demonstrates the standalone ALTCHA integration.
cd examples/testAltchadub runWhat it demonstrates:
createChallengeto generate an HMAC-based proof-of-work challengeverifySolutionto validate the client’s solutionextractPayloadSaltandnonceKeyfor replay prevention- Two endpoints:
GET /api/challengeandPOST /api/submit
Source:
import crate.spam.altcha;
immutable HMAC_KEY = "example-secret-key";
void getChallenge(HTTPServerRequest req, HTTPServerResponse res) { auto challenge = createChallenge(HMAC_KEY, 100_000); res.writeJsonBody(serializeToJson(challenge));}
void submitForm(HTTPServerRequest req, HTTPServerResponse res) { auto solution = data["altcha"].to!string;
if (!verifySolution(solution, HMAC_KEY)) { res.statusCode = 400; res.writeJsonBody(["error": "Invalid ALTCHA challenge"]); return; }
res.writeJsonBody(["status": "ok", "message": "Challenge verified, form accepted"]);}testCodeGenMemory
Section titled “testCodeGenMemory”CLI tool that generates typed model code for TypeScript, Swift, and Kotlin from Crate model definitions. This is not a server — it runs once and exits.
cd examples/testCodeGenMemorydub runWhat it demonstrates:
crateRouter.toTSModelsto generate TypeScript interfacescrateRouter.toSwiftModelsto generate Swift structscrateRouter.toKotlinModelsto generate Kotlin data classes- Writing generated files to
generated/typescript/,generated/swift/, andgenerated/kotlin/
Source:
import crate.generators.typescript;import crate.generators.swift;import crate.generators.kotlin;
int main() { auto router = new URLRouter;
auto bookCrate = new MemoryCrate!Book; auto categoryCrate = new MemoryCrate!Category;
auto crateRouter = router.crateSetup .add(bookCrate) .add(categoryCrate);
writeModels("generated/typescript", ".ts", crateRouter.toTSModels); writeModels("generated/swift", ".swift", crateRouter.toSwiftModels); writeModels("generated/kotlin", ".kt", crateRouter.toKotlinModels);
writeln("Code generation complete. Output in generated/"); return 0;}Project Structure
Section titled “Project Structure”Every example follows the same layout:
examples/testRestApiMemory/ dub.json # Build configuration dub.selections.json # Pinned dependency versions source/ app.d # Server entry point public/ index.html # Browser client (HTML + inline JS)The server entry point is typically under 40 lines of D code. The HTML client is a single file with no build step and no external dependencies.
Switching Between Protocols
Section titled “Switching Between Protocols”The examples show how little code changes when switching protocols. Compare the four in-memory examples — the only differences are:
- The import:
crate.protocols.jsonApi.policy,crate.protocols.mcp.policy, orcrate.protocols.graphql.policy - The setup call:
router.crateSetup!JsonApi,router.crateSetup!Mcp, orrouter.crateSetup!GraphQL - The client-side request format
The D model structs, crate creation, and server setup are identical across all four.