Chaotic can generate C++ server-side handler base classes, request/response types, and view stub files directly from an OpenAPI 3.x or Swagger 2.0 schema. This eliminates hand-written boilerplate and keeps the server contract in sync with the schema.
For generating HTTP clients instead, see Chaotic clients.
What gets generated
For each HTTP operation in the schema chaotic produces:
- Handler base class — a userver HttpHandlerBase subclass with typed Handle() method.
- Request / response types — C++ structs with JSON parsers and serializers.
- View stub — a minimal .hpp/.cpp pair in SRC_DIR that you fill in with business logic. Existing view stubs are never overwritten on subsequent runs.
- config.chaotic.yaml — static config fragment for all generated handlers; merged into the service config by userver_generate_config_yaml().
Quickstart
Step 1. Write an OpenAPI schema (OpenAPI 3.x YAML or Swagger 2.0 JSON/YAML):
openapi: 3.0.0
info:
title: Insecure handler
version: '1.0'
paths:
/call-secret:
get:
operationId: CallSecretGet
responses:
'200':
description: Greeting returned by the digest-protected API
content:
text/plain:
schema:
type: string
/insecure/secret:
post:
operationId: insecureSecretPost
parameters:
- name: name
in: query
required: true
schema:
type: string
- name: value
in: query
required: true
description: >-
Warning: should be used for legacy API / test only,
as the query might still be logged on proxies/other HTTP agents.
schema:
type: string
x-usrv-query-log-mode: hide
responses:
'200':
description: OK
content:
application/json:
schema:
$ref: '#/components/schemas/GreetingResponse'
components:
schemas:
GreetingResponse:
type: object
additionalProperties: false
required:
- greeting
properties:
greeting:
type: string
Step 2. Declare the handler target in CMakeLists.txt:
userver_target_generate_openapi_handlers(
${PROJECT_NAME}-handler-insecure_objs
NAME insecure
OUTPUT_DIR "${CMAKE_CURRENT_BINARY_DIR}/handlers/insecure"
SRC_DIR "${CMAKE_CURRENT_SOURCE_DIR}/src"
SCHEMAS "${CMAKE_CURRENT_SOURCE_DIR}/handlers/insecure/openapi.yaml"
)
target_link_libraries(${PROJECT_NAME} ${PROJECT_NAME}-handler-insecure_objs)
target_link_libraries(${PROJECT_NAME}-handler-insecure_objs ${PROJECT_NAME}-client-secure_objs)
Step 3. Implement the generated view stub. Chaotic writes a skeleton to src/handlers/NAME/OPERATION/view.cpp on the first run:
Response View::Handle(
Request&& request,
Deps&& ,
RequestContext&
) {
return Response200{.body = {.greeting = fmt::format("Hello, {}!", request.name)}};
}
Subsequent runs leave view.cpp untouched.
Step 4. Register the handler list in main.cpp:
.Append<USERVER_NAMESPACE::components::Container<
USERVER_NAMESPACE::chaotic::openapi::server::dependencies::Factories>>()
.AppendComponentList(::handlers::insecure::ChaoticHandlersList())
.AppendComponentList(::handlers::secure::ChaoticHandlersList());
Step 5. Merge the generated config.chaotic.yaml into the service config:
userver_generate_config_yaml(
${PROJECT_NAME}
BASE_CONFIGS "${CMAKE_CURRENT_SOURCE_DIR}/static_config.user.yaml"
OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/config.yaml"
)
CMake reference
userver_target_generate_openapi_handlers(TARGET)
| Parameter | Kind | Default | Description |
| First positional (TARGET) | — | required | CMake library target name to create. |
| NAME | one-value | required | Service name; used in generated include paths (handlers/NAME/…) and as the default C++ namespace base. |
| SCHEMAS | multi-value | required | OpenAPI YAML/JSON source files. |
| OUTPUT_DIR | one-value | ${CMAKE_CURRENT_BINARY_DIR}/NAME | Destination for generated include/ and src/ trees. |
| SRC_DIR | one-value | — | If set, view stubs for new operations are written here under handlers/NAME/OP/. Omit to skip view-stub generation. |
| ARGS | multi-value | — | Extra arguments forwarded verbatim to chaotic-openapi-gen. |
The macro links the target against userver::chaotic-openapi and exposes the generated include/ directory. It also attaches the generated config.chaotic.yaml path to the target via the INTERFACE_USERVER_EXTRA_CONFIG_YAML_PATHS property, which userver_generate_config_yaml() collects transitively.
userver_generate_config_yaml(BINARY_TARGET)
Merges config.chaotic.yaml files collected from all handler targets in the link graph of BINARY_TARGET with the caller-provided base configs, producing a single config.yaml.
| Parameter | Kind | Default | Description |
| First positional (BINARY_TARGET) | — | required | Executable or library whose transitive link graph is scanned for config.chaotic.yaml files. |
| OUTPUT | one-value | required | Path for the final merged config.yaml. |
| BASE_CONFIGS | multi-value | — | User-provided config fragments applied last (highest priority). |
The macro creates a ${BINARY_TARGET}_config target that depends on the output file.
Generated file layout
For a service named greeter with an operation GET /hello (operationId getHello), the layout under OUTPUT_DIR is:
include/handlers/greeter/
get_hello/
handler.hpp # handler base class
requests.hpp # request types
responses.hpp # response types
openapi.hpp # shared schema types
src/handlers/greeter/
get_hello/
handler.cpp
requests.cpp
responses.cpp
openapi.cpp
config.chaotic.yaml # static config fragment
View stubs (written to SRC_DIR/handlers/greeter/get_hello/):
view.hpp # view interface declaration
view.cpp # view stub — implement this
Authentication
When the OpenAPI schema declares a securityScheme on a handler operation, the generated config.chaotic.yaml includes the scheme name in auth.types:
security:
- myDigestScheme: []
Generate the handler and merge its generated config with the service config:
userver_target_generate_openapi_handlers(
${PROJECT_NAME}-handler-secure_objs
NAME secure
OUTPUT_DIR "${CMAKE_CURRENT_BINARY_DIR}/handlers/secure"
SRC_DIR "${CMAKE_CURRENT_SOURCE_DIR}/src"
SCHEMAS "${CMAKE_CURRENT_SOURCE_DIR}/handlers/secure/openapi.yaml"
)
target_link_libraries(${PROJECT_NAME} ${PROJECT_NAME}-handler-secure_objs)
Register an auth checker factory under the same security-scheme name:
USERVER_NAMESPACE::server::handlers::auth::RegisterAuthCheckerFactory<
samples::chaotic_openapi_service::CheckerFactory>("myDigestScheme");
class CheckerFactory final : public server::handlers::auth::digest::AuthStandaloneCheckerFactoryBase {
public:
explicit CheckerFactory(const components::ComponentContext& context);
private:
std::shared_ptr<server::handlers::auth::digest::AuthStandaloneCheckerBase> MakeStandaloneDigestAuthChecker(
const server::handlers::auth::HandlerAuthConfig& auth_config,
std::string realm,
const server::handlers::auth::digest::AuthCheckerSettings& settings,
const storages::secdist::SecdistConfig& secdist,
const server::handlers::auth::digest::NonceCacheSettings& nonce_cache_settings
) const override;
};
The reusable standalone Digest factory obtains the nonce-cache settings from the component list:
.Append<USERVER_NAMESPACE::server::handlers::auth::digest::NonceCacheSettingsComponent>()
Options that code generation cannot know, such as the Digest realm, stay in the service config:
handler-secret-get:
auth:
realm: 'chaotic@userver.com'
See Digest Authorization/Authentication via PostgreSQL for a database-backed Digest auth checker. The chaotic_openapi_service sample contains a complete in-memory example.
Direct CLI reference
chaotic-openapi-gen is the underlying executable. Handler-relevant flags:
| Flag | Required | Description |
| files … | yes | One or more OpenAPI/Swagger YAML/JSON files. |
| --name | yes | Service name (sets include-path prefix and default namespace). |
| --gen | yes | handlers — handler classes only; handlers+views — handlers and view stubs; views — view stubs only. |
| -o / --output-dir | for handlers/handlers+views | Destination for generated include/ and src/. |
| --src-dir | for views/handlers+views | Directory where view stubs are written. |
| --namespace | no | Override the C++ namespace (default: handlers::NAME). |
| -I / --include-dir | no (repeatable) | Extra include path for x-usrv-cpp-type header lookup. |
| --clang-format | no | clang-format binary; set to empty string to disable. |
| -u / --userver | no | userver namespace macro name (default: USERVER_NAMESPACE). |