climate compose merges multiple OpenAPI 3.x specifications — each assigned a
dedicated path prefix — into a single composite specification and generates a
CLI from the result. The CLI acts as a facade over several microservices:
one binary, one authentication model, all services.
Modern back-ends are decomposed into microservices. Each service owns its own OpenAPI document. Without tooling, developers and agents must juggle many separate CLIs or issue raw HTTP calls to reach different services. A single gateway CLI is far more ergonomic and aligns with the Backends-for-Frontends (BFF) pattern.
# Two microservices, each mounted under a different prefix
climate compose \
orders.yaml:/api/orders \
users.yaml:/api/users
# With all optional flags
climate compose \
--name gateway \
--title "My Gateway API" \
--api-version 2.0.0 \
--description "Unified facade over Orders and Users services" \
--out-dir /tmp/gateway \
--force \
https://orders.svc/openapi.json:/orders \
https://users.svc/openapi.json:/usersEach positional argument has the form <spec>:<prefix> where <spec> is a
local file or URL and <prefix> starts with /.
The merge is performed by internal/compose.Merge:
- Load — each source spec is loaded and validated with
spec.Load. - Namespace components — every schema, parameter, and security scheme
defined under
components/is renamed to<ns>-<original>where<ns>is derived from the prefix (e.g./api/orders→api-orders). This prevents name collisions between services that happen to share component names. - Rewrite
$refs — every$refinside the spec (operation parameters, request bodies, responses) is updated to point at the new namespaced name. - Prefix paths — every path key is prepended with the caller-specified
prefix (trailing slashes on the prefix are removed; the path's leading
/is preserved). - Merge — paths, components and tags are accumulated into a single output
spec.OpenAPIstruct. Tags are de-duplicated by name. Security schemes follow a last-writer-wins strategy so that a centralised gateway scheme can override individual service schemes. - Generate — the merged spec is passed to
generator.Generateexactly as a regularclimate generatewould; the resulting CLI binary works against all services.
Security schemes from all input specs are merged into the composed document.
The recommended approach for a real gateway is to pass a shared Bearer-token
scheme via the first or only input that defines one, letting the gateway
validate it and forward downstream with service-account tokens. Alternatively,
pass a custom --auth header per sub-command using the generated CLI's
persistent flags.
Given two services that each define a components/schemas/Error schema:
| Service | Original ref | Namespaced ref |
|---|---|---|
/api/orders |
#/components/schemas/Error |
#/components/schemas/api-orders-Error |
/api/users |
#/components/schemas/Error |
#/components/schemas/api-users-Error |
Both schemas are preserved in the merged document without conflict.
- Only OpenAPI 3.x specs are supported (same constraint as
climate generate). allOf/oneOf/anyOfschema combiners are not rewritten in the current implementation; plain$refstrings are rewritten.- The generated CLI uses the primary merged server URL and can override it via
--base-url(or<CLI>_BASE_URL). If the server URL contains template variables ({region}), the generated CLI also supports--server-var-<name>/<CLI>_SERVER_VAR_<NAME>overrides. - If you want a true API gateway (single ingress), deploy a reverse proxy in
front of the services and point
--base-urlat it.