Appearance
Middleware (core)
The middleware system in Vix provides a structured way to:
- intercept requests before they reach the handler
- share data across layers (auth, logging, rate limiting, etc.)
- standardize error handling
- manage cross-cutting concerns cleanly
It is built around five core concepts:
ContextServicesNextHooksResult/Error
This document explains how they fit together.
1. Context
vix::mw::Context is the object passed through the middleware pipeline.
It wraps:
- the current
Request - the current
ResponseWrapper - a shared
Servicescontainer
You use it to:
- read request data
- write responses
- access shared services
- manage request-scoped state
Example usage inside middleware:
cpp
void my_middleware(Context& ctx, Next next) {
auto& req = ctx.req();
auto& res = ctx.res();
if (!req.has_header("Authorization")) {
ctx.send_error(401, "unauthorized", "Missing token");
return;
}
next(); // continue pipeline
}The context is the single source of truth for the current request lifecycle.
2. Services
vix::mw::Services is a lightweight dependency container.
It allows registering shared services by type:
cpp
services.provide(std::make_shared<MyDatabase>(...));And retrieving them inside middleware or handlers:
cpp
auto db = ctx.services().get<MyDatabase>();Characteristics:
- type-safe
- shared ownership via
std::shared_ptr - no string keys
- minimal overhead
It is designed for runtime services such as:
- database connections
- configuration
- external clients
- caches
3. Request-Scoped State
Request-scoped data should not live in global services.
Use RequestState through Context:
cpp
ctx.emplace_state<User>(User{42, "Ada"});Later in the pipeline:
cpp
auto& user = ctx.state<User>();This is ideal for:
- authenticated user info
- correlation IDs
- rate-limit metadata
- validation results
State is strongly typed and stored using std::any internally.
4. Next (Continuation Control)
vix::mw::Next is a call-once continuation object.
It guarantees that the next middleware in the chain can only be invoked once.
cpp
void middleware(Context& ctx, Next next) {
// pre logic
next(); // continue
// post logic (optional)
}Important properties:
- call-once semantics
- safe against double invocation
- explicit pipeline control
If next() is not called, the pipeline stops.
5. Hooks
vix::mw::Hooks provides lifecycle integration points:
on_beginon_endon_error
Hooks can be merged using merge_hooks(...).
Execution order:
on_begin: forward orderon_end: reverse orderon_error: reverse order
This ensures predictable stacking behavior similar to nested middleware layers.
Hooks are ideal for:
- logging
- metrics
- tracing
- performance measurement
6. Result and Error
vix::mw::Result<T> is a typed result container that holds either:
- a value of type
T - an
Error
Error contains:
- HTTP status
- machine-readable code
- human-readable message
- optional details map
Example:
cpp
return vix::mw::fail(401, "unauthorized", "Invalid token");Or:
cpp
return vix::mw::ok(User{...});This pattern encourages explicit error handling without exceptions.
Result<void> is supported for side-effect operations.
7. Standard Error Model
The middleware error model standardizes JSON error responses:
json
{
"status": 401,
"code": "unauthorized",
"message": "Invalid token",
"details": {}
}Helper constructors exist for common HTTP errors:
bad_request()unauthorized()forbidden()not_found()conflict()internal()- etc.
Errors are normalized to ensure valid status codes.
8. Typical Middleware Flow
- Request arrives.
- Context is created.
on_beginhooks execute.- Middleware chain runs.
- Handler executes.
on_endhooks execute.- If an error occurs,
on_errorhooks run.
This guarantees structured lifecycle behavior.
Design Philosophy
The Vix middleware layer is:
- explicit
- minimal
- strongly typed
- deterministic in order
- safe under composition
It avoids:
- hidden global state
- magic injection
- implicit execution order
- runtime reflection
The result is a predictable pipeline suitable for both small services and large structured systems.
When to Use Middleware
Use middleware for:
- authentication
- authorization
- request validation
- rate limiting
- logging
- tracing
- CORS handling
- request enrichment
Avoid putting business logic in middleware. Keep it in handlers.
Summary
The middleware system provides:
- structured request lifecycle control
- typed dependency injection
- typed request-scoped storage
- explicit continuation control
- standardized error handling
It is designed to remain simple while scaling to complex applications.