Skip to content

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/.

Pick an example, build it, and run it:

Terminal window
cd examples/testRestApiMemory
dub run

Then open http://localhost:9090/ in your browser.

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.

REST API with in-memory storage.

Terminal window
cd examples/testRestApiMemory
dub run

What it demonstrates:

  • MemoryCrate!Book and MemoryCrate!Category for zero-setup storage
  • router.crateSetup with the default RestApi policy
  • 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", ...}]}

JSON:API with in-memory storage.

Terminal window
cd examples/testJsonApiMemory
dub run

What it demonstrates:

  • MemoryCrate with the JsonApi protocol policy
  • router.crateSetup!JsonApi to 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", ...}}]}

MCP (Model Context Protocol) with in-memory storage.

Terminal window
cd examples/testMcpMemory
dub run

What it demonstrates:

  • MemoryCrate with the Mcp protocol policy
  • router.crateSetup!Mcp to expose models as MCP tools
  • JSON-RPC 2.0 over a single /mcp endpoint
  • 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}}}

GraphQL with in-memory storage.

Terminal window
cd examples/testGraphQLMemory
dub run

What it demonstrates:

  • MemoryCrate with the GraphQL protocol policy
  • router.crateSetup!GraphQL to 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 } }"}

These examples use MongoCrate and require a MongoDB instance running on 127.0.0.1:27017. Data persists in the test database across restarts.

REST API with MongoDB storage and OpenAPI spec generation.

Terminal window
cd examples/testRestApiModel
dub run

What it demonstrates:

  • MongoCrate!Book backed by a real MongoDB collection
  • OpenAPI specification generation via crateRouter.toOpenApi
  • The same RestApi protocol as the in-memory example, but with persistent data

JSON:API with MongoDB storage and OpenAPI spec generation.

Terminal window
cd examples/testJsonApiModel
dub run

What it demonstrates:

  • MongoCrate with JsonApi protocol policy
  • OpenAPI specification generation for JSON:API endpoints
  • The same models as the REST example, but with JSON:API serialization

REST API with CSV file storage. Models use string IDs instead of ObjectId.

Terminal window
cd examples/testCsvMemory
dub run

What it demonstrates:

  • CsvCrate!Book backed by books.csv and CsvCrate!Category backed by categories.csv
  • Using string _id instead of ObjectId for 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);

These examples add cross-cutting features on top of the basic REST API.

REST API with OAuth2 authentication middleware.

Terminal window
cd examples/testAuthMemory
dub run

What it demonstrates:

  • PublicDataMiddleware to protect API endpoints with token-based auth
  • UserCrateCollection for user management
  • A custom /api/login endpoint 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 user
UserModel 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:

Terminal window
# Login to get a token
curl -X POST http://localhost:9090/api/login \
-H "Content-Type: application/json" \
-d '{"email": "admin@example.com", "password": "admin123"}'
# Use the token for write operations
curl -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}}'

REST API with a caching layer wrapping in-memory storage.

Terminal window
cd examples/testCacheMemory
dub run

What it demonstrates:

  • CrateCache!Book wrapping a MemoryCrate!Book to 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);

REST API with change notification callbacks.

Terminal window
cd examples/testNotificationsMemory
dub run

What it demonstrates:

  • NotificationCrate!Book wrapping a MemoryCrate!Book to emit change events
  • A CrateNotification callback that logs before/after state on every create, update, or delete
  • Using CrateChange to 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);
}

ALTCHA bot prevention with challenge-response verification. This example does not use Crate models — it demonstrates the standalone ALTCHA integration.

Terminal window
cd examples/testAltcha
dub run

What it demonstrates:

  • createChallenge to generate an HMAC-based proof-of-work challenge
  • verifySolution to validate the client’s solution
  • extractPayloadSalt and nonceKey for replay prevention
  • Two endpoints: GET /api/challenge and POST /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"]);
}

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.

Terminal window
cd examples/testCodeGenMemory
dub run

What it demonstrates:

  • crateRouter.toTSModels to generate TypeScript interfaces
  • crateRouter.toSwiftModels to generate Swift structs
  • crateRouter.toKotlinModels to generate Kotlin data classes
  • Writing generated files to generated/typescript/, generated/swift/, and generated/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;
}

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.

The examples show how little code changes when switching protocols. Compare the four in-memory examples — the only differences are:

  1. The import: crate.protocols.jsonApi.policy, crate.protocols.mcp.policy, or crate.protocols.graphql.policy
  2. The setup call: router.crateSetup!JsonApi, router.crateSetup!Mcp, or router.crateSetup!GraphQL
  3. The client-side request format

The D model structs, crate creation, and server setup are identical across all four.