Skip to content
Download

Request Bodies

Minimal API handlers can receive request bodies as strongly typed C++ values.

Instead of reading and deserializing the raw request body manually, declare the expected body type as a handler parameter and let Mach bind it before invoking the endpoint.

Consider a request model:

struct CreateUserRequest {
std::string name;
int age;
};
MACH_DEFINE_JSON(CreateUserRequest, name, age)

The model can be accepted directly by a minimal API handler:

app.mapPost("/users", [](CreateUserRequest user) {
return mach::ok(user.name);
});

For a request body such as:

{
"name": "Alice",
"age": 24
}

Mach deserializes the body into a CreateUserRequest and passes it to the handler by value.

The handler can then work with the strongly typed value without manually parsing JSON.

A minimal API handler can take between zero and two parameters:

  • no parameters
  • a mach::Context&
  • a request body
  • both a mach::Context& and a request body

When both are used, they can appear in either order:

app.mapPost("/users/{id:int}", [](mach::Context& context, CreateUserRequest user) {
const int id = context.request.routeParam<int>("id");
// ...
});

or:

app.mapPost("/users/{id:int}", [](CreateUserRequest user, mach::Context& context) {
const int id = context.request.routeParam<int>("id");
// ...
});

Mach identifies the context and body parameters by their types, so their order does not matter.

For user-defined types, use MACH_DEFINE_JSON to define how the type maps to JSON:

struct CreateUserRequest {
std::string name;
int age;
};
MACH_DEFINE_JSON(CreateUserRequest, name, age)

List the fields that should participate in serialization and deserialization after the model type.

MACH_DEFINE_JSON integrates the type with Mach’s JSON serialization support and is based on nlohmann/json’s non-intrusive JSON mapping.

Primitive types do not require MACH_DEFINE_JSON.

Request bodies can also be bound directly to primitive types:

app.mapPost("/double", [](int value) {
return mach::ok(value * 2);
});

Mach converts the request body to the type expected by the handler parameter before calling it.

Automatic binding is usually the simplest option for structured request bodies, but the original body is also available through the Request API:

app.mapPost("/raw", [](mach::Context& context) {
const auto& body = context.request.body();
return mach::ok(body);
});

Use the raw body when the endpoint needs to work with the original request content rather than a deserialized value.

Body binding handles the request body only.

Other request information, such as route parameters, query parameters, headers, and cookies, is accessed through mach::Context& and the Request API.

For example:

app.mapPost("/users/{id:int}", [](mach::Context& context, CreateUserRequest user) {
const int id = context.request.routeParam<int>("id");
// ...
});

This lets an endpoint use strongly typed body data alongside other information from the current request.


Next, you’ll learn how minimal API handlers work with the outgoing response.