OpenAPI and documentation
An API contract as a machine-readable document — what goes in it, design-first versus code-first, and the checks that keep the document and the API honest.
After this lesson you can
- Read the structure of an OpenAPI document — paths, operations, parameters, schemas
- Compare design-first and code-first workflows
- Lint a document for the mistakes that break generated clients
The OpenAPI Specification is a standard, language-neutral format for describing an HTTP API in YAML or JSON: its paths, the operations on each, their parameters, request bodies, responses, data schemas and security schemes. It began as Swagger; version 2.0 was donated to the OpenAPI Initiative and renamed, and 3.x is what new APIs use. Since 3.1, its Schema Object is a superset of JSON Schema draft 2020-12, instead of the similar-but-different dialect that 3.0 used.
openapi: 3.1.0
info: { title: Orders API, version: "2.3.0" }
paths:
/orders/{orderId}:
parameters:
- { name: orderId, in: path, required: true, schema: { type: string } }
get:
operationId: getOrder
responses:
"200":
description: The order
content:
application/json:
schema: { $ref: "#/components/schemas/Order" }
"404": { description: No such order }
Two rules in that snippet trip people up. Every {orderId} in a path
template must be declared as a parameter with in: path — either on
the path item (shared by all its operations) or on the operation — and a
path parameter must say required: true. And every operationId must be
unique across the document, because code generators turn it into a
method name.
What one document buys you
A description that tools can read is worth more than prose, because the same file drives:
- reference documentation (Swagger UI, Redoc and many others);
- client SDKs and server stubs from code generators;
- request and response validation at a gateway or in middleware;
- mock servers, so frontend work starts before the backend exists;
- contract tests and breaking-change checks in CI, by diffing the document against the last released one.
const METHODS = ["get", "put", "post", "delete", "options", "head", "patch", "trace"]; const doc = { openapi: "3.1.0", paths: { "/orders": { get: { operationId: "listOrders" }, post: { operationId: "createOrder" }, }, "/orders/{orderId}": { parameters: [{ name: "orderId", in: "path", required: true }], summary: "A single order", get: { operationId: "getOrder" }, delete: { operationId: "deleteOrder" }, }, },}; export function run() { const ops = []; for (const [path, item] of Object.entries(doc.paths)) { for (const m of METHODS) { if (item[m]) ops.push(`${m.toUpperCase()} ${path} -> ${item[m].operationId}`); } } return ops;}Design-first or code-first
Design-first: write the OpenAPI document, review it like code with the people who will call the API, then implement against it. The contract is agreed before anyone depends on accidental behaviour. Code-first: annotate controllers and generate the document from the code. Faster to start and never out of date with the code — but the API's shape becomes whatever the framework happened to produce. Either way, the document belongs in version control and in CI.
Validation is where the document stops being documentation and starts being enforced:
const schema = { type: "object", required: ["sku", "quantity"], properties: { sku: { type: "string" }, quantity: { type: "integer", minimum: 1 }, },}; function typeOf(v) { if (Number.isInteger(v)) return "integer"; if (Array.isArray(v)) return "array"; return v === null ? "null" : typeof v;} function validate(body) { const errors = []; for (const f of schema.required) if (!(f in body)) errors.push(`/${f}: is required`); for (const [f, s] of Object.entries(schema.properties)) { if (!(f in body)) continue; if (typeOf(body[f]) !== s.type) errors.push(`/${f}: must be ${s.type}`); else if (s.minimum !== undefined && body[f] < s.minimum) errors.push(`/${f}: must be >= ${s.minimum}`); } return errors;} export function run() { return [ validate({ sku: "PEN-1", quantity: 2 }), validate({ sku: "PEN-1", quantity: 0 }), validate({ quantity: 1.5 }), ];}Documentation is more than the reference
Generated reference pages answer "what does this field mean". New users
also need a quickstart that makes one real call in minutes, an
authentication guide, examples for every operation, a catalogue of error
types and what to do about each, rate-limit rules, and a changelog
with deprecation dates.
Common mistakes
- A hand-maintained document that drifts from the implementation, so generated clients fail against the real API. Test the API against it in CI.
- Path templates with undeclared parameters, or path parameters without
required: true; tools reject or misgenerate them. - Duplicate or missing
operationIds, giving generated SDKs clashing or meaningless method names. - Documenting only the 200 response, so clients learn the error shapes in production.
What interviewers ask
- What is OpenAPI for? — a machine-readable contract that generates docs, clients and mocks, validates traffic, and lets CI detect breaking changes.
- Design-first or code-first? — design-first agrees the contract before implementation; code-first keeps the document in sync with the code; both need the document checked in CI.
- How do you keep docs and the API in sync? — generate one from the other or test the running API against the document, and diff the document on every change.
Exercise · Pro
Lint an OpenAPI document
Exercises are free in the first 3 lessons of every course. From here on they are part of Pro and Sprint; the lesson and its runnable examples stay open.
Upgrade