| |

REST API 22 ๐ŸŒ API Documentation with OpenAPI and Swagger

โฑ๏ธ Estimated read: 16 min | |

API documentation has a reputation problem: it is either absent, outdated, or maintained by hand until the first endpoint change makes it wrong. The OpenAPI Specification exists to solve this by making the API description a machine-readable artifact. The specificationโ€”a YAML or JSON fileโ€”describes every endpoint, parameter, request body, response schema, and authentication method. Tools then consume that file to render interactive documentation, generate client SDKs, and validate contracts in CI . Swagger is the toolset built around that specification: Swagger Editor for writing and validating the spec, Swagger UI for rendering it as interactive docs, and Swagger Codegen for generating code from it .

For the REST API series, the distinction between OpenAPI and Swagger is foundational. OpenAPI is the standard. Swagger is the implementation. Confusing the two leads to conversations where “Swagger file” and “OpenAPI spec” are used interchangeably, which obscures the fact that the spec can be consumed by tools that have nothing to do with Swaggerโ€”Redoc, Stoplight Elements, Postman, and API gateways all read the same OpenAPI document . This chapter covers the specification structure, the code-first and design-first approaches to producing it, the rendering tools, and the CI integration that keeps documentation from drifting.

Key point: The OpenAPI document is the contract. Documentation, client libraries, and contract tests are all derived from it. Hand-editing the rendered documentation instead of the spec guarantees drift; editing the spec and regenerating guarantees consistency.


Why OpenAPI and Swagger exist

The documentation drift problem. Hand-written API documentation is a snapshot. The moment an endpoint changesโ€”a field is renamed, a status code is adjusted, a query parameter is addedโ€”the documentation becomes misleading. OpenAPI solves this by making the specification the single source of truth. The rendered documentation is generated from the spec, so a change to the spec propagates to every consumer automatically . The discipline required is not “update the docs”; it is “update the spec.”

The tool fragmentation problem. Before OpenAPI, every API documentation tool used its own format. A specification written for one tool could not be read by another. The OpenAPI Specification standardized the description format, which allowed an ecosystem of tools to emerge: editors, renderers, code generators, mock servers, and contract validators all reading the same file . This standardization is why a team can switch from Swagger UI to Redoc without rewriting their API description.

The code-first versus design-first problem. Some teams write the specification first and generate server stubs from it. Others write the code first and generate the specification from annotations. Both approaches produce an OpenAPI document, but they differ in where the contract lives. Design-first treats the spec as the authoritative artifact and the code as an implementation of it. Code-first treats the code as authoritative and the spec as a derived artifact . Neither is universally correct; the choice depends on whether the API is being designed for external consumers or documented after the fact.

The validation problem. A specification that is syntactically valid can still be semantically wrong: a required field missing from a schema, a response code not documented, an authentication requirement absent from an endpoint. OpenAPI linters catch these issues in CI before the spec is published . Without automated validation, the specification becomes another artifact that is assumed to be correct until a consumer discovers it is not.


a. The OpenAPI Document Structure

An OpenAPI document has a small number of top-level sections. The openapi field declares the specification version. The info object contains the title, version, and description. The servers array lists the base URLs. The paths object is where endpoints are defined. The components object holds reusable schemas, security schemes, and parameters .

# A minimal OpenAPI 3.0 document
openapi: 3.0.3
info:
  title: Orders API
  version: 1.0.0
  description: API for managing customer orders

servers:
  - url: https://api.example.com/v1
    description: Production
  - url: http://localhost:3000/v1
    description: Development

The paths object maps URL paths to operations. Each operation is identified by an HTTP method and includes a summary, parameters, request body, and responses. A response must document its status code, description, and content schema .

paths:
  /orders/{id}:
    get:
      summary: Get an order by ID
      operationId: getOrder
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: string
      responses:
        '200':
          description: The order
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Order'
        '404':
          description: Order not found

The $ref syntax in the response schema points to a reusable definition in components/schemas. This is how OpenAPI avoids duplication: a schema defined once can be referenced from every endpoint that returns that shape .

components:
  schemas:
    Order:
      type: object
      required: [id, total, status]
      properties:
        id:
          type: string
        total:
          type: number
          format: decimal
        status:
          type: string
          enum: [pending, confirmed, shipped]

The version field at the root is the authoritative indicator of which specification version the document uses. A file named swagger.yaml can contain OpenAPI 3.x, and a file named openapi.json can contain version 2.0. The swagger: "2.0" field indicates version 2.0; the openapi: 3.x.x field indicates version 3.x .


b. Producing the Specification: Code-First and Design-First

The specification can be produced in two directions. In the design-first approach, the OpenAPI document is written before the code, and the code implements it. In the code-first approach, annotations in the code generate the specification.

FastAPI is the clearest example of code-first. It generates an OpenAPI document automatically from Pydantic models and endpoint decorators. The specification is available at /openapi.json and rendered at /docs by default . The schema for a request body comes from the Pydantic model, and the response schema comes from the response_model argument.

# FastAPI generates OpenAPI from type hints and Pydantic models
from fastapi import FastAPI, Path, Query
from pydantic import BaseModel, Field

app = FastAPI(title="Orders API", version="1.0.0")

class Order(BaseModel):
    id: str = Field(..., description="Order identifier")
    total: float = Field(..., description="Order total")
    status: str = Field(..., description="Order status")

@app.get("/orders/{order_id}", response_model=Order)
async def get_order(
    order_id: str = Path(..., description="Order ID")
):
    """Retrieve a single order by its identifier."""
    return {"id": order_id, "total": 99.99, "status": "confirmed"}

In an Express application, the code-first approach usually involves JSDoc annotations parsed by swagger-jsdoc, or a dedicated tool that analyzes routes and validation schemas. The specification is assembled from annotations scattered across route files and combined with a base definition .

// Express route with JSDoc annotations for swagger-jsdoc
/**
 * @openapi
 * /orders/{id}:
 *   get:
 *     summary: Get an order by ID
 *     parameters:
 *       - in: path
 *         name: id
 *         required: true
 *         schema:
 *           type: string
 *     responses:
 *       200:
 *         description: The order
 *         content:
 *           application/json:
 *             schema:
 *               $ref: '#/components/schemas/Order'
 */
app.get('/orders/:id', (req, res) => { /* ... */ });

The design-first approach inverts this. The specification is written in a text editor or Swagger Editor, validated, and then used to generate server stubs or client libraries. Swagger Codegen and OpenAPI Generator both accept a specification and produce code in dozens of languages . The specification is committed to the repository and reviewed like code; changes to the API contract are changes to the spec, not changes to annotations in scattered source files.

The practical trade-off is visibility versus control. Code-first keeps the specification close to the implementation, which makes it likely to stay current. Design-first makes the contract visible before implementation, which allows consumers to review it and catches mismatches earlier . Many teams use a hybrid: design the spec first, then implement against it, and use code-first generation to verify that the implementation and the spec have not diverged.


c. Rendering and CI Integration

A specification is only useful if it is rendered where consumers can read it. Swagger UI is the interactive renderer: it displays every endpoint, allows readers to expand parameters and schemas, and includes a “Try it out” console that sends live requests . Redoc is the read-only alternative: it produces a clean, three-panel reference site that many teams prefer for public documentation because it loads faster and emphasizes readability over interactivity .

<!-- Embedding Swagger UI in a page -->
<link rel="stylesheet" href="https://unpkg.com/swagger-ui-dist/swagger-ui.css">
<div id="swagger"></div>
<script src="https://unpkg.com/swagger-ui-dist/swagger-ui-bundle.js"></script>
<script>
  SwaggerUIBundle({ url: "/openapi.yaml", dom_id: "#swagger" });
</script>
<!-- Embedding Redoc in a page -->
<redoc spec-url="/openapi.yaml"></redoc>
<script src="https://cdn.redoc.ly/redoc/latest/bundles/redoc.standalone.js"></script>

The choice between them is about audience. Swagger UI is for developers integrating against the API who need to send test requests. Redoc is for readers who need to understand the API structure without making calls . Both read the same specification, so switching later is a configuration change, not a rewrite.

CI integration is what prevents the specification from becoming stale. The pipeline should lint the spec on every commit, failing the build if the spec is invalid. The Redocly CLI provides both validation and static documentation generation, which means the docs can be built and deployed as part of the same pipeline that validates the spec .

# GitHub Actions step: validate spec and build docs
- name: Validate OpenAPI spec
  run: npx @redocly/cli lint openapi.yaml
- name: Build static documentation
  run: npx @redocly/cli build-docs openapi.yaml -o public/index.html

The validation step catches broken $ref references, missing required fields in schemas, and undocumented response codes. A specification that passes linting is not necessarily a good specificationโ€”it can still omit authentication requirements or use vague descriptionsโ€”but it is structurally sound. The linting is the floor, not the ceiling.

For specifications that use $ref across multiple files, a bundling step produces a single self-contained document before rendering. The swagger-cli bundle command (or the Redocly CLI equivalent) resolves references and outputs one file that can be served or imported into other tools .

# Bundle a multi-file spec into a single document
npx @redocly/cli bundle openapi.yaml --output dist/openapi.bundle.yaml

Complete Example Session

# ============================================
# PART 1: SPECIFICATION HEADER
# ============================================
# openapi.yaml โ€” the contract for a task management API
openapi: 3.0.3
info:
  title: Task Management API
  version: 1.0.0
  description: |
    API for managing tasks and projects.

    ## Authentication
    All endpoints require a Bearer token.
  contact:
    name: API Support
    email: api@example.com
servers:
  - url: https://api.example.com/v1
    description: Production
  - url: http://localhost:8000/v1
    description: Development


# ============================================
# PART 2: SECURITY SCHEME
# ============================================
components:
  securitySchemes:
    bearerAuth:
      type: http
      scheme: bearer
      bearerFormat: JWT

security:
  - bearerAuth: []


# ============================================
# PART 3: REUSABLE SCHEMA
# ============================================
components:
  schemas:
    Task:
      type: object
      required: [id, title, status]
      properties:
        id:
          type: string
          description: Unique task identifier
        title:
          type: string
          description: Task title
        status:
          type: string
          enum: [pending, in_progress, done]
          description: Current task status
        created_at:
          type: string
          format: date-time


# ============================================
# PART 4: ENDPOINT DEFINITION
# ============================================
paths:
  /tasks:
    get:
      summary: List all tasks
      operationId: listTasks
      parameters:
        - name: status
          in: query
          required: false
          schema:
            type: string
            enum: [pending, in_progress, done]
      responses:
        '200':
          description: A list of tasks
          content:
            application/json:
              schema:
                type: array
                items:
                  $ref: '#/components/schemas/Task'
        '401':
          description: Unauthorized


# ============================================
# PART 5: ENDPOINT WITH REQUEST BODY
# ============================================
  /tasks/{id}:
    get:
      summary: Get a task by ID
      operationId: getTask
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: string
      responses:
        '200':
          description: The task
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Task'
        '404':
          description: Task not found
    patch:
      summary: Update a task
      operationId: updateTask
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: string
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                title:
                  type: string
                status:
                  type: string
                  enum: [pending, in_progress, done]
      responses:
        '200':
          description: Task updated
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Task'


# ============================================
# PART 6: CODE-FIRST GENERATION WITH FASTAPI
# ============================================
# main.py โ€” the same API implemented in FastAPI
from fastapi import FastAPI, Path
from pydantic import BaseModel, Field

app = FastAPI(title="Task Management API", version="1.0.0")

class Task(BaseModel):
    id: str = Field(..., description="Unique task identifier")
    title: str = Field(..., description="Task title")
    status: str = Field(..., description="pending, in_progress, or done")

@app.get("/tasks/{task_id}", response_model=Task)
async def get_task(task_id: str = Path(..., description="Task ID")):
    """Retrieve a task by its identifier."""
    return {"id": task_id, "title": "Example", "status": "pending"}


# ============================================
# PART 7: SWAGGER UI RENDERING
# ============================================
# Serve the spec with Swagger UI using the Docker image
# docker run -p 8080:8080 -e SWAGGER_JSON=/spec/openapi.yaml \
#   -v $(pwd):/spec swaggerapi/swagger-ui
# Open http://localhost:8080 to interact with the API


# ============================================
# PART 8: REDOC STATIC BUILD
# ============================================
# Build a self-contained HTML file with Redoc
# npx @redocly/cli build-docs openapi.yaml -o docs/index.html
# The output can be hosted on any static file server


# ============================================
# PART 9: CI VALIDATION
# ============================================
# .github/workflows/openapi.yml
# - Lint the spec (fails on invalid references or schemas)
# - Build static docs
# - Deploy to GitHub Pages
# npx @redocly/cli lint openapi.yaml
# npx @redocly/cli build-docs openapi.yaml -o public/index.html


# ============================================
# PART 10: CLIENT GENERATION
# ============================================
# Generate a TypeScript client from the spec
# npx @openapitools/openapi-generator-cli generate \
#   -i openapi.yaml -g typescript-fetch -o ./client

The ten parts trace the full documentation workflow: the specification structure, security schemes, reusable schemas, endpoint definitions, code-first generation, rendering with Swagger UI and Redoc, CI validation, and client generation. The specification is the artifact that every other step consumes.


Quick Reference

OpenAPI Document Sections

SectionPurposeRequired
openapiSpecification versionYes
infoTitle, version, descriptionYes
serversBase URLsRecommended
pathsEndpoint definitionsYes
componentsReusable schemas, securityOptional
securityGlobal authentication requirementOptional

Specification Production Approaches

ApproachDirectionTool Examples
Design-firstSpec โ†’ CodeSwagger Editor, OpenAPI Generator
Code-firstCode โ†’ SpecFastAPI, swagger-jsdoc, tsoa

Rendering Tools

ToolStyleBest For
Swagger UIInteractive, “try it out”Developer integration
RedocRead-only, three-panelPublic reference
Stoplight ElementsEmbeddable web componentDocs inside existing app

CI Commands

CommandPurpose
redocly lint openapi.yamlValidate spec structure
redocly build-docsGenerate static HTML
openapi-generator generateProduce client SDK
redocly bundleResolve $ref into single file

Best Practices

โœ… Do This:

# Use $ref for reusable schemas
schema:
  $ref: '#/components/schemas/Order'                              # โœ…
# Document every response code, including errors
responses:
  '404':
    description: Resource not found                                # โœ…
# Declare security requirements per endpoint or globally
security:
  - bearerAuth: []                                                 # โœ…
# Validate the spec in CI on every commit
npx @redocly/cli lint openapi.yaml                                # โœ…
# Let the framework generate the spec from types
@app.get("/users", response_model=UserListResponse)               # โœ…
# Use the root version field to determine spec version
openapi: 3.0.3                                                     # โœ…

โŒ Don’t Do This:

# Hand-write documentation instead of generating from spec
# Rendered docs and spec will drift                                 # โŒ
# Omit error responses from endpoint definitions
responses:
  '200': { description: OK }                                       # โŒ
# Hardcode schemas inline instead of using components
schema:
  type: object
  properties: { ... }                                              # โŒ
# Assume a file named swagger.yaml contains Swagger 2.0
# It may contain OpenAPI 3.x                                          # โŒ
# Leave authentication undocumented
# Consumers cannot integrate without it                             # โŒ
# Skip CI validation and discover broken refs in production
# No lint step in pipeline                                          # โŒ

Common Pitfalls

PitfallWhy It HappensFix
Spec and implementation divergeCode-first without validationGenerate spec from code or add contract tests
Broken $ref linksSchema moved or renamedRun redocly lint in CI
Undocumented error responsesOnly happy path documentedAdd 4xx and 5xx responses
Swagger UI shows “No operations”Spec path wrong or invalidVerify URL and lint spec
File named swagger.yaml contains OpenAPI 3Naming is not indicativeCheck root openapi field
Client generator produces wrong typesSchema definitions incompleteAdd required and format fields

Real-World Examples

1. Minimal Spec Header

openapi: 3.0.3
info:
  title: My API
  version: 1.0.0

2. Endpoint with Path Parameter

/users/{id}:
  get:
    parameters:
      - name: id
        in: path
        required: true
        schema: { type: string }

3. Reusable Schema Reference

schema:
  $ref: '#/components/schemas/User'

4. Security Scheme

components:
  securitySchemes:
    bearerAuth: { type: http, scheme: bearer }

5. Swagger UI Docker

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

6. Redoc Static Build

npx @redocly/cli build-docs openapi.yaml -o index.html

7. CI Lint Step

- run: npx @redocly/cli lint openapi.yaml

8. FastAPI Auto-Generation

app = FastAPI(title="My API", version="1.0.0")
# Spec available at /openapi.json

9. Response Schema

responses:
  '200':
    content:
      application/json:
        schema: { $ref: '#/components/schemas/User' }

10. Client Generation

npx @openapitools/openapi-generator-cli generate \
  -i openapi.yaml -g typescript-fetch -o ./client

Visual

The Specification as Single Source of Truth

โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
โ”‚  openapi.yaml                                                โ”‚
โ”‚                                                              โ”‚
โ”‚  โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”                โ”‚
โ”‚  โ”‚ Paths, schemas, security, responses     โ”‚                โ”‚
โ”‚  โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜                โ”‚
โ”‚           โ”‚                                                  โ”‚
โ”‚           โ”œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”    โ”‚
โ”‚           โ–ผ              โ–ผ              โ–ผ              โ–ผ    โ”‚
โ”‚  โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”โ”‚
โ”‚  โ”‚ Swagger UI  โ”‚ โ”‚ Redoc       โ”‚ โ”‚ Client Gen  โ”‚ โ”‚ Mock   โ”‚โ”‚
โ”‚  โ”‚ Interactive โ”‚ โ”‚ Reference   โ”‚ โ”‚ SDKs        โ”‚ โ”‚ Server โ”‚โ”‚
โ”‚  โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜โ”‚
โ”‚                                                              โ”‚
โ”‚  Every consumer reads the same file.                         โ”‚
โ”‚  A change to the spec propagates to all of them.             โ”‚
โ”‚                                                              โ”‚
โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜

Design-First vs Code-First

โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
โ”‚  DESIGN-FIRST                  CODE-FIRST                    โ”‚
โ”‚                                                              โ”‚
โ”‚  โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”          โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”           โ”‚
โ”‚  โ”‚ Write spec      โ”‚          โ”‚ Write code      โ”‚           โ”‚
โ”‚  โ”‚ (openapi.yaml)  โ”‚          โ”‚ (annotations)   โ”‚           โ”‚
โ”‚  โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜          โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜           โ”‚
โ”‚           โ–ผ                            โ–ผ                     โ”‚
โ”‚  โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”          โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”           โ”‚
โ”‚  โ”‚ Generate server โ”‚          โ”‚ Generate spec   โ”‚           โ”‚
โ”‚  โ”‚ stubs / client  โ”‚          โ”‚ from code       โ”‚           โ”‚
โ”‚  โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜          โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜           โ”‚
โ”‚           โ–ผ                            โ–ผ                     โ”‚
โ”‚  โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”          โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”           โ”‚
โ”‚  โ”‚ Implement       โ”‚          โ”‚ Spec is derived โ”‚           โ”‚
โ”‚  โ”‚ against spec    โ”‚          โ”‚ from code       โ”‚           โ”‚
โ”‚  โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜          โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜           โ”‚
โ”‚                                                              โ”‚
โ”‚  Spec is authoritative.        Code is authoritative.        โ”‚
โ”‚  Better for external APIs.     Better for internal APIs.     โ”‚
โ”‚                                                              โ”‚
โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜

OpenAPI Document Structure

โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
โ”‚  openapi: 3.0.3              โ† Spec version                  โ”‚
โ”‚                                                              โ”‚
โ”‚  info:                                                       โ”‚
โ”‚    title: Orders API         โ† Human-readable metadata       โ”‚
โ”‚    version: 1.0.0                                            โ”‚
โ”‚                                                              โ”‚
โ”‚  servers:                                                    โ”‚
โ”‚    - url: https://api...     โ† Base URLs                     โ”‚
โ”‚                                                              โ”‚
โ”‚  paths:                                                      โ”‚
โ”‚    /orders:                  โ† Endpoint definitions          โ”‚
โ”‚      get:                                                    โ”‚
โ”‚        responses:                                            โ”‚
โ”‚          '200':                                              โ”‚
โ”‚            content:                                          โ”‚
โ”‚              application/json:                               โ”‚
โ”‚                schema:                                       โ”‚
โ”‚                  $ref: '#/components/schemas/Order'          โ”‚
โ”‚                                                              โ”‚
โ”‚  components:                                                 โ”‚
โ”‚    schemas:                  โ† Reusable definitions          โ”‚
โ”‚      Order: { ... }                                          โ”‚
โ”‚    securitySchemes:                                          โ”‚
โ”‚      bearerAuth: { ... }                                     โ”‚
โ”‚                                                              โ”‚
โ”‚  $ref connects paths to components.                          โ”‚
โ”‚  The spec is one document with internal references.          โ”‚
โ”‚                                                              โ”‚
โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜

CI Validation Pipeline

โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
โ”‚  PUSH TO BRANCH                                              โ”‚
โ”‚                                                              โ”‚
โ”‚  โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”                                        โ”‚
โ”‚  โ”‚ Lint spec       โ”‚  redocly lint                          โ”‚
โ”‚  โ”‚                 โ”‚  Fails on broken refs, invalid schema  โ”‚
โ”‚  โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜                                        โ”‚
โ”‚           โ”‚ PASS                                             โ”‚
โ”‚           โ–ผ                                                  โ”‚
โ”‚  โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”                                        โ”‚
โ”‚  โ”‚ Build docs      โ”‚  redocly build-docs                    โ”‚
โ”‚  โ”‚                 โ”‚  Static HTML output                    โ”‚
โ”‚  โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜                                        โ”‚
โ”‚           โ”‚                                                  โ”‚
โ”‚           โ–ผ                                                  โ”‚
โ”‚  โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”                                        โ”‚
โ”‚  โ”‚ Deploy docs     โ”‚  GitHub Pages, Netlify, S3             โ”‚
โ”‚  โ”‚                 โ”‚  Published on merge                    โ”‚
โ”‚  โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜                                        โ”‚
โ”‚                                                              โ”‚
โ”‚  Invalid spec blocks the merge.                              โ”‚
โ”‚  Published docs always match the spec.                       โ”‚
โ”‚                                                              โ”‚
โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜

Summary

ItemValue
OpenAPISpecification format for REST APIs (YAML/JSON)
SwaggerToolset implementing OpenAPI (UI, Editor, Codegen)
Root version fieldopenapi: 3.x.x or swagger: "2.0"
Paths objectEndpoint definitions by URL and method
Components objectReusable schemas and security schemes
$refInternal reference to components
Code-first toolFastAPI (Python), swagger-jsdoc (Express)
Interactive rendererSwagger UI
Read-only rendererRedoc
CI validationredocly lint openapi.yaml

Key takeaways:

  • OpenAPI is the standard; Swagger is the toolset. The specification was donated to the OpenAPI Initiative in 2015 and renamed. Swagger refers to the tools that consume itโ€”Swagger UI, Swagger Editor, Swagger Codegen . A “Swagger file” is an OpenAPI document; a “Swagger UI” renders it.
  • The specification is the contract. Documentation, client libraries, and mock servers are all derived from it. Hand-editing rendered docs instead of the spec guarantees drift. The CI pipeline should validate the spec on every commit .
  • Code-first and design-first both produce valid specs. FastAPI generates the spec from type hints and Pydantic models automatically. Design-first writes the spec and generates server stubs from it. The choice depends on whether the API is designed for external consumers or documented after implementation .
  • $ref prevents duplication. Schemas defined once in components are referenced from every endpoint. A change to a schema propagates everywhere it is used, which is exactly what a contract should do.
  • Swagger UI and Redoc serve different audiences. Swagger UI is interactive and includes a “try it out” console for developers integrating against the API. Redoc is read-only and optimized for readability. Both read the same spec, so the choice is about presentation, not capability .
  • Validation must be automated. A syntactically valid spec can still have broken $ref links, missing required fields, or undocumented response codes. A lint step in CI catches these before they reach consumers.

Remember: The OpenAPI document is not documentation; it is the source from which documentation is generated. The distinction matters because it determines where changes are made. When an endpoint changes, the spec changes, and every consumerโ€”Swagger UI, Redoc, client SDKs, contract testsโ€”updates automatically. When the spec is treated as a secondary artifact, it drifts, and the documentation it produces becomes misleading. The tools are interchangeable; the specification is not. Whether the team uses Swagger UI or Redoc, FastAPI or design-first YAML, the contract lives in one place and everything else derives from it. That is the discipline the objective requires.

Was this helpful?


Stop using slow, ad-bloated tool sites! ๐Ÿคฎ

๐Ÿ”Ž Search “KandZ Tools” on Google to use many professional utilities for free.

KandZ.me is the ultimate minimalist hub for:
โœ… Finance (Mortgage, Interest, Inflation)
โœ… Tech (Base64, JSON, Dev Suite, IP)
โœ… Health (BMI, BMR, TDEE)
โœ… Productivity (Timer, Workspace, QR)

โšก๏ธ Fast & Private
๐Ÿ”’ No data leaves your device
๐Ÿ’Ž 100% Free

๐Ÿ”— Use it now: https://tools.kandz.me
๐Ÿ”– Bookmark itโ€”youโ€™ll need it later!