OpenAPI Documentation Expert
Expert in creating comprehensive OpenAPI/Swagger specifications and API documentation aligned with OpenAPI 3.0+ standards.
Core Principles
Specification Standards
-
Use OpenAPI 3.0.3 or 3.1.0
-
Consistent naming conventions (kebab-case for paths, camelCase for properties)
-
Organize endpoints through tags
-
Maintain reusable component schemas
-
Document all response codes
Documentation Quality
-
Provide business logic context
-
Include extensive realistic examples
-
Document all error scenarios
-
Define rate-limiting specifications
-
Explicit data format definitions
OpenAPI 3.0 Structure
Basic Specification
openapi: "3.0.3" info: title: "User Management API" description: | REST API for managing users in the platform.
## Authentication
All endpoints require Bearer token authentication.
## Rate Limiting
- Standard: 100 requests/minute
- Premium: 1000 requests/minute
## Versioning
API version is included in the URL path (/v1/).
version: "1.0.0" contact: name: "API Support" email: "api-support@example.com" url: "https://developer.example.com/support" license: name: "Apache 2.0" url: "https://www.apache.org/licenses/LICENSE-2.0" termsOfService: "https://example.com/terms"
servers:
- url: "https://api.example.com/v1" description: "Production server"
- url: "https://staging-api.example.com/v1" description: "Staging server"
- url: "http://localhost:3000/v1" description: "Development server"
tags:
- name: "users" description: "User management operations"
- name: "authentication" description: "Authentication and authorization"
- name: "admin" description: "Administrative operations"
security:
- bearerAuth: []
Path Documentation
paths: /users: get: operationId: "listUsers" tags: - "users" summary: "List all users" description: | Retrieve a paginated list of users.
Results can be filtered by status and sorted by various fields.
Pagination is cursor-based for optimal performance.
parameters:
- $ref: "#/components/parameters/PageSize"
- $ref: "#/components/parameters/PageCursor"
- name: "status"
in: "query"
description: "Filter by user status"
required: false
schema:
type: "string"
enum: ["active", "inactive", "pending"]
default: "active"
- name: "sort"
in: "query"
description: "Sort field and direction"
required: false
schema:
type: "string"
enum: ["created_at:asc", "created_at:desc", "name:asc", "name:desc"]
default: "created_at:desc"
responses:
"200":
description: "Successful response"
content:
application/json:
schema:
$ref: "#/components/schemas/UserListResponse"
examples:
success:
$ref: "#/components/examples/UserListSuccess"
headers:
X-RateLimit-Limit:
$ref: "#/components/headers/X-RateLimit-Limit"
X-RateLimit-Remaining:
$ref: "#/components/headers/X-RateLimit-Remaining"
"400":
$ref: "#/components/responses/BadRequest"
"401":
$ref: "#/components/responses/Unauthorized"
"429":
$ref: "#/components/responses/TooManyRequests"
post:
operationId: "createUser"
tags:
- "users"
summary: "Create a new user"
description: |
Create a new user account.
A verification email will be sent to the provided email address.
The user must verify their email before they can log in.
requestBody:
required: true
content:
application/json:
schema:
$ref: "#/components/schemas/CreateUserRequest"
examples:
basic:
summary: "Basic user creation"
value:
email: "user@example.com"
name: "John Doe"
password: "SecurePassword123!"
withProfile:
summary: "User with profile data"
value:
email: "user@example.com"
name: "John Doe"
password: "SecurePassword123!"
profile:
bio: "Software developer"
location: "San Francisco, CA"
responses:
"201":
description: "User created successfully"
content:
application/json:
schema:
$ref: "#/components/schemas/User"
example:
id: "usr_1234567890"
email: "user@example.com"
name: "John Doe"
status: "pending"
createdAt: "2024-03-15T10:30:00Z"
headers:
Location:
description: "URL of the created user"
schema:
type: "string"
format: "uri"
example: "/users/usr_1234567890"
"400":
$ref: "#/components/responses/BadRequest"
"409":
description: "User already exists"
content:
application/json:
schema:
$ref: "#/components/schemas/Error"
example:
code: "USER_EXISTS"
message: "A user with this email already exists"
"422":
$ref: "#/components/responses/ValidationError"
/users/{userId}: parameters: - $ref: "#/components/parameters/UserId"
get:
operationId: "getUser"
tags:
- "users"
summary: "Get user by ID"
description: "Retrieve detailed information about a specific user"
responses:
"200":
description: "Successful response"
content:
application/json:
schema:
$ref: "#/components/schemas/User"
"404":
$ref: "#/components/responses/NotFound"
patch:
operationId: "updateUser"
tags:
- "users"
summary: "Update user"
description: |
Partially update a user's information.
Only the fields provided in the request body will be updated.
To remove a field, set it to null explicitly.
requestBody:
required: true
content:
application/json:
schema:
$ref: "#/components/schemas/UpdateUserRequest"
responses:
"200":
description: "User updated successfully"
content:
application/json:
schema:
$ref: "#/components/schemas/User"
"400":
$ref: "#/components/responses/BadRequest"
"404":
$ref: "#/components/responses/NotFound"
delete:
operationId: "deleteUser"
tags:
- "users"
summary: "Delete user"
description: |
Permanently delete a user account.
This action cannot be undone. All associated data will be removed.
responses:
"204":
description: "User deleted successfully"
"404":
$ref: "#/components/responses/NotFound"
Components
components: schemas: User: type: "object" description: "User account information" required: - "id" - "email" - "name" - "status" - "createdAt" properties: id: type: "string" description: "Unique user identifier" pattern: "^usr_[a-zA-Z0-9]{10}$" example: "usr_1234567890" readOnly: true email: type: "string" format: "email" description: "User's email address" example: "user@example.com" name: type: "string" description: "User's display name" minLength: 1 maxLength: 100 example: "John Doe" status: type: "string" description: "Account status" enum: ["active", "inactive", "pending", "suspended"] example: "active" profile: $ref: "#/components/schemas/UserProfile" createdAt: type: "string" format: "date-time" description: "Account creation timestamp" readOnly: true example: "2024-03-15T10:30:00Z" updatedAt: type: "string" format: "date-time" description: "Last update timestamp" readOnly: true example: "2024-03-15T10:30:00Z"
UserProfile:
type: "object"
description: "Extended user profile information"
properties:
bio:
type: "string"
description: "User biography"
maxLength: 500
example: "Software developer passionate about APIs"
location:
type: "string"
description: "User's location"
maxLength: 100
example: "San Francisco, CA"
avatarUrl:
type: "string"
format: "uri"
description: "URL to user's avatar image"
example: "https://cdn.example.com/avatars/usr_123.jpg"
timezone:
type: "string"
description: "User's timezone"
example: "America/Los_Angeles"
CreateUserRequest:
type: "object"
description: "Request body for creating a new user"
required:
- "email"
- "name"
- "password"
properties:
email:
type: "string"
format: "email"
description: "User's email address"
example: "user@example.com"
name:
type: "string"
description: "User's display name"
minLength: 1
maxLength: 100
example: "John Doe"
password:
type: "string"
format: "password"
description: "User's password"
minLength: 8
maxLength: 128
pattern: "^(?=.*[a-z])(?=.*[A-Z])(?=.*\\d)(?=.*[@$!%*?&])[A-Za-z\\d@$!%*?&]{8,}$"
example: "SecurePassword123!"
profile:
$ref: "#/components/schemas/UserProfile"
UpdateUserRequest:
type: "object"
description: "Request body for updating a user"
properties:
name:
type: "string"
description: "User's display name"
minLength: 1
maxLength: 100
nullable: true
profile:
$ref: "#/components/schemas/UserProfile"
UserListResponse:
type: "object"
description: "Paginated list of users"
required:
- "data"
- "pagination"
properties:
data:
type: "array"
items:
$ref: "#/components/schemas/User"
pagination:
$ref: "#/components/schemas/Pagination"
Pagination:
type: "object"
description: "Pagination metadata"
required:
- "total"
- "hasMore"
properties:
total:
type: "integer"
description: "Total number of items"
example: 150
hasMore:
type: "boolean"
description: "Whether more items exist"
example: true
nextCursor:
type: "string"
description: "Cursor for next page"
example: "eyJpZCI6MTAwfQ=="
prevCursor:
type: "string"
description: "Cursor for previous page"
example: "eyJpZCI6NTB9"
Error:
type: "object"
description: "Error response"
required:
- "code"
- "message"
properties:
code:
type: "string"
description: "Machine-readable error code"
example: "VALIDATION_ERROR"
message:
type: "string"
description: "Human-readable error message"
example: "The request body is invalid"
details:
type: "array"
description: "Detailed error information"
items:
type: "object"
properties:
field:
type: "string"
description: "Field that caused the error"
example: "email"
message:
type: "string"
description: "Error message for this field"
example: "Must be a valid email address"
requestId:
type: "string"
description: "Request ID for support"
example: "req_abc123xyz"
timestamp:
type: "string"
format: "date-time"
description: "Error timestamp"
example: "2024-03-15T10:30:00Z"
parameters: UserId: name: "userId" in: "path" description: "User ID" required: true schema: type: "string" pattern: "^usr_[a-zA-Z0-9]{10}$" example: "usr_1234567890"
PageSize:
name: "limit"
in: "query"
description: "Number of items per page"
required: false
schema:
type: "integer"
minimum: 1
maximum: 100
default: 20
PageCursor:
name: "cursor"
in: "query"
description: "Pagination cursor"
required: false
schema:
type: "string"
headers: X-RateLimit-Limit: description: "Request limit per minute" schema: type: "integer" example: 100
X-RateLimit-Remaining:
description: "Remaining requests in current window"
schema:
type: "integer"
example: 95
X-RateLimit-Reset:
description: "Unix timestamp when limit resets"
schema:
type: "integer"
example: 1710500000
responses: BadRequest: description: "Bad request" content: application/json: schema: $ref: "#/components/schemas/Error" example: code: "BAD_REQUEST" message: "The request could not be processed" requestId: "req_abc123xyz"
Unauthorized:
description: "Authentication required"
content:
application/json:
schema:
$ref: "#/components/schemas/Error"
example:
code: "UNAUTHORIZED"
message: "Authentication credentials are missing or invalid"
NotFound:
description: "Resource not found"
content:
application/json:
schema:
$ref: "#/components/schemas/Error"
example:
code: "NOT_FOUND"
message: "The requested resource was not found"
ValidationError:
description: "Validation error"
content:
application/json:
schema:
$ref: "#/components/schemas/Error"
example:
code: "VALIDATION_ERROR"
message: "Request validation failed"
details:
- field: "email"
message: "Must be a valid email address"
- field: "password"
message: "Must be at least 8 characters"
TooManyRequests:
description: "Rate limit exceeded"
headers:
Retry-After:
description: "Seconds until rate limit resets"
schema:
type: "integer"
content:
application/json:
schema:
$ref: "#/components/schemas/Error"
example:
code: "RATE_LIMIT_EXCEEDED"
message: "Too many requests. Please try again later."
securitySchemes: bearerAuth: type: "http" scheme: "bearer" bearerFormat: "JWT" description: | JWT authentication token.
Include in the Authorization header:
`Authorization: Bearer <token>`
Tokens expire after 1 hour. Use the refresh token endpoint
to obtain a new access token.
apiKey:
type: "apiKey"
in: "header"
name: "X-API-Key"
description: "API key for server-to-server communication"
examples: UserListSuccess: summary: "Successful user list" value: data: - id: "usr_1234567890" email: "user1@example.com" name: "John Doe" status: "active" createdAt: "2024-03-15T10:30:00Z" - id: "usr_0987654321" email: "user2@example.com" name: "Jane Smith" status: "active" createdAt: "2024-03-14T09:00:00Z" pagination: total: 150 hasMore: true nextCursor: "eyJpZCI6MTAwfQ=="
Advanced Features
Webhooks (OpenAPI 3.1)
webhooks: userCreated: post: operationId: "userCreatedWebhook" summary: "User created event" description: | Triggered when a new user account is created.
Your endpoint must respond with a 2xx status code within 30 seconds.
Failed deliveries will be retried up to 5 times with exponential backoff.
requestBody:
required: true
content:
application/json:
schema:
type: "object"
required:
- "event"
- "timestamp"
- "data"
properties:
event:
type: "string"
const: "user.created"
timestamp:
type: "string"
format: "date-time"
data:
$ref: "#/components/schemas/User"
responses:
"200":
description: "Webhook received successfully"
security:
- webhookSignature: []
userUpdated: post: operationId: "userUpdatedWebhook" summary: "User updated event" description: "Triggered when a user's information is modified" requestBody: required: true content: application/json: schema: type: "object" properties: event: type: "string" const: "user.updated" timestamp: type: "string" format: "date-time" data: type: "object" properties: user: $ref: "#/components/schemas/User" changes: type: "object" description: "Fields that were changed" responses: "200": description: "Webhook received successfully"
Custom Extensions
x-code-samples:
-
lang: "curl" label: "cURL" source: | curl -X GET "https://api.example.com/v1/users"
-H "Authorization: Bearer your_token_here"
-H "Accept: application/json" -
lang: "javascript" label: "JavaScript" source: | const response = await fetch('https://api.example.com/v1/users', { headers: { 'Authorization': 'Bearer your_token_here', 'Accept': 'application/json' } }); const users = await response.json();
-
lang: "python" label: "Python" source: | import requests
response = requests.get( 'https://api.example.com/v1/users', headers={'Authorization': 'Bearer your_token_here'} ) users = response.json()
x-rate-limiting: standard: limit: 100 window: "1 minute" premium: limit: 1000 window: "1 minute"
x-changelog:
- version: "1.0.0"
date: "2024-03-15"
changes:
- "Initial release"
- version: "1.1.0"
date: "2024-04-01"
changes:
- "Added user profile endpoints"
- "Added pagination support"
Validation & Quality
Schema Validation Rules
validation_patterns: strings: email: format: "email" maxLength: 254
uuid:
format: "uuid"
pattern: "^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$"
phone:
pattern: "^\\+[1-9]\\d{1,14}$"
url:
format: "uri"
pattern: "^https?://"
numbers: positive_integer: type: "integer" minimum: 1
percentage:
type: "number"
minimum: 0
maximum: 100
currency:
type: "number"
multipleOf: 0.01
minimum: 0
dates: date_only: type: "string" format: "date" pattern: "^\d{4}-\d{2}-\d{2}$"
datetime:
type: "string"
format: "date-time"
arrays: non_empty_array: type: "array" minItems: 1
unique_array:
type: "array"
uniqueItems: true
Linting Configuration
.spectral.yaml
extends: ["spectral:oas"]
rules:
Naming conventions
operation-operationId-valid-in-url: true path-keys-no-trailing-slash: true
Documentation requirements
operation-description: true operation-tag-defined: true info-contact: true
Schema quality
oas3-schema: true typed-enum: true
Custom rules
operation-summary-length: description: "Operation summary should be concise" severity: warn given: "$.paths.*[get,post,put,patch,delete]" then: field: "summary" function: length functionOptions: max: 80
must-have-examples: description: "Responses should have examples" severity: warn given: "$.paths...responses..content." then: field: "examples" function: truthy
Code Generation
Generator Configuration
openapi-generator-cli.yaml
$schema: https://raw.githubusercontent.com/OpenAPITools/openapi-generator-cli/master/schema.json
spaces: 2
generators: typescript-axios: inputSpec: ./api/openapi.yaml output: ./generated/typescript-client generatorName: typescript-axios additionalProperties: npmName: "@example/api-client" supportsES6: true withInterfaces: true withSeparateModelsAndApi: true
python-client: inputSpec: ./api/openapi.yaml output: ./generated/python-client generatorName: python additionalProperties: packageName: "example_api_client" projectName: "example-api-client"
go-server: inputSpec: ./api/openapi.yaml output: ./generated/go-server generatorName: go-server additionalProperties: packageName: "api" serverPort: 8080
SDK Generation Script
#!/bin/bash
generate-sdks.sh
SPEC_FILE="./api/openapi.yaml" OUTPUT_DIR="./generated"
Validate spec first
npx @redocly/cli lint $SPEC_FILE
if [ $? -ne 0 ]; then echo "Spec validation failed" exit 1 fi
Generate TypeScript client
npx openapi-generator-cli generate
-i $SPEC_FILE
-g typescript-axios
-o $OUTPUT_DIR/typescript
--additional-properties=npmName=@example/api-client,supportsES6=true
Generate Python client
npx openapi-generator-cli generate
-i $SPEC_FILE
-g python
-o $OUTPUT_DIR/python
--additional-properties=packageName=example_api
echo "SDK generation complete"
Documentation Tools
Redoc Configuration
redoc.yaml
openapi: "./api/openapi.yaml" output: "./docs/index.html"
options: theme: colors: primary: main: "#1976d2" typography: fontSize: "15px" fontFamily: "Inter, sans-serif" code: fontSize: "13px" fontFamily: "JetBrains Mono, monospace"
hideDownloadButton: false hideHostname: false pathInMiddlePanel: true requiredPropsFirst: true sortPropsAlphabetically: false hideLoading: false nativeScrollbars: true
jsonSampleExpandLevel: 2 enumSkipQuotes: false showExtensions: true
Swagger UI Configuration
// swagger-ui-config.js const swaggerUiOptions = { dom_id: '#swagger-ui', url: '/api/openapi.yaml',
// Display options deepLinking: true, displayOperationId: false, defaultModelsExpandDepth: 2, defaultModelExpandDepth: 2, displayRequestDuration: true, docExpansion: 'list', filter: true, showExtensions: true, showCommonExtensions: true,
// Try it out configuration tryItOutEnabled: true, supportedSubmitMethods: ['get', 'post', 'put', 'patch', 'delete'],
// Request interceptor for auth
requestInterceptor: (request) => {
const token = localStorage.getItem('api_token');
if (token) {
request.headers.Authorization = Bearer ${token};
}
return request;
},
// Plugins plugins: [ SwaggerUIBundle.plugins.DownloadUrl ],
// Layout layout: "StandaloneLayout", presets: [ SwaggerUIBundle.presets.apis, SwaggerUIStandalonePreset ] };
Лучшие практики
-
Version everything — используйте семантическое версионирование
-
Examples for all — добавляйте реалистичные примеры для всех схем
-
Error documentation — документируйте все возможные коды ошибок
-
Consistent naming — kebab-case для paths, camelCase для properties
-
Reusable components — выносите общие схемы в components
-
Validate specs — используйте линтеры (Spectral, Redocly)