Skip to content

Route options

Every Baldr route can carry per-route metadata — summary, description, tags, an operation id, a deprecation flag, and arbitrary string key/value pairs. Middleware and the OpenAPI extension read this metadata to apply policy and to render documentation.

Fluent builder

WebApplication::MapGet, MapPost, MapPut, MapDelete, and MapPatch each return a fluent RouteRegistration when called with a single path argument. Chain option setters on the result, then call .Handle(handler) to bind the route.

src/main.cpp
app->MapGet("/api/devices")
    .WithSummary("List devices")
    .WithOperationId("listDevices")
    .WithTag("devices")
    .Handle([]() { return std::vector<Device> { /* ... */ }; });

The legacy two-argument form app->MapGet(path, handler) still compiles and produces a route with default options.

Available options

Method Effect
WithSummary(std::string) Short, single-sentence description.
WithDescription(std::string) Long-form description.
WithTag(std::string) Adds a tag (may be called multiple times).
WithTags(std::vector<std::string>) Replaces the tag list.
WithOperationId(std::string) Unique operation identifier.
WithDeprecated(bool = true) Marks the operation as deprecated.
WithConsumes(std::vector<std::string>) Accepted request content types.
WithProduces(std::vector<std::string>) Produced response content types.
WithMetadata(std::string, std::string) Free-form key/value entry.
WithRequestSchemaJson(std::string) Raw JSON Schema for the request body.
WithResponseSchemaJson(std::string) Raw JSON Schema for the response body.
WithMaxBodyBytes(std::size_t) Per-route cap on the request body. Requests above this size receive 413 Payload Too Large before middleware runs.

Reading options in middleware

HttpRequest carries a RouteInfo member populated from the matched RouteEntry before middleware runs:

src/middleware.cpp
class AuditMiddleware : public IMiddleware
{
  public:
    void Handle(HttpRequest& req, HttpResponse& /*res*/,
                std::function<void()> next) override
    {
        if (req.route.options.deprecated)
        {
            // log a deprecation warning for the matched route
        }
        next();
    }
};

request.route.path is the resolved template (e.g. /api/v1/users/:id), request.route.method is the HTTP method, and request.route.group is the prefix from the enclosing MapGroup, if any.

Route groups

MapGroup(prefix, setup) registers a group of routes that share a common URL prefix. setup receives a RouteBuilder exposing the same MapGet/MapPost/... fluent API:

src/main.cpp
app->MapGroup("/api/v1", [](auto& group) {
    group.MapGet("/users")
        .WithSummary("List users")
        .WithTag("users")
        .Handle([]() { return std::vector<User>{}; });

    group.MapPost("/users", [](const HttpRequest& req) -> IResult {
        // ...
        return Results::Status(StatusCode::Created);
    });
});

The prefix is concatenated with each route's template when matching. Route options (summary, tags, operation id, schemas) are still applied per-route inside the group.

Per-route body size limit

Routes that accept a body can override the global HttpRequestParser::maxBodySize (default 100 MB) with WithMaxBodyBytes. The framework rejects requests whose Content-Length exceeds the cap (or whose accumulated body crosses the cap when no Content-Length is declared) with 413 Payload Too Large before any middleware or handler runs:

src/main.cpp
app->MapPost("/api/uploads")
    .WithMaxBodyBytes(1 * 1024 * 1024) // 1 MB
    .WithSummary("Upload a file")
    .Handle([](const HttpRequest& req) -> IResult {
        // req.body is guaranteed <= 1 MB
        return Results::Status(StatusCode::Accepted);
    });

The global parser cap still applies as a hard ceiling for every connection — WithMaxBodyBytes can only lower it for the matching route.

Transfer-Encoding is not supported on requests

The parser rejects any request carrying a Transfer-Encoding header with 400 Bad Request. Clients must send a Content-Length header (the framework uses that value to bound the body). Implementing chunked request bodies is deliberately out of scope — the routing layer expects HTTP/1.1 request framing with a known length up front.

Static files

Use MapStaticFiles(urlPrefix, rootPath) to serve a directory tree under a URL prefix — see Static files for path safety, MIME-type inference, and streaming behaviour.

src/main.cpp
app->MapStaticFiles("/static", "/var/www/my_app/wwwroot");

Next steps