Writing OpenAPI specs to document your REST API completely means treating the spec itself as a first-class citizen, not an afterthought.

Here’s a real-world openapi.yaml snippet for a simple user endpoint:

openapi: 3.0.0
info:
  title: User API
  version: 1.0.0
servers:
  - url: http://api.example.com/v1
paths:
  /users:
    get:
      summary: List all users
      operationId: listUsers
      tags:
        - Users
      parameters:
        - name: limit
          in: query
          schema:
            type: integer
            format: int32
            default: 10
          description: Maximum number of users to return
        - name: offset
          in: query
          schema:
            type: integer
            format: int32
            default: 0
          description: Number of users to skip
      responses:
        '200':
          description: A list of users
          content:
            application/json:
              schema:
                type: array
                items:
                  $ref: '#/components/schemas/User'
        '500':
          description: Internal server error
    post:
      summary: Create a new user
      operationId: createUser
      tags:
        - Users
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/NewUser'
      responses:
        '201':
          description: User created successfully
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/User'
        '400':
          description: Invalid input
components:
  schemas:
    User:
      type: object
      properties:
        id:
          type: string
          format: uuid
          readOnly: true
        username:
          type: string
          example: johndoe
        email:
          type: string
          format: email
          example: john.doe@example.com
      required:
        - username
        - email
    NewUser:
      type: object
      properties:
        username:
          type: string
          example: janedoe
        email:
          type: string
          format: email
          example: jane.doe@example.com
      required:
        - username
        - email

This specification describes the /users endpoint, detailing GET and POST operations. For GET, it specifies optional limit and offset query parameters, both integers defaulting to 10 and 0 respectively. The POST operation requires a requestBody conforming to the NewUser schema and returns a User object on success. The User and NewUser schemas themselves define the structure and types of data exchanged, including examples and required fields.

The core problem OpenAPI solves is the inherent ambiguity and manual effort in understanding and interacting with REST APIs. Without a formal contract, developers rely on documentation that quickly becomes outdated, or they resort to trial-and-error, leading to integration headaches. OpenAPI provides a machine-readable, language-agnostic description that serves as a single source of truth.

Internally, an OpenAPI document is a JSON or YAML object that follows a strict schema. It’s structured into key sections: openapi (version), info (metadata about the API), servers (base URLs), paths (the endpoints and their operations), and components (reusable elements like schemas, parameters, and responses). Each path item maps to an HTTP method (get, post, put, delete, etc.), and within each operation, you define parameters, request bodies, and responses.

The real power comes from the detail. For parameters, you specify name, in (query, path, header, cookie), required, and schema. The schema is where you define the type (string, integer, boolean, object, array), format (like uuid, email, date-time), enum values, minLength, maxLength, minimum, maximum, and importantly, example values. Examples are crucial for quickly understanding expected data.

Responses are defined by their HTTP status code. For each, you specify a description and content which details the media type (e.g., application/json) and the schema of the returned data. Using $ref to point to definitions in the components/schemas section is key for DRY (Don’t Repeat Yourself) and maintainability.

The most surprising thing about OpenAPI is how much it can automate your entire API lifecycle. Beyond just documentation generation, you can use tools to generate client SDKs in dozens of languages, server stubs, and even perform automated testing against the spec. This means a single OpenAPI document can become the engine for generating code for both your API consumers and your API implementation, drastically reducing manual boilerplate and ensuring consistency.

If you’re generating your OpenAPI spec from code annotations, ensure your annotations are comprehensive. Missing descriptions, examples, or explicit schema definitions for request/response bodies are common pitfalls that undermine the "completeness" aspect of the spec.

Want structured learning?

Take the full API Architecture course →