OpenAPI and API Contract Generation

An OpenAPI document is a machine-readable description of an HTTP API: its paths and operations, parameters, request and response bodies, error formats, and authentication. When the document is accurate, it becomes the engine for everything around the API — reference documentation, client SDKs and TypeScript types, server stubs, request validation, mock servers, generated tests, and automatic breaking-change detection. When it drifts from what the server actually does, every one of those artifacts becomes quietly wrong.

This guide is about treating the OpenAPI document as a contract: choosing a single source of truth, generating everything else from it, and wiring checks into CI so that the specification, the implementation, and the clients cannot silently disagree.

TL;DR

Quick Example

A code-first FastAPI service whose OpenAPI document is exported in CI, checked for drift and breaking changes, and used to generate TypeScript types for the web client:

Removing total_cents, or making a new request field required, now fails the build before any client breaks.

Core Concepts

What an OpenAPI Document Contains

Operations are identified by operationId, reusable pieces live under components, and $ref links them. OpenAPI 3.1 aligned its schemas with JSON Schema; a 3.2 release followed in 2025. Check that every tool in your pipeline supports the version you choose.

Design-First Versus Code-First

Both work. What fails is a hybrid in which people edit the spec by hand and the code independently, hoping they match.

What You Can Generate

Schemathesis applies property-based testing to APIs: it generates requests from the schema and checks that responses conform to it and that the server never returns a 500.

Preventing Drift

Code-First: Commit and Diff the Spec

Export the spec in CI, commit it, and fail the build when the exported spec differs from the committed one. Reviewers then see API changes as a readable diff of openapi.json, separate from the implementation details.

Design-First: Validate the Implementation

Run the server's tests with request and response validation middleware enabled, so any response that does not match the spec fails a test. Add a schema-driven fuzzing run against a test deployment, and use contract testing — bi-directional contracts compare consumer expectations with the provider's published spec.

Detect Breaking Changes

Tools such as oasdiff compare two versions of a spec and classify each change:

Renaming an operationId breaks nothing on the wire, but it renames methods in every generated SDK. Treat operation and schema names as part of the public API. Deliberate breaking changes belong in a new version — see API Versioning.

Linting for Consistency

A linter enforces house rules that the OpenAPI format itself does not: every operation has an operationId and description, paths are kebab-case, errors use the shared problem format, list endpoints use the same pagination parameters. Spectral rulesets are YAML:

Run the linter in CI on every change to the spec, and in editors for fast feedback. Shared rulesets make many teams' APIs feel like one product — the consistency goals in API Design.

Generated Clients in Practice

Best Practices

Name Things for the Code That Will Be Generated

operationId: getOrder becomes client.getOrder(); an inline schema becomes GetOrdersOrderIdGet200Response. Choose operation IDs deliberately and define shared schemas under components with good names.

Describe Errors and Security

Document every error response, ideally using the problem details format from RFC 9457, and declare security schemes and per-operation requirements. Generated clients and documentation are only as complete as the spec.

Add Examples

Examples make documentation useful, power mock servers, and serve as test fixtures. Validate them against their schemas so they do not rot.

Keep the Spec Next to the Code

Store the specification in the same repository as the service, review its changes with the code, and publish it at a stable URL such as /openapi.json for tools and partners.

Common Mistakes

Editing a Generated Spec by Hand

Default Operation IDs

Schemas So Loose They Say Nothing

A Published Spec Nobody Checks

FAQ

Should we use design-first or code-first?

Design-first suits public APIs, APIs consumed by several teams, and organizations that review API designs before building them. Code-first suits internal services where the team owning the code owns the API and wants zero duplication. Either works if exactly one side is the source of truth and the other is generated or validated.

What is the difference between OpenAPI and Swagger?

Swagger was the original name of the specification and remains the brand of a family of tools, such as Swagger UI and Swagger Editor. The specification was donated to the OpenAPI Initiative in 2015 and renamed the OpenAPI Specification; "Swagger 2.0" files are the predecessor of OpenAPI 3.

Which OpenAPI version should we use?

OpenAPI 3.1 is widely supported and aligns schemas with JSON Schema, so it is a sensible default. Use 3.0 if a critical tool in your pipeline does not yet support 3.1, and check tool support before adopting 3.2.

Should generated client code be committed?

Either approach works. Committing makes changes visible in review and keeps builds simple; generating in CI keeps repositories smaller. Whichever you choose, pin the generator version and never hand-edit generated files.

Does OpenAPI work for GraphQL, gRPC, or events?

No. GraphQL APIs use their own schema, gRPC uses Protocol Buffers definitions, and event-driven APIs are described with AsyncAPI. The same principles — a single source of truth, generation, and breaking-change checks — apply to each.

Related Topics

References