Skip to content
brix.v2 stable

Search documentation

70 guides

Documentation menu
Docs / APIs and dataRead Markdown ↗

OpenAPI Contract Generation

Brix generates Zod schemas and typed API contracts from Swagger/OpenAPI 2.0 and OpenAPI 3.0/3.1 definitions. Generated contracts are universal: they can be consumed from a Brix application, a Node service, a worker, a library, or an external browser client with the standard Fetch API.

Generate one specification

brix openapi generate ./openapi/catalog.yaml --out src/generated/catalog-api --name catalog

The input can be a local file or an HTTP(S) URL. External references are resolved and bundled. The example assumes the application owns openapi/catalog.yaml.

The output directory is completely generated. Every successful run replaces all of its contents, including unexpected files. Never store handwritten code or unrelated tool output there. Brix rejects paths outside the project and symbolic-link traversal.

Configure multiple definitions

Create brix.openapi.ts at the owning project root:

import { defineBrixOpenApiConfig } from "brix/config";

export default defineBrixOpenApiConfig({
  name: "commerce",
  out: "src/generated/commerce-api",
  definitions: {
    catalog: "openapi/catalog.yaml",
    accounts: "openapi/accounts.json",
  },
  strict: true,
});
brix openapi generate
brix openapi generate --config config/apis.ts

Definition paths and out are relative to the selected project root, not the config directory. Within a workspace, use brix openapi#storefront generate for the owning member.

Import the public generated root

The configured name determines the aggregate export. A single specification named catalog exposes catalogContracts; multiple definitions named commerce expose commerceContracts with definition namespaces.

import { catalogContracts, type CatalogUser } from "./generated/catalog-api/index.ts";

The exact members depend on the specification. For example, a products tag with a getProduct operation and a User schema produces access through:

catalogContracts.products.getProduct;
catalogContracts.schemas.catalogUserSchema;

Contracts are grouped by their first tag by default. --group-by none uses a nested contracts group instead. Multi-definition output adds definition prefixes and collision-safe namespaces. Repeated adjacent prefixes are collapsed rather than producing names such as CommerceCatalogCatalog.

Import only the generated top-level index.ts. Internal module paths are implementation details and can change as the specification evolves.

Consume a generated contract

Generated modules import brix/contracts and brix/zod. They do not require Hono, a server setup file, route discovery, or a running Brix application.

For the illustrative getProduct operation above, whose path declares productId:

import { createApiClient } from "brix/contracts";
import { catalogContracts } from "./generated/catalog-api/index.ts";

const client = createApiClient({ baseUrl: "https://catalog.example.com/api/v1" });
const result = await client.execute(catalogContracts.products.getProduct, {
  params: { productId: "sku-123" },
});

if (result.isOk()) console.log(result.value);
else console.error(result.error.kind);

Set baseUrl explicitly and include any path prefix from the selected OpenAPI server URL. Configure credentials and required header/cookie values at the request/client boundary; the generator does not implement authentication for you. See API clients and caching.

Review and enforce drift

brix openapi generate --dry-run
brix openapi generate --check
brix check

Generation is deterministic. The .brix-openapi.json manifest records generated output so --check detects changes and unexpected files without writing. Use it in CI with the same input and options as local generation. Review specification and generated-code changes together.

Serialization boundaries

Brix fails operations it cannot represent faithfully instead of silently altering their HTTP behavior:

  • Request bodies must have one application/json representation.
  • GET bodies, unsupported methods, unsupported success bodies, and complex or reserved query serialization are rejected.
  • Colliding tag namespaces fail generation.
  • Numeric and boolean query inputs are decoded from their URL representation before validation.
  • OpenAPI 2XX, bodyless, and default success responses remain represented in the runtime response union.

Authentication requirements, header/cookie parameters, path-parameter schemas, error-response schemas, and optional bodies produce warnings where the current runtime does not own the complete semantics. --strict turns every warning into a failure. Investigate each warning; do not simply remove strict mode to accept an incorrect client.

Keep handwritten endpoint adapters outside the generated directory when an API needs behavior that cannot be represented by the contract runtime. The generated schemas describe the supported wire boundary, not arbitrary application validation or server implementations.