OpenAPI Specification

A standard format for describing REST APIs, enabling documentation and code generation.

OpenAPI Specification (formerly known as Swagger) is a standard way to describe REST APIs. Think of it as a blueprint for your API - it defines every endpoint, what parameters they accept, what responses they return, and how authentication works. This blueprint is machine-readable, which means tools can automatically generate documentation, client libraries, and even mock servers from it.

Why Use OpenAPI?

Without a spec, API knowledge lives in developers' heads or scattered docs. With OpenAPI:

For API consumers:

  • Interactive documentation where you can try endpoints
  • Auto-generated client libraries in any language
  • Know exactly what to send and what you'll receive

For API developers:

  • Design API before writing code (API-first approach)
  • Auto-generate server stubs
  • Validate requests/responses automatically
  • Keep docs in sync with code

For teams:

  • Single source of truth for the API contract
  • Frontend and backend can work in parallel
  • Clear communication between teams

Spec Structure

An OpenAPI document has these main sections:

SectionPurposeExample
infoAPI metadataTitle, version, description
serversBase URLshttps://api.example.com/v1
pathsEndpoints/users, /users/{id}
componentsReusable definitionsSchemas, parameters, responses
securityAuth methodsAPI key, OAuth2, JWT
tagsGroup endpointsUsers, Orders, Products

Defining Endpoints

Each path defines available operations:

  • Path: The URL pattern (/users/{id})
  • Method: HTTP method (GET, POST, etc.)
  • Parameters: Path, query, header params
  • Request body: What to send (for POST/PUT)
  • Responses: What comes back (200, 400, 404, etc.)

Parameters have types:

  • path: Part of URL (/users/{id})
  • query: After ? (/users?status=active)
  • header: HTTP headers (Authorization)
  • cookie: Cookie values

Schemas (Data Models)

Schemas define the shape of your data using JSON Schema:

  • type: string, number, integer, boolean, array, object
  • properties: Object fields
  • required: Which fields are mandatory
  • enum: Allowed values
  • format: Specific formats (email, date-time, uuid)

You define schemas once in components/schemas and reference them everywhere with $ref.

Tools Ecosystem

ToolPurpose
Swagger UIInteractive documentation
Swagger EditorWrite and validate specs
OpenAPI GeneratorGenerate client/server code in 50+ languages
PrismMock server from spec
StoplightVisual API design
RedocBeautiful documentation

Versions

VersionStatusNotes
2.0LegacyCalled "Swagger", JSON only
3.0CurrentYAML support, better reusability
3.1LatestFull JSON Schema compatibility

Use 3.0 or 3.1 for new projects. The differences are minor but 3.1 has better JSON Schema alignment.

Common Mistakes

1. Not using $ref for reusability Don't copy-paste the same schema everywhere. Define once, reference everywhere.

2. Missing error responses Document 400, 401, 404, 500 responses. Clients need to know what errors to expect.

3. Vague descriptions "Get user" doesn't help. "Retrieves a user by their unique ID. Returns 404 if not found." does.

4. Not versioning the spec Include version in info and update it with API changes.

5. Spec doesn't match implementation Use tools that validate your API against the spec. Drift causes bugs.

Best Practices

Design first, code second: Write the OpenAPI spec before implementing. This catches design issues early and lets frontend work start immediately.

Use semantic versioning: 1.0.01.1.0 for new endpoints, 2.0.0 for breaking changes.

Add examples: Include example values for every field. Makes docs useful and enables better mocking.

Group with tags: Use tags to organize endpoints by resource (Users, Products, Orders).

Keep it DRY: Put common parameters, responses, and schemas in components. Reference them with $ref.

Code Examples

OpenAPI 3.0 Example

openapi: 3.0.3
info:
  title: Users API
  version: 1.0.0
  description: API for managing users

servers:
  - url: https://api.example.com/v1

paths:
  /users:
    get:
      summary: List all users
      tags: [Users]
      parameters:
        - name: status
          in: query
          schema:
            type: string
            enum: [active, inactive]
      responses:
        '200':
          description: List of users
          content:
            application/json:
              schema:
                type: array
                items:
                  $ref: '#/components/schemas/User'

    post:
      summary: Create a user
      tags: [Users]
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/CreateUser'
      responses:
        '201':
          description: User created
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/User'
        '400':
          description: Invalid input

components:
  schemas:
    User:
      type: object
      properties:
        id:
          type: integer
        name:
          type: string
        email:
          type: string
          format: email
      required: [id, name, email]

    CreateUser:
      type: object
      properties:
        name:
          type: string
          minLength: 1
        email:
          type: string
          format: email
      required: [name, email]