Skip to content

Crest Schema Documentation

Complete guide to defining and using schemas in Crest for API documentation.

Overview

Crest supports custom schema definitions for request and response bodies. Schemas are displayed in Swagger UI and help document your API's data structures.

Important: Schemas must be set manually using set_request_schema() and set_response_schema(). If not set, Crest uses sensible defaults based on HTTP method.

Features

  • Manual Schema Definition - Set custom schemas for any route
  • Type Display - Show types: string, number, boolean, object, array, null
  • Dynamic Documentation - Schemas appear in Swagger UI automatically
  • Request & Response - Define schemas for both request and response
  • All HTTP Methods - Works with GET, POST, PUT, DELETE, PATCH

Supported Types

Type Description Example Value
string Text values "hello", "john@example.com"
number Numeric values (int/float) 42, 3.14, -10
boolean True/false values true, false
object Nested objects {"key": "value"}
array Lists/arrays [1, 2, 3], ["a", "b"]
null Null value null

Setting Schemas

C++ API

// Set response schema
app.set_response_schema(crest::Method::GET, "/user",
    R"({"id": "number", "name": "string", "email": "string"})");

// Set request schema
app.set_request_schema(crest::Method::POST, "/user",
    R"({"name": "string", "email": "string", "age": "number"})");

// Method chaining
app.post("/user", handler, "Create user")
   .set_request_schema(crest::Method::POST, "/user", R"({"name": "string"})")
   .set_response_schema(crest::Method::POST, "/user", R"({"id": "number"})");

C API

// Set response schema
crest_set_response_schema(app, CREST_GET, "/user",
    "{\"id\": \"number\", \"name\": \"string\"}");

// Set request schema
crest_set_request_schema(app, CREST_POST, "/user",
    "{\"name\": \"string\", \"email\": \"string\"}");

Schema Format

Schemas are JSON objects where: - Keys are field names - Values are type strings: "string", "number", "boolean", "object", "array", "null"

Basic Schema

{
  "id": "number",
  "name": "string",
  "active": "boolean"
}

Nested Objects

{
  "user": "object",
  "settings": "object",
  "metadata": "object"
}

Arrays

{
  "tags": "array",
  "items": "array"
}

Mixed Types

{
  "id": "string",
  "count": "number",
  "enabled": "boolean",
  "data": "object",
  "list": "array",
  "optional": "null"
}

Examples

Example 1: Simple User API

crest::App app;

// GET /user - Retrieve user
app.get("/user", [](crest::Request& req, crest::Response& res) {
    res.json(200, R"({
        "id": 123,
        "name": "John Doe",
        "email": "john@example.com",
        "age": 30
    })");
});

app.set_response_schema(crest::Method::GET, "/user",
    R"({"id": "number", "name": "string", "email": "string", "age": "number"})");

Swagger UI Display:

📥 Request Schema
None

📤 Response Schema (200 OK)
{
  "id": "number",
  "name": "string",
  "email": "string",
  "age": "number"
}

Example 2: Create User

// POST /user - Create user
app.post("/user", [](crest::Request& req, crest::Response& res) {
    res.json(201, R"({"id": 456, "status": "created"})");
});

app.set_request_schema(crest::Method::POST, "/user",
    R"({"name": "string", "email": "string", "age": "number"})");

app.set_response_schema(crest::Method::POST, "/user",
    R"({"id": "number", "status": "string"})");

Swagger UI Display:

📥 Request Schema
{
  "name": "string",
  "email": "string",
  "age": "number"
}

📤 Response Schema (201 Created)
{
  "id": "number",
  "status": "string"
}

Example 3: Complex Nested Data

app.get("/profile", [](crest::Request& req, crest::Response& res) {
    res.json(200, R"({
        "user": {"id": 1, "name": "Alice"},
        "settings": {"theme": "dark", "notifications": true},
        "stats": {"posts": 42, "followers": 1337}
    })");
});

app.set_response_schema(crest::Method::GET, "/profile",
    R"({
        "user": "object",
        "settings": "object",
        "stats": "object"
    })");

Swagger UI Display:

📥 Request Schema
None

📤 Response Schema (200 OK)
{
  "user": "object",
  "settings": "object",
  "stats": "object"
}

Example 4: Array Response

app.get("/users", [](crest::Request& req, crest::Response& res) {
    res.json(200, R"([
        {"id": 1, "name": "Alice"},
        {"id": 2, "name": "Bob"}
    ])");
});

app.set_response_schema(crest::Method::GET, "/users",
    R"([{"id": "number", "name": "string"}])");

Swagger UI Display:

📥 Request Schema
None

📤 Response Schema (200 OK)
[
  {
    "id": "number",
    "name": "string"
  }
]

Example 5: All Types

app.post("/analytics", [](crest::Request& req, crest::Response& res) {
    res.json(200, R"({
        "event_id": "evt_123",
        "count": 42,
        "percentage": 85.5,
        "active": true,
        "metadata": {"source": "web"},
        "tags": ["analytics", "metrics"],
        "error": null
    })");
});

app.set_response_schema(crest::Method::POST, "/analytics",
    R"({
        "event_id": "string",
        "count": "number",
        "percentage": "number",
        "active": "boolean",
        "metadata": "object",
        "tags": "array",
        "error": "null"
    })");

Swagger UI Display:

📥 Request Schema
{
  "event": "string",
  "user_id": "number",
  "properties": "object"
}

📤 Response Schema (200 OK)
{
  "event_id": "string",
  "count": "number",
  "percentage": "number",
  "active": "boolean",
  "metadata": "object",
  "tags": "array",
  "error": "null"
}

Default Schemas

If you don't set custom schemas, Crest provides sensible defaults:

Method Request Schema Response Schema
GET None {"data": "string"}
POST {"name": "string", "value": "string"} {"id": "number", "status": "string"}
PUT {"name": "string", "value": "string"} {"status": "string"}
DELETE None {"status": "string"}
PATCH {"field": "string"} {"status": "string"}

Best Practices

1. Define Schemas for All Routes

// ✅ Good - Clear documentation
app.post("/user", handler)
   .set_request_schema(crest::Method::POST, "/user", R"({"name": "string"})")
   .set_response_schema(crest::Method::POST, "/user", R"({"id": "number"})");

// ❌ Bad - No schema, uses defaults
app.post("/user", handler);

2. Use Accurate Types

// ✅ Good - Correct types
R"({"age": "number", "name": "string", "active": "boolean"})"

// ❌ Bad - Wrong types
R"({"age": "string", "name": "number", "active": "string"})"

3. Document Nested Objects

// ✅ Good - Shows structure
R"({"user": "object", "settings": "object"})"

// ❌ Bad - Too vague
R"({"data": "object"})"

4. Use Arrays for Lists

// ✅ Good - Clear array type
R"({"users": "array", "tags": "array"})"

// ❌ Bad - Unclear
R"({"users": "object"})"

5. Match Actual Response

// ✅ Good - Schema matches response
app.get("/user", [](auto& req, auto& res) {
    res.json(200, R"({"id": 123, "name": "John"})");
});
app.set_response_schema(crest::Method::GET, "/user",
    R"({"id": "number", "name": "string"})");

// ❌ Bad - Schema doesn't match
app.set_response_schema(crest::Method::GET, "/user",
    R"({"email": "string"})");  // Response doesn't have email!

Complete Example

#include "crest/crest.hpp"

int main() {
    crest::App app;

    // User CRUD API with full schemas

    // List users
    app.get("/users", [](crest::Request& req, crest::Response& res) {
        res.json(200, R"([{"id": 1, "name": "Alice"}, {"id": 2, "name": "Bob"}])");
    }, "List all users");
    app.set_response_schema(crest::Method::GET, "/users",
        R"([{"id": "number", "name": "string"}])");

    // Get user
    app.get("/user", [](crest::Request& req, crest::Response& res) {
        res.json(200, R"({"id": 1, "name": "Alice", "email": "alice@example.com"})");
    }, "Get user by ID");
    app.set_response_schema(crest::Method::GET, "/user",
        R"({"id": "number", "name": "string", "email": "string"})");

    // Create user
    app.post("/user", [](crest::Request& req, crest::Response& res) {
        res.json(201, R"({"id": 3, "status": "created"})");
    }, "Create new user");
    app.set_request_schema(crest::Method::POST, "/user",
        R"({"name": "string", "email": "string"})");
    app.set_response_schema(crest::Method::POST, "/user",
        R"({"id": "number", "status": "string"})");

    // Update user
    app.put("/user", [](crest::Request& req, crest::Response& res) {
        res.json(200, R"({"updated": true})");
    }, "Update user");
    app.set_request_schema(crest::Method::PUT, "/user",
        R"({"name": "string", "email": "string"})");
    app.set_response_schema(crest::Method::PUT, "/user",
        R"({"updated": "boolean"})");

    // Delete user
    app.del("/user", [](crest::Request& req, crest::Response& res) {
        res.json(200, R"({"deleted": true})");
    }, "Delete user");
    app.set_response_schema(crest::Method::DELETE, "/user",
        R"({"deleted": "boolean"})");

    app.run("0.0.0.0", 8000);
    return 0;
}

Testing

Run Schema Example

xmake run crest_schema_example
# Visit http://localhost:8000/docs

Verify Schemas

  1. Open http://localhost:8000/docs
  2. Click any endpoint to expand
  3. View "📥 Request Schema" section
  4. View "📤 Response Schema (200 OK)" section
  5. Verify types match your definitions

Summary

Crest schema system provides: - ✅ Manual schema definition for all routes - ✅ Type display: string, number, boolean, object, array, null - ✅ Automatic documentation in Swagger UI - ✅ Request & response schemas - ✅ Default schemas for convenience - ✅ Method chaining support

Perfect for documenting your API's data structures! 🌊