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.