API Documentation Skill
API Documentation Skill
When to Use This Skill
Use this skill when implementing api documentation patterns in your codebase.
How to Use This Skill
- Review the patterns and examples below
- Apply the relevant patterns to your implementation
- Follow the best practices outlined in this skill
Comprehensive API documentation patterns for creating discoverable, interactive, and maintainable API references.
OpenAPI 3.1 Specification
Basic Structure
# openapi.yaml
openapi: 3.1.0
info:
title: User Service API
version: 1.0.0
description: |
User management API for authentication, profiles, and permissions.
## Authentication
All endpoints require Bearer token authentication.
## Rate Limiting
- Standard: 100 requests/minute
- Authenticated: 1000 requests/minute
contact:
name: API Support
email: api@example.com
url: https://developer.example.com
license:
name: Apache 2.0
url: https://www.apache.org/licenses/LICENSE-2.0
servers:
- url: https://api.example.com/v1
description: Production
- url: https://staging-api.example.com/v1
description: Staging
- url: http://localhost:3000/v1
description: Development
tags:
- name: Users
description: User management operations
- name: Auth
description: Authentication endpoints
- name: Admin
description: Administrative operations
paths:
/users:
get:
summary: List all users
description: Retrieve paginated list of users with optional filtering
operationId: listUsers
tags: [Users]
parameters:
- $ref: '#/components/parameters/PageParam'
- $ref: '#/components/parameters/LimitParam'
- name: status
in: query
schema:
type: string
enum: [active, inactive, pending]
responses:
'200':
description: Successful response
content:
application/json:
schema:
$ref: '#/components/schemas/UserList'
'401':
$ref: '#/components/responses/Unauthorized'
post:
summary: Create a user
operationId: createUser
tags: [Users]
requestBody:
required: true
content:
application/json:
schema:
$ref: '#/components/schemas/CreateUser'
examples:
basic:
summary: Basic user
value:
email: user@example.com
name: John Doe
admin:
summary: Admin user
value:
email: admin@example.com
name: Admin User
role: admin
responses:
'201':
description: User created
content:
application/json:
schema:
$ref: '#/components/schemas/User'
/users/{userId}:
get:
summary: Get user by ID
operationId: getUser
tags: [Users]
parameters:
- $ref: '#/components/parameters/UserIdParam'
responses:
'200':
description: User found
content:
application/json:
schema:
$ref: '#/components/schemas/User'
'404':
$ref: '#/components/responses/NotFound'
components:
schemas:
User:
type: object
required: [id, email, name]
properties:
id:
type: string
format: uuid
readOnly: true
email:
type: string
format: email
name:
type: string
minLength: 1
maxLength: 100
role:
type: string
enum: [user, admin, moderator]
default: user
createdAt:
type: string
format: date-time
readOnly: true
example:
id: "123e4567-e89b-12d3-a456-426614174000"
email: "user@example.com"
name: "John Doe"
role: "user"
createdAt: "2024-01-15T10:30:00Z"
CreateUser:
type: object
required: [email, name]
properties:
email:
type: string
format: email
name:
type: string
role:
type: string
enum: [user, admin]
UserList:
type: object
properties:
data:
type: array
items:
$ref: '#/components/schemas/User'
pagination:
$ref: '#/components/schemas/Pagination'
Pagination:
type: object
properties:
total:
type: integer
page:
type: integer
limit:
type: integer
hasMore:
type: boolean
Error:
type: object
required: [code, message]
properties:
code:
type: string
message:
type: string
details:
type: object
parameters:
UserIdParam:
name: userId
in: path
required: true
schema:
type: string
format: uuid
PageParam:
name: page
in: query
schema:
type: integer
minimum: 1
default: 1
LimitParam:
name: limit
in: query
schema:
type: integer
minimum: 1
maximum: 100
default: 20
responses:
Unauthorized:
description: Authentication required
content:
application/json:
schema:
$ref: '#/components/schemas/Error'
example:
code: UNAUTHORIZED
message: Invalid or missing authentication token
NotFound:
description: Resource not found
content:
application/json:
schema:
$ref: '#/components/schemas/Error'
securitySchemes:
BearerAuth:
type: http
scheme: bearer
bearerFormat: JWT
ApiKeyAuth:
type: apiKey
in: header
name: X-API-Key
security:
- BearerAuth: []
Redoc Integration
Static HTML Generation
# Install Redoc CLI
npm install -g @redocly/cli
# Generate static HTML
redocly build-docs openapi.yaml -o docs/api.html
# Preview locally
redocly preview-docs openapi.yaml
React Component
// ApiDocs.tsx
import { RedocStandalone } from 'redoc'
export function ApiDocs() {
return (
<RedocStandalone
specUrl="/api/openapi.yaml"
options={{
theme: {
colors: {
primary: { main: '#3B82F6' }
},
typography: {
fontSize: '15px',
fontFamily: 'Inter, sans-serif',
headings: {
fontFamily: 'Inter, sans-serif'
}
},
sidebar: {
width: '260px'
}
},
hideDownloadButton: false,
expandResponses: '200,201',
requiredPropsFirst: true,
sortPropsAlphabetically: true,
pathInMiddlePanel: true,
jsonSampleExpandLevel: 2
}}
/>
)
}
Redoc Configuration
# redocly.yaml
extends:
- recommended
theme:
openapi:
theme:
colors:
primary:
main: '#3B82F6'
typography:
fontSize: '15px'
fontFamily: 'Inter, sans-serif'
sidebar:
backgroundColor: '#F8FAFC'
rightPanel:
backgroundColor: '#1E293B'
apis:
main:
root: openapi.yaml
rules:
no-unused-components: warn
operation-operationId: error
operation-description: warn
tag-description: warn
Swagger UI Integration
Express.js Setup
// swagger.ts
import swaggerUi from 'swagger-ui-express'
import YAML from 'yamljs'
import { Express } from 'express'
export function setupSwagger(app: Express) {
const swaggerDocument = YAML.load('./openapi.yaml')
const options: swaggerUi.SwaggerUiOptions = {
explorer: true,
customCss: '.swagger-ui .topbar { display: none }',
customSiteTitle: 'API Documentation',
swaggerOptions: {
persistAuthorization: true,
displayRequestDuration: true,
filter: true,
tryItOutEnabled: true
}
}
app.use('/api-docs', swaggerUi.serve, swaggerUi.setup(swaggerDocument, options))
// Serve raw OpenAPI spec
app.get('/api-docs.json', (req, res) => {
res.json(swaggerDocument)
})
}
FastAPI (Python)
# main.py
from fastapi import FastAPI
from fastapi.openapi.docs import get_swagger_ui_html, get_redoc_html
from fastapi.openapi.utils import get_openapi
app = FastAPI(
title="User Service API",
version="1.0.0",
description="User management API",
docs_url=None, # Disable default
redoc_url=None
)
# Custom Swagger UI
@app.get("/docs", include_in_schema=False)
async def custom_swagger_ui():
return get_swagger_ui_html(
openapi_url="/openapi.json",
title=f"{app.title} - Swagger UI",
swagger_js_url="https://cdn.jsdelivr.net/npm/swagger-ui-dist@5/swagger-ui-bundle.js",
swagger_css_url="https://cdn.jsdelivr.net/npm/swagger-ui-dist@5/swagger-ui.css",
)
# Custom Redoc
@app.get("/redoc", include_in_schema=False)
async def custom_redoc():
return get_redoc_html(
openapi_url="/openapi.json",
title=f"{app.title} - ReDoc",
)
# Custom OpenAPI schema
def custom_openapi():
if app.openapi_schema:
return app.openapi_schema
openapi_schema = get_openapi(
title=app.title,
version=app.version,
description=app.description,
routes=app.routes,
)
# Add security schemes
openapi_schema["components"]["securitySchemes"] = {
"BearerAuth": {
"type": "http",
"scheme": "bearer",
"bearerFormat": "JWT"
}
}
app.openapi_schema = openapi_schema
return app.openapi_schema
app.openapi = custom_openapi
Stoplight Studio
Project Structure
api-project/
├── reference/
│ ├── openapi.yaml # Main spec
│ └── models/
│ ├── User.yaml
│ ├── Error.yaml
│ └── Pagination.yaml
├── docs/
│ ├── getting-started.md
│ ├── authentication.md
│ └── rate-limiting.md
└── .stoplight.json
Stoplight Configuration
// .stoplight.json
{
"projectName": "User Service API",
"formats": ["oas3.1"],
"linting": {
"ruleset": ".spectral.yaml"
}
}
Model References
# reference/models/User.yaml
type: object
title: User
description: Represents a user in the system
required:
- id
- email
- name
properties:
id:
type: string
format: uuid
readOnly: true
email:
type: string
format: email
name:
type: string
examples:
- id: "123e4567-e89b-12d3-a456-426614174000"
email: "user@example.com"
name: "John Doe"
API Versioning in Docs
URL-Based Versioning
# openapi-v1.yaml
openapi: 3.1.0
info:
title: User Service API
version: 1.0.0
servers:
- url: https://api.example.com/v1
# openapi-v2.yaml
openapi: 3.1.0
info:
title: User Service API
version: 2.0.0
servers:
- url: https://api.example.com/v2
Multi-Version Documentation
// version-switcher.tsx
import { useState } from 'react'
import { RedocStandalone } from 'redoc'
const API_VERSIONS = [
{ version: 'v2', url: '/api/v2/openapi.yaml', label: 'v2 (Latest)' },
{ version: 'v1', url: '/api/v1/openapi.yaml', label: 'v1 (Deprecated)' }
]
export function ApiDocs() {
const [version, setVersion] = useState('v2')
const currentSpec = API_VERSIONS.find(v => v.version === version)
return (
<div>
<select value={version} onChange={(e) => setVersion(e.target.value)}>
{API_VERSIONS.map(v => (
<option key={v.version} value={v.version}>{v.label}</option>
))}
</select>
<RedocStandalone specUrl={currentSpec?.url} />
</div>
)
}
Linting with Spectral
Configuration
# .spectral.yaml
extends:
- spectral:oas
rules:
# Naming conventions
operation-operationId-valid-in-url: true
paths-kebab-case: true
# Documentation requirements
operation-description: error
operation-tags: error
info-description: error
# Security
operation-security-defined: error
# Custom rules
response-must-have-example:
description: Responses must have examples
severity: warn
given: "$.paths..responses.*.content.*.schema"
then:
field: example
function: truthy
use-standard-error-format:
description: Error responses must use standard format
severity: error
given: "$.paths..responses[?(@property >= '400')].content.*.schema"
then:
function: schema
functionOptions:
schema:
type: object
required: [code, message]
CI Integration
# .github/workflows/api-lint.yml
name: API Lint
on: [push, pull_request]
jobs:
lint:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Lint OpenAPI
uses: stoplightio/spectral-action@latest
with:
file_glob: 'openapi.yaml'
spectral_ruleset: '.spectral.yaml'
Automated Doc Generation
TypeScript to OpenAPI
// Using tsoa
// src/controllers/UserController.ts
import { Controller, Get, Post, Body, Route, Tags, Security, Response } from 'tsoa'
@Route('users')
@Tags('Users')
export class UserController extends Controller {
/**
* Retrieves all users with pagination
* @param page Page number
* @param limit Items per page
*/
@Get()
@Security('bearerAuth')
public async getUsers(
@Query() page: number = 1,
@Query() limit: number = 20
): Promise<UserListResponse> {
// Implementation
}
/**
* Creates a new user
* @param body User creation data
*/
@Post()
@Response<ErrorResponse>(400, 'Validation error')
@Response<ErrorResponse>(409, 'User already exists')
public async createUser(
@Body() body: CreateUserRequest
): Promise<User> {
// Implementation
}
}
Generate Spec
# tsoa.json
{
"entryFile": "src/app.ts",
"noImplicitAdditionalProperties": "throw-on-extras",
"controllerPathGlobs": ["src/controllers/**/*.ts"],
"spec": {
"outputDirectory": "docs",
"specVersion": 3,
"securityDefinitions": {
"bearerAuth": {
"type": "http",
"scheme": "bearer"
}
}
}
}
# Generate
npx tsoa spec
Usage Examples
Setup OpenAPI Documentation
Apply api-documentation skill to create OpenAPI 3.1 spec with Redoc for REST API
Add Interactive Explorer
Apply api-documentation skill to integrate Swagger UI with authentication persistence
Implement Version Switching
Apply api-documentation skill to setup multi-version API docs with deprecation notices
Success Output
When successful, this skill MUST output:
✅ SKILL COMPLETE: api-documentation
Completed:
- [x] OpenAPI 3.1 specification created with all endpoints documented
- [x] Redoc or Swagger UI integration configured and tested
- [x] Interactive examples with authentication working
- [x] API versioning strategy implemented
- [x] Spectral linting passing with no errors
Outputs:
- openapi.yaml or openapi.json
- API documentation HTML (docs/api.html or /api-docs endpoint)
- .spectral.yaml linting configuration
- CI/CD integration for automatic spec validation
Completion Checklist
Before marking this skill as complete, verify:
- OpenAPI specification validates against OpenAPI 3.1 schema
- All endpoints have operation IDs, summaries, and descriptions
- Request/response schemas defined with examples
- Error responses documented (4xx, 5xx status codes)
- Authentication schemes specified (Bearer, API Key, OAuth)
- Interactive documentation UI accessible and functional
- API versioning strategy documented and implemented
- Spectral linting rules configured and passing
- CI/CD pipeline validates spec on every commit
Failure Indicators
This skill has FAILED if:
- ❌ OpenAPI spec fails validation or has syntax errors
- ❌ Missing endpoint documentation (incomplete coverage)
- ❌ No examples provided for request/response schemas
- ❌ Authentication not documented or incorrect
- ❌ Interactive UI (Redoc/Swagger) not accessible or broken
- ❌ API versioning not implemented or inconsistent
- ❌ Spectral linting not configured or has errors
- ❌ Documentation not auto-generated from code annotations (if using tsoa/FastAPI)
When NOT to Use
Do NOT use this skill when:
- API has fewer than 3 endpoints (use simple README.md instead)
- API is internal-only and not exposed to external consumers
- Documentation needs are non-interactive (use markdown-based docs)
- GraphQL API (use GraphQL-specific documentation tools like GraphiQL)
- WebSocket-only API (requires different documentation approach)
- Prototyping phase before API design is stable
- Legacy API without OpenAPI support (requires migration work first)
Use these alternatives instead:
- Simple APIs:
README.mdwith curl examples - GraphQL:
graphql-documentation-patternsskill - Internal APIs:
internal-api-documentationskill
Anti-Patterns (Avoid)
| Anti-Pattern | Problem | Solution |
|---|---|---|
| Manually writing OpenAPI spec | Specs drift from actual code | Use code-first tools (tsoa, FastAPI auto-generation) |
| No request/response examples | Developers can't understand usage | Include realistic examples in every schema |
| Missing error documentation | Unclear how errors are handled | Document all 4xx/5xx responses with examples |
| Version-less API docs | Breaking changes break clients | Implement URL-based or header-based versioning |
| No CI validation | Specs become outdated | Add Spectral linting to CI/CD pipeline |
| Over-documenting internal endpoints | Clutter for external consumers | Use x-internal: true to hide internal endpoints |
| No interactive playground | Developers can't test easily | Always include Swagger UI or Redoc |
Principles
This skill embodies:
- #2 Progressive Disclosure - Start with minimal spec, progressively add schemas, examples, and advanced features
- #5 Eliminate Ambiguity - Clear endpoint descriptions, explicit request/response contracts
- #6 Clear, Understandable, Explainable - Interactive documentation makes API self-documenting
- #7 Verification Protocol - Spectral linting enforces standards automatically
- #9 Avoid Reinventing the Wheel - Use OpenAPI 3.1 standard, established tools (Redoc, Swagger)
Full Standard: CODITECT-STANDARD-AUTOMATION.md