# OpenAPI and Swagger

> Describe your REST API once in OpenAPI, and get interactive docs, client SDKs, request validation, and contract tests for free - the API as a spec.


---

# OpenAPI and Swagger

You've written a REST API. Now someone wants docs, and someone else wants a client library, and a third person keeps calling your endpoint with the wrong field names. You could write all that by hand, three times, and watch it drift out of date the moment you ship the next endpoint. Or you could describe the API once, in a file, and let machines do the rest. That file is an OpenAPI document, and this guide is about treating it as the single source of truth instead of an afterthought.

## How to read this

Read in order. Phase 1 builds the mental model: what OpenAPI actually is, why "Swagger" and "OpenAPI" are two names for overlapping things, and why a machine-readable contract changes how you work. Phase 2 is the everyday core: writing a spec by hand, generating docs and clients, and choosing between design-first and code-first. Phase 3 is production reality: validation, mocking, contract tests, versioning, and the gotchas that bite teams who treat the spec as decoration.

## The phases

1. [Phase 1: The Contract, Not the Docs](01-the-contract-not-the-docs.md) - what OpenAPI is and why a machine-readable spec exists.
2. [Phase 2: Writing and Generating From the Spec](02-writing-and-generating.md) - author a spec, render docs, generate clients, pick a workflow.
3. [Phase 3: The Spec at Work in Production](03-the-spec-in-production.md) - validation, mocking, contract tests, versioning, and the traps.


---

# The Contract, Not the Docs

Here's the situation you probably know. You have a REST API. It works. But the people who *use* it can't see inside your head. They don't know that `POST /users` wants `email` and not `emailAddress`, that `age` must be a positive integer, or that a 404 comes back as `{ "error": "..." }` and not `{ "message": "..." }`. So you write docs. In a wiki, maybe. And the docs are right on Tuesday and wrong on Thursday, because you shipped a new field and forgot to update the wiki, and now someone is debugging against a lie.

OpenAPI exists to kill that gap. Instead of describing your API in prose that humans read and machines ignore, you describe it in a structured file that *machines* read first. That file is the contract.

## What OpenAPI actually is

OpenAPI is a specification format - a standard shape for a document that describes a REST API. The document is plain YAML or JSON. It lists every endpoint, every method, every parameter, the shape of every request body, the shape of every response, and the meaning of every status code. Nothing executes. It's a description, not a program.

That's the whole trick: it's *machine-readable*. Because the format is standardized, any tool that understands OpenAPI can read your file and do something useful with it - render docs, generate a client, validate a request - without knowing anything else about your project.

Here's the smallest version that's still real:

```yaml
openapi: 3.1.0
info:
  title: Bookmarks API
  version: 1.0.0
paths:
  /bookmarks/{id}:
    get:
      summary: Fetch one bookmark
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: integer
      responses:
        '200':
          description: The bookmark
          content:
            application/json:
              schema:
                type: object
                properties:
                  id: { type: integer }
                  url: { type: string }
        '404':
          description: No bookmark with that id
```

*What just happened:* in about 25 lines you've stated a complete fact - "there's a `GET /bookmarks/{id}`, it takes an integer `id` in the path, and it returns either a 200 with `{ id, url }` or a 404." A human can read it, but more importantly a tool can *parse* it. The `{id}` in the path and the `parameters` block aren't decoration; they're the formal declaration that this endpoint is parameterized.

## So what is "Swagger," then?

This trips up everyone, so let's settle it plainly.

"Swagger" was the original name. It started as a spec format *and* a set of tools. In 2015 the spec itself was donated to a foundation and renamed **OpenAPI** - that's the standard, the thing your file conforms to. The name "Swagger" stayed on the *tooling* built around it.

So the rule of thumb:

- **OpenAPI** = the specification (the format your file is written in). Versions look like 3.0, 3.1.
- **Swagger** = a family of tools that work with OpenAPI files - Swagger UI (renders docs), Swagger Editor (writes specs), Swagger Codegen (generates code).

When someone says "the Swagger file," they almost always mean the OpenAPI document. When they say "let me check the Swagger," they usually mean Swagger UI - the interactive docs page. The terms blur in conversation; the distinction that matters is *spec vs. tool*.

> The version number on the first line (`openapi: 3.1.0`) is the spec version your document follows, not your API's version. Your API's version goes under `info.version`. Mixing these up is a classic first-day confusion.

## Why a machine-readable contract changes everything

Once the description is something a machine can read, the same file feeds a whole toolchain. You write it once; many tools consume it:

```text
                       ┌─→ Swagger UI  (interactive docs)
                       ├─→ codegen     (client SDKs, server stubs)
  openapi.yaml ────────┼─→ validator   (reject bad requests)
  (single source)      ├─→ mock server (fake responses before code exists)
                       └─→ contract test (does the real API still match?)
```

*What just happened:* one file fans out into five different jobs that teams otherwise do by hand. The docs can't drift from the client SDK, because both are generated from the same source. That's the payoff - not "nice docs," but *one source of truth* that everything else derives from.

This is the mental shift. Without OpenAPI, the truth about your API lives in the code, and everything else (docs, clients, test fixtures) is a hand-maintained copy that rots. With OpenAPI, the truth lives in the spec, and the copies are generated. Copies you regenerate can't lie.

## The contract is a promise to two audiences

A good OpenAPI document serves humans and machines at once, and it's worth holding both in mind:

- **Humans** read it (usually rendered as docs) to learn how to call your API: what to send, what comes back, what the errors mean.
- **Machines** read it to *do work*: generate a typed client so a frontend never guesses a field name, reject a malformed request before it reaches your handler, or fail a CI build when the live API stops matching the promise.

If you've read [the broader story of how to design APIs that don't rot](/guides/designing-apis-that-last), this is the concrete artifact that lets you *enforce* a stable contract instead of merely hoping for one.

## For builders

You don't need to adopt the whole toolchain to get value. The cheapest possible win is this: write the spec, point Swagger UI at it, and now you have docs that live next to your code in version control. Every other tool - codegen, validation, mocking - is something you bolt on later when the pain shows up. Start with the file. The file is the asset.

```quiz
[
  {
    "q": "What is the relationship between OpenAPI and Swagger?",
    "choices": [
      "They are competing, incompatible spec formats",
      "OpenAPI is the specification; Swagger is the family of tools built around it",
      "Swagger is the new name and OpenAPI is deprecated",
      "OpenAPI is for REST and Swagger is for GraphQL"
    ],
    "answer": 1,
    "explain": "The spec was renamed OpenAPI in 2015; the Swagger name stayed on tools like Swagger UI and Swagger Codegen."
  },
  {
    "q": "What does the first line `openapi: 3.1.0` declare?",
    "choices": [
      "The version of your API",
      "The version of the OpenAPI specification the document conforms to",
      "The minimum client library version required",
      "The HTTP version the API uses"
    ],
    "answer": 1,
    "explain": "That field is the spec version. Your API's own version lives under info.version."
  },
  {
    "q": "Why is a machine-readable contract more valuable than hand-written docs?",
    "choices": [
      "It is shorter to write",
      "It renders in a prettier font",
      "Many tools can generate docs, clients, validators, and tests from one source, so they cannot drift apart",
      "It removes the need to write any code"
    ],
    "answer": 2,
    "explain": "One source of truth fans out to docs, SDKs, validation, and tests - generated copies cannot lie about the API."
  }
]
```


---

# Writing and Generating From the Spec

Now you actually write one. The good news: you already saw the shape in phase 1, and it doesn't get much scarier than that. The work is mostly learning a handful of keys and one organizing trick (`components`) that keeps the file from turning into a swamp. Then we point tools at it and watch the spec earn its keep.

## The anatomy of a real document

Every OpenAPI document has the same skeleton. Three top-level sections do almost all the work:

```yaml
openapi: 3.1.0

info:                          # who/what this API is
  title: Bookmarks API
  version: 1.0.0

paths:                         # every endpoint lives here
  /bookmarks:
    get: { ... }
    post: { ... }

components:                    # reusable pieces, referenced by $ref
  schemas:
    Bookmark: { ... }
```

*What just happened:* `info` is metadata, `paths` is the list of endpoints (the bulk of the file), and `components` is your toolbox of reusable definitions. That third section is the one that keeps you sane - instead of redefining the shape of a bookmark in five places, you define it once and point at it.

## Reuse with components and $ref

Repeating yourself in a spec is how it rots: you change one copy, forget the other four, and now the contract contradicts itself. The fix is `components` plus `$ref` (a reference - "look over there for the definition").

```yaml
paths:
  /bookmarks:
    post:
      summary: Create a bookmark
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/NewBookmark'   # reuse
      responses:
        '201':
          description: Created
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Bookmark'    # reuse

components:
  schemas:
    NewBookmark:
      type: object
      required: [url]
      properties:
        url: { type: string, format: uri }
        title: { type: string }
    Bookmark:
      allOf:
        - $ref: '#/components/schemas/NewBookmark'
        - type: object
          properties:
            id: { type: integer }
```

*What just happened:* `$ref: '#/components/schemas/Bookmark'` means "insert the `Bookmark` schema defined below." Define a shape once, reference it everywhere. The `allOf` on `Bookmark` says "everything in `NewBookmark`, plus an `id`" - so the create-shape and the stored-shape share a definition and can't drift apart. Change `url` to required in one spot and every endpoint that references it updates at once.

> `required` is its own list at the object level, not a flag on each property. `required: [url]` means `url` must be present; `title` is optional. This is the single most common thing newcomers get wrong.

## Render it: Swagger UI

A spec you can't see is hard to trust. Swagger UI turns the file into an interactive page - every endpoint expandable, every schema documented, and a "Try it out" button that fires real requests from the browser. The fastest way to look at a spec is the official Docker image:

```bash
docker run -p 8080:8080 \
  -e SWAGGER_JSON=/spec/openapi.yaml \
  -v "$(pwd)":/spec \
  swaggerapi/swagger-ui
```

*What just happened:* you mounted your current directory into the container and told Swagger UI to load `openapi.yaml` from it. Open `http://localhost:8080` and the YAML you wrote is now browsable docs. No build step, no framework - the file is the input, the docs are the output. Most API frameworks also ship a plugin that serves Swagger UI directly from your running app at a path like `/docs`.

## Generate a client (or a server)

This is where the contract pays for itself. Point a codegen tool at the spec and it writes a typed client library - so the people calling your API never hand-type a URL or guess a field name again. The widely used open-source generator is `openapi-generator`:

```bash
openapi-generator-cli generate \
  -i openapi.yaml \
  -g typescript-fetch \
  -o ./generated-client
```

*What just happened:* `-i` is the input spec, `-g` is the target generator (`typescript-fetch` here; there are generators for Python, Go, Java, C#, and dozens more), and `-o` is where the code lands. Out comes a client where `createBookmark({ url })` is a real typed function - misspell a field and your compiler complains before you ever run it.

The same tool runs the other direction with a server generator: feed it the spec and it scaffolds route handlers, request models, and the wiring, leaving you to fill in the business logic. The contract decides the shape; you write only the part that's actually yours.

## Design-first vs. code-first

There are two ways the spec and the code relate, and which one you pick shapes your whole workflow.

**Design-first:** you write the OpenAPI document *before* the implementation. The spec is the plan. You can review it, mock it, and hand it to frontend and backend teams who then build against the agreed contract in parallel.

**Code-first:** you write the code first, decorate your handlers with annotations, and a library *generates* the spec from the running app. The code is the source; the spec is a byproduct.

```text
  DESIGN-FIRST                         CODE-FIRST
  write spec ─→ review ─→ build      write code ─→ annotate ─→ generate spec
  spec is the source of truth        code is the source of truth
```

*What just happened:* the arrow direction is the whole difference. Design-first front-loads agreement (great for teams building against each other, or public APIs that need a stable contract). Code-first front-loads shipping (great for a solo backend where the code already exists and you want docs without maintaining a separate file). Neither is wrong; they optimize for different pain.

A blunt rule that holds up: if multiple teams or external consumers depend on the contract, lean design-first - agreeing on the shape before anyone writes code is cheaper than renegotiating after. If it's your own service and your own client, code-first gets you docs with almost no extra effort. The [principles behind a contract worth stabilizing](/guides/designing-apis-that-last) apply either way; OpenAPI is the tool, not the discipline.

## In the wild

Most mature backend frameworks lean code-first by default - you annotate handlers and get a spec and Swagger UI for free at `/docs`. That's the gentle on-ramp. Teams that outgrow it (because the generated spec is awkward to review, or because frontend needs the contract before backend has built it) graduate to design-first, where the spec lives in version control as a reviewed, first-class file. You can start code-first and migrate; many do.

```quiz
[
  {
    "q": "What does `$ref: '#/components/schemas/Bookmark'` do?",
    "choices": [
      "Sends an HTTP request to fetch a bookmark",
      "Inserts the reusable schema named Bookmark defined under components",
      "Imports a schema from an external file on disk",
      "Marks the Bookmark field as required"
    ],
    "answer": 1,
    "explain": "$ref points at a definition elsewhere in the document, letting you define a shape once and reuse it."
  },
  {
    "q": "In design-first, what is the source of truth?",
    "choices": [
      "The running server code, with the spec generated from it",
      "The Swagger UI page",
      "The hand-written OpenAPI spec, written before the implementation",
      "The generated client SDK"
    ],
    "answer": 2,
    "explain": "Design-first means you author the spec first and build code against it; the spec is the plan and the source of truth."
  },
  {
    "q": "How do you express that a request field must always be present?",
    "choices": [
      "Add `required: true` next to the property",
      "List the property name in the object's `required` array",
      "Put the property under `components`",
      "Wrap the property in `allOf`"
    ],
    "answer": 1,
    "explain": "In OpenAPI, `required` is a list at the object level naming which properties must be present - not a per-property flag."
  }
]
```


---

# The Spec at Work in Production

A spec that only renders docs is a brochure. A spec doing real work *enforces* the contract: it rejects bad requests, stands in for the API before the API exists, and fails the build the day the live API stops matching its promise. This is also where teams get burned - usually by trusting a spec that quietly drifted out of sync with reality. Let's wire it up properly and name the traps.

## Validation: reject bad requests at the door

Your spec already says `url` is required and `id` is an integer. A validation middleware reads that and enforces it *before* the request reaches your handler - so your business logic never has to re-check what the contract already guarantees.

```text
POST /bookmarks   { "title": "no url here" }

→ 400 Bad Request
{
  "errors": [
    { "path": "/url", "message": "must have required property 'url'" }
  ]
}
```

*What just happened:* the request never reached your code. The middleware compared the body against the spec's schema, saw `url` missing, and returned a 400 with a precise reason. You wrote the rule once (in the spec) and it's enforced automatically. The same idea runs in reverse - *response* validation in tests catches the day your handler starts returning a shape the spec doesn't promise.

> Validation is only as accurate as the spec. If the spec is generated code-first from the same handlers it's "validating," it can't catch a mismatch - the handler and the rule have the same author. Design-first specs (written independently) make validation a genuine second opinion.

## Mocking: an API before the API exists

Because the spec describes every response shape, a mock server can serve fake-but-valid responses straight from the file - no backend required. Frontend builds against the mock on Monday; backend delivers the real thing on Friday; nothing blocks. Tools like Prism do this:

```bash
prism mock openapi.yaml
# [GET] /bookmarks/1  →  200  { "id": 1, "url": "https://example.com" }
```

*What just happened:* Prism read the spec and started a server that answers every documented endpoint with example data matching the declared schema. The frontend team now has a working API to call before a single handler exists. The contract became a stand-in for the product.

## Contract tests: catch the drift

This is the one that saves you at 3am. Code changes. Specs are forgotten. Six months in, the live API returns `created_at` but the spec still says `createdAt`, and every generated client is subtly wrong. A contract test runs the *real* API against the spec and fails when they disagree.

```bash
schemathesis run --url http://localhost:3000 openapi.yaml
# generates requests from the spec, checks every response against it
# FAILED: GET /bookmarks/1 - response has 'created_at', spec declares 'createdAt'
```

*What just happened:* Schemathesis read the spec, generated real requests for every endpoint, hit the running API, and compared each response against the promised schema. It found the drift a human review would have missed. Wire this into CI and the build goes red the moment code and contract part ways - the spec stays accurate because a machine checks it on every push.

```text
  spec ──┐
         ├─→ contract test ─→ ✅ match  → merge
  API  ──┘                  └─→ ❌ drift → fail CI
```

*What just happened:* the test sits between the spec and the live API and refuses to let them diverge silently. That feedback loop is the difference between a spec that documents your API and a spec that *governs* it.

## Versioning the contract

Your API will change. The question is whether the change breaks the people depending on it. Two moves keep you out of trouble:

- **Bump `info.version` on every meaningful change**, and treat it like any other versioned artifact (semantic versioning works well - major for breaking changes, minor for additive ones).
- **Additive changes are safe; removals and renames are breaking.** Adding an optional field or a new endpoint won't hurt existing clients. Removing a field, renaming one, or making an optional field required *will*. The spec makes these visible in a diff, which is exactly why keeping it in version control matters.

When you must break the contract, the common pattern is a new path prefix (`/v2/bookmarks`) so old and new live side by side while consumers migrate. The deeper reasoning on evolving a contract without breaking callers lives in [the guide on APIs built to last](/guides/designing-apis-that-last); here the point is narrow - your OpenAPI file is the artifact you diff to *see* a breaking change coming.

## The gotchas that actually bite

A short list of what goes wrong, drawn from real pain:

- **The spec drifts and nobody notices.** The number one failure. Without a contract test, a stale spec is worse than no spec - it lies with authority. Automate the check or assume it's wrong.
- **"Try it out" leaks into production.** Swagger UI's live request button is a gift in dev and a footgun in prod if your docs page is public and unauthenticated. Gate it.
- **Over-trusting code-first generation.** Generated specs reflect what the code *does*, including bugs and accidents. They're a description, not a design - review them, don't assume they're correct because a tool produced them.
- **Examples that go stale.** Hand-written `example` values in the spec aren't validated against the schema by default. A bad example misleads every reader. Some linters catch this; turn them on.
- **Treating the spec as write-once.** A spec maintained only at creation rots like any other doc. The whole value proposition collapses the moment it stops matching reality. The tools in this phase exist precisely to keep that from happening.

## In the wild

The teams that get real value from OpenAPI aren't the ones with the prettiest Swagger UI - they're the ones who wired the spec into CI. Validation middleware in the app, a mock server for the frontend, and a contract test in the pipeline. At that point the spec isn't documentation anymore; it's an enforced agreement, and the docs are a free side effect. That's the whole promise of "the API as a spec" actually delivered: write it once, and let machines hold everyone (including future you) to it. For the bigger picture of what makes a [REST API worth describing this carefully](/guides/rest-apis-explained), that guide is the companion to this one.

```quiz
[
  {
    "q": "What does a contract test (e.g. Schemathesis) verify?",
    "choices": [
      "That the spec file is valid YAML",
      "That the running API's real responses still match what the spec promises",
      "That Swagger UI renders without errors",
      "That the generated client compiles"
    ],
    "answer": 1,
    "explain": "Contract tests run the live API against the spec and fail when responses drift from the declared schemas."
  },
  {
    "q": "Which change to an API is safe for existing clients?",
    "choices": [
      "Renaming a response field",
      "Removing an endpoint",
      "Adding a new optional field",
      "Making a previously optional field required"
    ],
    "answer": 2,
    "explain": "Additive changes (new optional fields, new endpoints) don't break callers. Removals, renames, and new requirements do."
  },
  {
    "q": "Why is a mock server generated from the spec useful?",
    "choices": [
      "It replaces the need for a real backend permanently",
      "It serves fake-but-valid responses so consumers can build before the real API exists",
      "It compresses the spec file",
      "It encrypts API traffic"
    ],
    "answer": 1,
    "explain": "A mock serves schema-valid responses straight from the spec, letting frontend and consumers work in parallel with backend."
  }
]
```
