Durable Orders
This example shows a small Vix backend with one Cnerium durable route for order creation.
The goal is to demonstrate the core Cnerium behavior without introducing a large application structure. The backend has one normal Vix route and one durable Cnerium route:
GET /health
POST /ordersGET /health is a normal Vix route. It can be called repeatedly without changing state.
POST /orders is a durable route. It creates a logical order operation and must be safe when the client retries the same request after a timeout or lost response.
What this example proves
This example proves four things:
A new durable request executes the handler.
A retry with the same Idempotency-Key and same body returns the stored response.
A retry with the same Idempotency-Key and a different body returns 409 Conflict.
A missing Idempotency-Key is rejected.The example also reinforces the Cnerium architecture boundary:
vix::App app;
auto cnerium = cnerium::attach(app);Vix owns the backend. Cnerium attaches to the Vix app and protects selected write operations.
Complete example
#include <vix.hpp>
#include <cnerium/cnerium.hpp>
#include <string>
int main()
{
vix::App app;
auto cnerium = cnerium::attach(app);
app.get("/health", [](vix::Request &req, vix::Response &res)
{
(void)req;
res.json({
{"ok", true},
{"service", "durable-orders"}
});
});
cnerium.durable_post(
"/orders",
"orders.create",
[](cnerium::DurableRequest &request)
{
const auto body = request.json();
const std::string product_id = cnerium::support::string_or(body, "product_id", "");
const int quantity = cnerium::support::int_or(body, "quantity", 0);
if (product_id.empty())
{
return cnerium::DurableResponse::bad_request(
"Missing required field: product_id");
}
if (quantity <= 0)
{
return cnerium::DurableResponse::bad_request(
"Field quantity must be greater than zero");
}
const std::string order_id =
"ord_" + request.idempotency_key_value();
return cnerium::created({
{"ok", true},
{"order_id", order_id},
{"product_id", product_id},
{"quantity", quantity}
});
});
if (!cnerium.start())
{
return 1;
}
app.run();
return 0;
}This is intentionally small. In a real application, the handler would usually call a service, write to a database, reserve inventory, or create a domain event. The reliability behavior remains the same.
Route structure
The health route is ordinary Vix code:
app.get("/health", [](vix::Request &req, vix::Response &res)
{
(void)req;
res.json({
{"ok", true},
{"service", "durable-orders"}
});
});Cnerium is not involved because this route does not need durable execution.
The order route is different:
cnerium.durable_post(
"/orders",
"orders.create",
handler);It is a POST route that creates state. If the client retries after a timeout, the backend must not accidentally create another order.
That is why this route is registered through Cnerium.
Operation name
The durable route uses the operation name:
orders.createThe operation name is part of the idempotency scope. Cnerium uses it together with the Idempotency-Key and the request body hash.
A stable operation name makes the route behavior explicit. In a larger backend, you may have operations such as:
orders.create
payments.create
invoices.create
users.registerThese operations should not share the same idempotency namespace.
Request body
The route expects a JSON body:
{
"product_id": "p1",
"quantity": 2
}The handler reads it with:
const auto body = request.json();Then extracts fields:
const std::string product_id = cnerium::support::string_or(body, "product_id", "");
const int quantity = cnerium::support::int_or(body, "quantity", 0);The example keeps validation simple and visible.
Validation
The route rejects missing or invalid fields:
if (product_id.empty())
{
return cnerium::DurableResponse::bad_request(
"Missing required field: product_id");
}
if (quantity <= 0)
{
return cnerium::DurableResponse::bad_request(
"Field quantity must be greater than zero");
}Cnerium handles retry safety. The application still handles domain validation.
A durable route does not remove the need to validate input. It only changes the execution contract around retries.
Response
For a valid order request, the route returns:
return cnerium::created({
{"ok", true},
{"order_id", order_id},
{"product_id", product_id},
{"quantity", quantity}
});The response is a cnerium::DurableResponse.
Cnerium can store this response and replay it later if the client retries the same operation with the same key and body.
Run the example
Build and run the backend.
For a standalone Vix project:
vix build
vix runInside the Cnerium repository, if examples are enabled:
vix build --build-target all -v -- -DCNERIUM_BUILD_EXAMPLES=ON
./build-ninja/cnerium_durable_orders_realtimeThe server should listen on the configured HTTP port. Local examples usually use 8080.
First request
Send a valid request with an Idempotency-Key:
curl -i -X POST http://127.0.0.1:8080/orders \
-H "Content-Type: application/json" \
-H "Idempotency-Key: order-123" \
-d '{"product_id":"p1","quantity":2}'Expected response:
HTTP/1.1 201 CreatedExample body:
{
"ok": true,
"order_id": "ord_order-123",
"product_id": "p1",
"quantity": 2
}This is the first request for the orders.create operation with the key order-123. Cnerium executes the handler, stores the request hash, stores the response, and returns the result.
Safe retry
Send the same request again with the same key and the same body:
curl -i -X POST http://127.0.0.1:8080/orders \
-H "Content-Type: application/json" \
-H "Idempotency-Key: order-123" \
-d '{"product_id":"p1","quantity":2}'Expected response:
HTTP/1.1 201 CreatedThe body should match the first response.
The important behavior is that the handler should not execute again. Cnerium returns the stored response because this is a safe retry of the same logical operation.
Unsafe key reuse
Now reuse the same key with a different body:
curl -i -X POST http://127.0.0.1:8080/orders \
-H "Content-Type: application/json" \
-H "Idempotency-Key: order-123" \
-d '{"product_id":"p2","quantity":1}'Expected response:
HTTP/1.1 409 ConflictExample body:
{
"error": "Idempotency-Key was reused with a different request body"
}This is not a safe retry. The key already belongs to a previous body. Cnerium rejects the request instead of executing the handler or replaying the wrong response.
Missing key
A durable route requires an Idempotency-Key.
curl -i -X POST http://127.0.0.1:8080/orders \
-H "Content-Type: application/json" \
-d '{"product_id":"p1","quantity":2}'Expected response:
HTTP/1.1 400 Bad RequestWithout a key, Cnerium cannot identify the logical operation. The route cannot safely decide whether the request is new or a retry.
Why the handler should not run twice
The example creates a deterministic order id from the idempotency key:
const std::string order_id =
"ord_" + request.idempotency_key_value();This keeps the demo simple, but the real point is handler execution.
In a real service, the handler might insert a database row, reserve stock, create a payment intent, send an email, or emit a realtime event. Those actions should not happen twice because a client retried after losing the response.
Cnerium prevents the durable handler from running again for a safe retry.
Add a service layer
For a slightly more realistic structure, move the order creation logic into a function:
struct Order
{
std::string id;
std::string product_id;
int quantity{};
};
Order create_order(
const std::string &idempotency_key,
const std::string &product_id,
int quantity)
{
return Order{
"ord_" + idempotency_key,
product_id,
quantity};
}Then call it inside the durable handler:
const Order order = create_order(request.idempotency_key_value(),
product_id,
quantity);
return cnerium::created({
{"ok", true},
{"order_id", order.id},
{"product_id", order.product_id},
{"quantity", order.quantity}
});Cnerium still decides whether the handler should execute. The service only runs for a new safe request.
Complete example with service function
#include <vix.hpp>
#include <cnerium/cnerium.hpp>
#include <string>
struct Order
{
std::string id;
std::string product_id;
int quantity{};
};
Order create_order(
const std::string &idempotency_key,
const std::string &product_id,
int quantity)
{
return Order{
"ord_" + idempotency_key,
product_id,
quantity};
}
int main()
{
vix::App app;
auto cnerium = cnerium::attach(app);
app.get("/health", [](vix::Request &req, vix::Response &res)
{
(void)req;
res.json({
{"ok", true},
{"service", "durable-orders"}
});
});
cnerium.durable_post(
"/orders",
"orders.create",
[](cnerium::DurableRequest &request)
{
const auto body = request.json();
const std::string product_id = cnerium::support::string_or(body, "product_id", "");
const int quantity = cnerium::support::int_or(body, "quantity", 0);
if (product_id.empty())
{
return cnerium::DurableResponse::bad_request(
"Missing required field: product_id");
}
if (quantity <= 0)
{
return cnerium::DurableResponse::bad_request(
"Field quantity must be greater than zero");
}
const Order order =
create_order(
request.idempotency_key_value(),
product_id,
quantity);
return cnerium::created({
{"ok", true},
{"order_id", order.id},
{"product_id", order.product_id},
{"quantity", order.quantity}
});
});
if (!cnerium.start())
{
return 1;
}
app.run();
return 0;
}This shape is closer to how real projects should grow: the route handles HTTP-level parsing and validation, while the service owns the domain action.
What to check
When this example is working correctly, these behaviors should be true:
POST /orders without Idempotency-Key
returns 400 Bad Request
POST /orders with a new key and valid body
returns 201 Created
POST /orders with the same key and same body
returns the same stored response
POST /orders with the same key and different body
returns 409 ConflictIf the second request executes the handler again, the durable route is not replaying correctly. Check that the same key and exactly the same body are being sent.
If the conflict test does not return 409, make sure the route was registered with cnerium.durable_post, not app.post.
Summary
The durable orders example is the smallest useful Cnerium backend pattern.
It keeps Vix as the application owner, attaches Cnerium, registers a normal health route with Vix, and registers a critical order creation route with cnerium.durable_post.
The value is not the order example itself. The value is the durable behavior: safe retries replay the stored response, unsafe retries are rejected, and the handler is not executed twice for the same completed operation.