api-docs-generator
SKILL.md
API Docs Generator
Create comprehensive API documentation with OpenAPI specifications and interactive documentation.
Core Workflow
- Analyze API endpoints: Review routes, methods, parameters
- Define OpenAPI spec: Create specification in YAML/JSON
- Add schemas: Define request/response models
- Include examples: Add realistic example values
- Generate documentation: Deploy interactive docs
- Create SDK: Optional client library generation
OpenAPI Specification Structure
# openapi.yaml
openapi: 3.1.0
info:
title: My API
version: 1.0.0
description: |
API description with **Markdown** support.
## Authentication
All endpoints require Bearer token authentication.
contact:
name: API Support
email: api@example.com
url: https://docs.example.com
license:
name: MIT
url: https://opensource.org/licenses/MIT
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 endpoints
- name: Products
description: Product catalog endpoints
- name: Orders
description: Order processing endpoints
paths:
# Endpoints defined here
components:
# Reusable schemas, security, etc.
Path Definitions
Basic CRUD Endpoints
paths:
/users:
get:
tags:
- Users
summary: List all users
description: Retrieve a paginated list of users
operationId: listUsers
parameters:
- $ref: '#/components/parameters/PageParam'
- $ref: '#/components/parameters/LimitParam'
- name: role
in: query
description: Filter by user role
schema:
type: string
enum: [admin, user, guest]
responses:
'200':
description: Successful response
content:
application/json:
schema:
$ref: '#/components/schemas/UserList'
example:
data:
- id: "usr_123"
email: "john@example.com"
name: "John Doe"
role: "admin"
createdAt: "2024-01-15T10:30:00Z"
pagination:
page: 1
limit: 20
total: 150
'401':
$ref: '#/components/responses/Unauthorized'
'500':
$ref: '#/components/responses/InternalError'
post:
tags:
- Users
summary: Create a new user
description: Create a new user account
operationId: createUser
requestBody:
required: true
content:
application/json:
schema:
$ref: '#/components/schemas/CreateUserRequest'
example:
email: "newuser@example.com"
name: "New User"
password: "securePassword123"
role: "user"
responses:
'201':
description: User created successfully
content:
application/json:
schema:
$ref: '#/components/schemas/User'
'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:
tags:
- Users
summary: Get user by ID
description: Retrieve a specific user by their ID
operationId: getUserById
responses:
'200':
description: Successful response
content:
application/json:
schema:
$ref: '#/components/schemas/User'
'404':
$ref: '#/components/responses/NotFound'
patch:
tags:
- Users
summary: Update user
description: Update an existing user's information
operationId: updateUser
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'
'404':
$ref: '#/components/responses/NotFound'
'422':
$ref: '#/components/responses/ValidationError'
delete:
tags:
- Users
summary: Delete user
description: Permanently delete a user
operationId: deleteUser
responses:
'204':
description: User deleted successfully
'404':
$ref: '#/components/responses/NotFound'
Component Schemas
Data Models
components:
schemas:
# Base User Schema
User:
type: object
properties:
id:
type: string
format: uuid
description: Unique user identifier
example: "usr_123abc"
readOnly: true
email:
type: string
format: email
description: User's email address
example: "john@example.com"
name:
type: string
minLength: 1
maxLength: 100
description: User's full name
example: "John Doe"
role:
$ref: '#/components/schemas/UserRole'
avatar:
type: string
format: uri
nullable: true
description: URL to user's avatar image
example: "https://cdn.example.com/avatars/123.jpg"
createdAt:
type: string
format: date-time
description: Account creation timestamp
readOnly: true
updatedAt:
type: string
format: date-time
description: Last update timestamp
readOnly: true
required:
- id
- email
- name
- role
- createdAt
UserRole:
type: string
enum:
- admin
- user
- guest
description: User's role in the system
example: "user"
# Request Schemas
CreateUserRequest:
type: object
properties:
email:
type: string
format: email
name:
type: string
minLength: 1
maxLength: 100
password:
type: string
format: password
minLength: 8
description: Must contain at least one uppercase, one lowercase, and one number
role:
$ref: '#/components/schemas/UserRole'
required:
- email
- name
- password
UpdateUserRequest:
type: object
properties:
name:
type: string
minLength: 1
maxLength: 100
role:
$ref: '#/components/schemas/UserRole'
avatar:
type: string
format: uri
nullable: true
minProperties: 1
# List Response
UserList:
type: object
properties:
data:
type: array
items:
$ref: '#/components/schemas/User'
pagination:
$ref: '#/components/schemas/Pagination'
Pagination:
type: object
properties:
page:
type: integer
minimum: 1
example: 1
limit:
type: integer
minimum: 1
maximum: 100
example: 20
total:
type: integer
minimum: 0
example: 150
hasMore:
type: boolean
example: true
# Error Schemas
Error:
type: object
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
items:
$ref: '#/components/schemas/ErrorDetail'
required:
- code
- message
ErrorDetail:
type: object
properties:
field:
type: string
description: The field that caused the error
example: "email"
message:
type: string
description: Description of the validation error
example: "Must be a valid email address"
Parameters and Responses
components:
parameters:
UserId:
name: userId
in: path
required: true
description: Unique user identifier
schema:
type: string
format: uuid
example: "usr_123abc"
PageParam:
name: page
in: query
description: Page number for pagination
schema:
type: integer
minimum: 1
default: 1
example: 1
LimitParam:
name: limit
in: query
description: Number of items per page
schema:
type: integer
minimum: 1
maximum: 100
default: 20
example: 20
SortParam:
name: sort
in: query
description: Sort field and direction
schema:
type: string
pattern: '^[a-zA-Z]+:(asc|desc)$'
example: "createdAt:desc"
responses:
BadRequest:
description: Bad request - invalid input
content:
application/json:
schema:
$ref: '#/components/schemas/Error'
example:
code: "BAD_REQUEST"
message: "Invalid request format"
Unauthorized:
description: Authentication required
content:
application/json:
schema:
$ref: '#/components/schemas/Error'
example:
code: "UNAUTHORIZED"
message: "Authentication token is missing or invalid"
Forbidden:
description: Permission denied
content:
application/json:
schema:
$ref: '#/components/schemas/Error'
example:
code: "FORBIDDEN"
message: "You don't have permission to access this resource"
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"
InternalError:
description: Internal server error
content:
application/json:
schema:
$ref: '#/components/schemas/Error'
example:
code: "INTERNAL_ERROR"
message: "An unexpected error occurred"
Security Definitions
components:
securitySchemes:
BearerAuth:
type: http
scheme: bearer
bearerFormat: JWT
description: |
JWT token obtained from the /auth/login endpoint.
Example: `Authorization: Bearer eyJhbGciOiJIUzI1...`
ApiKeyAuth:
type: apiKey
in: header
name: X-API-Key
description: API key for server-to-server communication
OAuth2:
type: oauth2
description: OAuth 2.0 authentication
flows:
authorizationCode:
authorizationUrl: https://auth.example.com/oauth/authorize
tokenUrl: https://auth.example.com/oauth/token
scopes:
read:users: Read user information
write:users: Create and modify users
admin: Full administrative access
# Apply security globally
security:
- BearerAuth: []
# Or per-endpoint
paths:
/public/health:
get:
security: [] # No auth required
summary: Health check
responses:
'200':
description: Service is healthy
Express/Node.js Integration
Generate from Code with express-openapi
// src/docs/openapi.ts
import { OpenAPIV3_1 } from 'openapi-types';
export const openApiDocument: OpenAPIV3_1.Document = {
openapi: '3.1.0',
info: {
title: 'My API',
version: '1.0.0',
description: 'API documentation',
},
servers: [
{ url: 'http://localhost:3000', description: 'Development' },
],
paths: {},
components: {
schemas: {},
securitySchemes: {
BearerAuth: {
type: 'http',
scheme: 'bearer',
bearerFormat: 'JWT',
},
},
},
};
Swagger UI Express
// src/docs/swagger.ts
import swaggerUi from 'swagger-ui-express';
import YAML from 'yamljs';
import path from 'path';
import { Express } from 'express';
export function setupSwagger(app: Express) {
const swaggerDocument = YAML.load(
path.join(__dirname, '../../openapi.yaml')
);
const options: swaggerUi.SwaggerUiOptions = {
explorer: true,
customSiteTitle: 'API Documentation',
customCss: '.swagger-ui .topbar { display: none }',
swaggerOptions: {
persistAuthorization: true,
displayRequestDuration: true,
filter: true,
showExtensions: true,
},
};
app.use('/docs', swaggerUi.serve, swaggerUi.setup(swaggerDocument, options));
app.get('/openapi.json', (req, res) => res.json(swaggerDocument));
}
Zod to OpenAPI
// src/schemas/user.ts
import { z } from 'zod';
import { extendZodWithOpenApi } from '@asteasolutions/zod-to-openapi';
extendZodWithOpenApi(z);
export const UserSchema = z.object({
id: z.string().uuid().openapi({ example: 'usr_123abc' }),
email: z.string().email().openapi({ example: 'john@example.com' }),
name: z.string().min(1).max(100).openapi({ example: 'John Doe' }),
role: z.enum(['admin', 'user', 'guest']).openapi({ example: 'user' }),
createdAt: z.string().datetime(),
}).openapi('User');
export const CreateUserSchema = z.object({
email: z.string().email(),
name: z.string().min(1).max(100),
password: z.string().min(8),
role: z.enum(['admin', 'user', 'guest']).optional().default('user'),
}).openapi('CreateUserRequest');
// src/docs/generator.ts
import {
OpenAPIRegistry,
OpenApiGeneratorV31,
} from '@asteasolutions/zod-to-openapi';
import { UserSchema, CreateUserSchema } from '../schemas/user';
const registry = new OpenAPIRegistry();
// Register schemas
registry.register('User', UserSchema);
registry.register('CreateUserRequest', CreateUserSchema);
// Register endpoints
registry.registerPath({
method: 'get',
path: '/users',
tags: ['Users'],
summary: 'List all users',
responses: {
200: {
description: 'List of users',
content: {
'application/json': {
schema: z.array(UserSchema),
},
},
},
},
});
registry.registerPath({
method: 'post',
path: '/users',
tags: ['Users'],
summary: 'Create a user',
request: {
body: {
content: {
'application/json': {
schema: CreateUserSchema,
},
},
},
},
responses: {
201: {
description: 'User created',
content: {
'application/json': {
schema: UserSchema,
},
},
},
},
});
// Generate OpenAPI document
const generator = new OpenApiGeneratorV31(registry.definitions);
export const openApiDocument = generator.generateDocument({
openapi: '3.1.0',
info: {
title: 'My API',
version: '1.0.0',
},
});
FastAPI Integration
# main.py
from fastapi import FastAPI, HTTPException, Query
from fastapi.openapi.utils import get_openapi
from pydantic import BaseModel, EmailStr, Field
from typing import Optional
from datetime import datetime
from enum import Enum
app = FastAPI(
title="My API",
description="API documentation with FastAPI",
version="1.0.0",
docs_url="/docs",
redoc_url="/redoc",
)
class UserRole(str, Enum):
admin = "admin"
user = "user"
guest = "guest"
class UserBase(BaseModel):
email: EmailStr = Field(..., example="john@example.com")
name: str = Field(..., min_length=1, max_length=100, example="John Doe")
role: UserRole = Field(default=UserRole.user, example="user")
class UserCreate(UserBase):
password: str = Field(..., min_length=8, example="securePassword123")
class User(UserBase):
id: str = Field(..., example="usr_123abc")
created_at: datetime
updated_at: Optional[datetime] = None
class Config:
from_attributes = True
class UserList(BaseModel):
data: list[User]
total: int
page: int
limit: int
@app.get(
"/users",
response_model=UserList,
tags=["Users"],
summary="List all users",
description="Retrieve a paginated list of users",
)
async def list_users(
page: int = Query(1, ge=1, description="Page number"),
limit: int = Query(20, ge=1, le=100, description="Items per page"),
role: Optional[UserRole] = Query(None, description="Filter by role"),
):
# Implementation
pass
@app.post(
"/users",
response_model=User,
status_code=201,
tags=["Users"],
summary="Create a new user",
responses={
409: {"description": "User already exists"},
422: {"description": "Validation error"},
},
)
async def create_user(user: UserCreate):
# Implementation
pass
# Custom OpenAPI schema
def custom_openapi():
if app.openapi_schema:
return app.openapi_schema
openapi_schema = get_openapi(
title="My API",
version="1.0.0",
description="API documentation",
routes=app.routes,
)
# Add security scheme
openapi_schema["components"]["securitySchemes"] = {
"BearerAuth": {
"type": "http",
"scheme": "bearer",
"bearerFormat": "JWT",
}
}
openapi_schema["security"] = [{"BearerAuth": []}]
app.openapi_schema = openapi_schema
return app.openapi_schema
app.openapi = custom_openapi
Documentation Generators
Redoc
<!-- docs/index.html -->
<!DOCTYPE html>
<html>
<head>
<title>API Documentation</title>
<meta charset="utf-8"/>
<meta name="viewport" content="width=device-width, initial-scale=1">
<link href="https://fonts.googleapis.com/css?family=Montserrat:300,400,700|Roboto:300,400,700" rel="stylesheet">
<style>
body { margin: 0; padding: 0; }
</style>
</head>
<body>
<redoc spec-url='/openapi.yaml' expand-responses="200,201"></redoc>
<script src="https://cdn.redoc.ly/redoc/latest/bundles/redoc.standalone.js"></script>
</body>
</html>
Stoplight Elements
<!DOCTYPE html>
<html>
<head>
<title>API Documentation</title>
<script src="https://unpkg.com/@stoplight/elements/web-components.min.js"></script>
<link rel="stylesheet" href="https://unpkg.com/@stoplight/elements/styles.min.css">
</head>
<body>
<elements-api
apiDescriptionUrl="/openapi.yaml"
router="hash"
layout="sidebar"
/>
</body>
</html>
SDK Generation
OpenAPI Generator
# Install OpenAPI Generator
npm install -g @openapitools/openapi-generator-cli
# Generate TypeScript client
openapi-generator-cli generate \
-i openapi.yaml \
-g typescript-fetch \
-o ./sdk/typescript \
--additional-properties=supportsES6=true,npmName=@myorg/api-client
# Generate Python client
openapi-generator-cli generate \
-i openapi.yaml \
-g python \
-o ./sdk/python \
--additional-properties=packageName=myapi_client
Configuration
# openapitools.json
{
"$schema": "https://raw.githubusercontent.com/OpenAPITools/openapi-generator/master/modules/openapi-generator-gradle-plugin/src/main/resources/openapitools.json",
"spaces": 2,
"generator-cli": {
"version": "7.0.0",
"generators": {
"typescript-client": {
"generatorName": "typescript-fetch",
"inputSpec": "./openapi.yaml",
"output": "./sdk/typescript",
"additionalProperties": {
"supportsES6": true,
"npmName": "@myorg/api-client",
"npmVersion": "1.0.0"
}
}
}
}
}
Validation
Spectral Linting
# .spectral.yaml
extends: ["spectral:oas", "spectral:asyncapi"]
rules:
operation-operationId: error
operation-description: warn
operation-tags: error
info-contact: warn
info-license: warn
oas3-schema: error
oas3-valid-media-example: warn
# Custom rules
path-must-have-tag:
given: "$.paths[*][*]"
severity: error
then:
field: tags
function: truthy
# Run linting
npx @stoplight/spectral-cli lint openapi.yaml
Best Practices
- Use $ref for reusability: Define schemas once, reference everywhere
- Include examples: Add realistic examples for all schemas
- Document errors: Describe all possible error responses
- Version your API: Use URL or header versioning
- Group with tags: Organize endpoints logically
- Add descriptions: Explain every parameter and field
- Use security schemes: Document authentication clearly
- Validate spec: Use Spectral or similar tools
- Generate SDKs: Automate client library creation
- Keep spec in sync: Generate from code or validate against it
Output Checklist
Every API documentation should include:
- Complete OpenAPI 3.x specification
- All endpoints documented with examples
- Request/response schemas with types
- Error responses documented
- Authentication scheme defined
- Parameters described with examples
- Interactive documentation deployed (Swagger UI/Redoc)
- Specification validated with linter
- SDK generation configured
- Versioning strategy documented
Weekly Installs
10
Repository
patricio0312rev/skillsFirst Seen
10 days ago
Installed on
claude-code8
gemini-cli7
antigravity7
windsurf7
github-copilot7
codex7