Documenting APIs with OpenAPI

A specification that is generated from code drifts less; one written by hand stays readable. The useful answer is usually both, with a lint gate.

A specification worth having

openapi: 3.1.0
info:
  title: Orders API
  version: 2.3.0
servers:
  - url: https://api.example.com/v2
paths:
  /orders/{id}:
    get:
      operationId: getOrder
      summary: Fetch one order
      parameters:
        - name: id
          in: path
          required: true
          schema: { type: string, pattern: '^[0-9]{4,10}$' }
      responses:
        '200':
          description: The order
          content:
            application/json:
              schema: { $ref: '#/components/schemas/Order' }
              examples:
                pending:
                  summary: A newly placed order
                  value: { id: "1042", status: "pending", total: { amount: 2500, currency: "GBP" } }
        '404':
          $ref: '#/components/responses/Problem'
components:
  responses:
    Problem:
      description: RFC 9457 problem details
      content:
        application/problem+json:
          schema: { $ref: '#/components/schemas/Problem' }
  schemas:
    Order:
      type: object
      required: [id, status, total]
      properties:
        id: { type: string }
        status: { type: string, enum: [pending, fulfilled, cancelled] }
        total:
          type: object
          required: [amount, currency]
          properties:
            amount: { type: integer, description: Minor units, e.g. pence }
            currency: { type: string, pattern: '^[A-Z]{3}$' }
    Problem:
      type: object
      required: [type, title, status]
      properties:
        type: { type: string, format: uri }
        title: { type: string }
        status: { type: integer }
        detail: { type: string }
        instance: { type: string, format: uri }
  • Every operation needs a stable operationId; it is what generated clients name their methods after.
  • Give at least one response example per status code. Examples remove more support tickets than prose.
  • Model errors once in components and reference them, so every endpoint documents the same failure shape.
  • Put units in the description, not in the field name: amount in minor units is a documented decision, amount_pence is a naming compromise that leaks into every language.

Keeping the specification true

# 1. Lint the document in CI
npx @redocly/cli lint openapi.yaml --extends recommended

# 2. Check it is a breaking-change-free evolution of the published version
npx oasdiff breaking --base published.yaml --revision openapi.yaml

# 3. Generate the docs and the clients from the same file
npx @redocly/cli build-docs openapi.yaml -o docs/api.html
npx openapi-typescript openapi.yaml -o src/types/api.d.ts

# 4. When the spec is generated from code, fail the build if it changed
#    without a version bump
git diff --exit-code openapi.yaml || echo "spec changed: review and version it"
ApproachStrengthWeakness
Design-firstThe contract is reviewed before implementationThe implementation can drift from the spec
Code-first generationCannot drift from the handler signaturesComments and descriptions rarely get written
HybridGenerated structure, hand-written description and examplesNeeds a discipline to keep the overrides in the source
Contract testsProves the implementation matches the specExtra test surface to maintain
💡
A specification that is not enforced is worse than no specification, because clients trust it. Make the CI job that checks the spec against live responses a required status check, so drift fails the build instead of reaching a consumer.

FAQ

OpenAPI or GraphQL SDL?
They do different jobs. OpenAPI describes HTTP endpoints and their status codes; GraphQL describes a type system and a single endpoint. A service can legitimately have both surfaces.
How do I document webhooks in OpenAPI?
Use the top-level webhooks object in OpenAPI 3.1, which describes the requests your service sends to a consumer's endpoint, including the payload schema and signature header.

Testing and contract testing REST APIs Versioning and error shapes

Last refreshed 2026-09-18.