KAHIBARO
Discord Login Register

7.21. OpenAPI and Swagger

Why OpenAPI and Swagger Matter

When you build REST APIs, you eventually need to answer questions like:

You can write this in a README, but it gets out of date very quickly. OpenAPI and Swagger solve this by giving you a standard, machine readable way to describe your API and a set of tools that use that description.

OpenAPI is the specification, Swagger is a tooling ecosystem that works with that specification.

Key idea: OpenAPI describes what your API looks like, in a standard format (YAML or JSON), so both humans and tools can understand it and generate docs, clients, and tests automatically.


What Is OpenAPI?

OpenAPI is a specification for describing HTTP APIs.

An OpenAPI document is usually written in YAML or JSON and contains:

OpenAPI is versioned, for example:

Most modern tools use OpenAPI 3.

Here is a tiny OpenAPI 3 document that describes one endpoint, GET /hello:

yaml
openapi: 3.0.0
info:
  title: Hello API
  version: 1.0.0
paths:
  /hello:
    get:
      summary: Say hello
      responses:
        '200':
          description: Successful response
          content:
            text/plain:
              schema:
                type: string

This says:

Tools can read this and automatically:

What Is Swagger?

Historically, Swagger was the name of both:

The specification part was later renamed to OpenAPI. The tools kept the Swagger name.

So today, when people say "Swagger", they usually mean one of these:

Many backend frameworks integrate with Swagger UI so you get clickable, interactive docs for free.

Important:

  • OpenAPI = the standard format / specification.
  • Swagger = tools that work with OpenAPI, like Swagger UI and Swagger Editor.

The Structure of an OpenAPI Document

An OpenAPI file has a defined structure. Knowing the main sections helps you read or generate them.

Here is a simplified view of the top-level structure in OpenAPI 3:

yaml
openapi: 3.0.0
info:          # General information about the API
servers:       # Base URLs where the API is served
paths:         # All endpoints and operations
components:    # Reusable pieces (schemas, parameters, etc.)
security:      # Global security requirements (optional)
tags:          # Tags to group operations (optional)

Let us break down each part with examples.

Info Section

Describes the API in general.

yaml
info:
  title: Task Management API
  description: Simple API to manage tasks.
  version: 1.0.0
  contact:
    name: API Support
    email: support@example.com

This is used in docs and tools to show metadata.

Servers Section

Defines your base URLs.

yaml
servers:
  - url: https://api.example.com/v1
    description: Production server
  - url: https://staging-api.example.com/v1
    description: Staging server

If you do not specify it, tools might assume http://localhost by default.

Paths Section

This is the core. It lists all endpoints.

Each path has one or more HTTP methods with details.

Example with multiple methods and a path parameter:

yaml
paths:
  /tasks:
    get:
      summary: List tasks
      responses:
        '200':
          description: List of tasks
    post:
      summary: Create a task
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/TaskCreate'
      responses:
        '201':
          description: Task created
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Task'
  /tasks/{task_id}:
    get:
      summary: Get a single task
      parameters:
        - in: path
          name: task_id
          required: true
          schema:
            type: integer
      responses:
        '200':
          description: Task details
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Task'
        '404':
          description: Task not found

Where do bodies, responses, and parameters go?

Example of query and header parameters:

yaml
parameters:
  - in: query
    name: completed
    required: false
    schema:
      type: boolean
    description: Filter by completion status
  - in: header
    name: X-Request-ID
    required: false
    schema:
      type: string

Components Section

components holds reusable parts. This keeps your OpenAPI file clean and avoids repetition.

Most commonly you use:

Example: Defining Schemas

yaml
components:
  schemas:
    Task:
      type: object
      properties:
        id:
          type: integer
        title:
          type: string
        description:
          type: string
          nullable: true
        completed:
          type: boolean
      required:
        - id
        - title
        - completed
    TaskCreate:
      type: object
      properties:
        title:
          type: string
        description:
          type: string
      required:
        - title

You reference these with $ref:

yaml
schema:
  $ref: '#/components/schemas/Task'

This means "use the Task schema defined in components.schemas".

Rule: Use $ref to reuse schemas and avoid duplication. This keeps your API description easier to maintain and more consistent.

Example: Security Schemes

For authentication based on Bearer tokens (like JWT):

yaml
components:
  securitySchemes:
    BearerAuth:
      type: http
      scheme: bearer
      bearerFormat: JWT

Then you can apply it globally:

yaml
security:
  - BearerAuth: []

or per operation:

yaml
paths:
  /tasks:
    get:
      security:
        - BearerAuth: []
      responses:
        '200':
          description: List of tasks

Documenting Endpoints in OpenAPI

Let us look more closely at how to document parameters, request bodies, and responses.

Path and Query Parameters

Example endpoint:

OpenAPI snippet:

yaml
paths:
  /products/{product_id}:
    get:
      summary: Get a product and related items
      parameters:
        - name: product_id
          in: path
          required: true
          schema:
            type: integer
        - name: limit
          in: query
          required: false
          schema:
            type: integer
            default: 10
            minimum: 1
            maximum: 100
      responses:
        '200':
          description: Product and related items

Notice:

Request Body

Example: POST /users with a JSON body.

yaml
paths:
  /users:
    post:
      summary: Create a new user
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/UserCreate'
      responses:
        '201':
          description: User created
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/User'
        '400':
          description: Invalid input

And the schemas:

yaml
components:
  schemas:
    UserCreate:
      type: object
      properties:
        email:
          type: string
          format: email
        password:
          type: string
          minLength: 8
      required:
        - email
        - password
    User:
      type: object
      properties:
        id:
          type: integer
        email:
          type: string
          format: email
      required:
        - id
        - email

Notice the use of:

These are validation hints that tools and frameworks can use.

Responses and Error Models

You can define not only success responses but also structured error responses.

Example: standard error format:

yaml
components:
  schemas:
    ErrorResponse:
      type: object
      properties:
        detail:
          type: string
        code:
          type: string
      required:
        - detail

Use in responses:

yaml
responses:
  '400':
    description: Bad Request
    content:
      application/json:
        schema:
          $ref: '#/components/schemas/ErrorResponse'
  '404':
    description: Not Found
    content:
      application/json:
        schema:
          $ref: '#/components/schemas/ErrorResponse'

This way clients know exactly how errors will look.

Best practice: Define a consistent error schema and reference it for all error status codes. This makes client error handling much simpler.


Swagger UI and Interactive Documentation

Swagger UI is a web page that:

Most modern frameworks, including those you will use later, can serve Swagger UI automatically at a URL like /docs.

Here is what Swagger UI typically shows for an endpoint:

This is extremely useful for:

OpenAPI in a Backend Workflow

Here are common ways OpenAPI fits into the development process.

Design First vs Code First

Two popular approaches:

ApproachDescriptionProsCons
Design FirstWrite the OpenAPI spec before coding the backend.Clear contract, can generate server stubs and clients, good for collaboration.Needs discipline, spec and code must be kept in sync.
Code FirstWrite backend code, let the framework generate OpenAPI from code.Faster to start, less manual spec writing.You design via code, spec is "after the fact".

As a beginner, you will commonly start with code first because:

Later in larger teams, you may also see design first processes.

Code Generation

With an OpenAPI file you can generate:

Example tooling:

The general workflow:

  1. Write or generate openapi.yaml.
  2. Run a generator tool.
  3. Commit generated client code to your frontend repository.

Validation and Testing

Tools can compare real responses against the OpenAPI spec:

This helps catch regressions and undocumented changes.

Some tools can also:

Practical Examples for Beginners

You will often see or work with OpenAPI indirectly through your backend framework. It is useful to recognize what is happening.

Imagine you have these two endpoints in your API:

An OpenAPI representation might look like:

yaml
openapi: 3.0.0
info:
  title: Task API
  version: 1.0.0
paths:
  /tasks:
    get:
      summary: List tasks
      responses:
        '200':
          description: Successful response
          content:
            application/json:
              schema:
                type: array
                items:
                  $ref: '#/components/schemas/Task'
    post:
      summary: Create a task
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/TaskCreate'
      responses:
        '201':
          description: Task created
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Task'
components:
  schemas:
    Task:
      type: object
      properties:
        id:
          type: integer
        title:
          type: string
        completed:
          type: boolean
      required:
        - id
        - title
        - completed
    TaskCreate:
      type: object
      properties:
        title:
          type: string
        completed:
          type: boolean
          default: false
      required:
        - title

From this single file tools can:

js
  const tasks = await api.listTasks();
  const created = await api.createTask({ title: "Study OpenAPI" });

Summary and Key Takeaways

Remember: A well documented API using OpenAPI is easier to use, easier to maintain, and easier to integrate with. Treat your OpenAPI spec as part of your source code, not as an afterthought.

As you move on to frameworks that support OpenAPI automatically, you will see these concepts in action through interactive docs and generated API descriptions.

Views: 8

Comments

Please login to add a comment.

Don't have an account? Register now!