Back to Blog
APIDocumentationOpenAPIDeveloper Experience

API Documentation Strategies That Work

Create documentation developers love. From OpenAPI specs to interactive docs to code examples.

B
Bootspring Team
Engineering
April 12, 2023
5 min read

Good API documentation reduces support burden and improves developer adoption. Poor documentation leads to frustration and errors. Here's how to document APIs effectively.

OpenAPI Specification#

1# openapi.yaml 2openapi: 3.1.0 3info: 4 title: User API 5 version: 1.0.0 6 description: | 7 API for managing users in the system. 8 9 ## Authentication 10 All endpoints require Bearer token authentication. 11 12 ## Rate Limiting 13 API is limited to 100 requests per minute per API key. 14 15servers: 16 - url: https://api.example.com/v1 17 description: Production 18 - url: https://api-staging.example.com/v1 19 description: Staging 20 21paths: 22 /users: 23 get: 24 summary: List users 25 description: Returns a paginated list of users 26 operationId: listUsers 27 tags: 28 - Users 29 parameters: 30 - name: limit 31 in: query 32 schema: 33 type: integer 34 minimum: 1 35 maximum: 100 36 default: 20 37 description: Number of users to return 38 - name: cursor 39 in: query 40 schema: 41 type: string 42 description: Pagination cursor from previous response 43 responses: 44 '200': 45 description: Successful response 46 content: 47 application/json: 48 schema: 49 $ref: '#/components/schemas/UserList' 50 example: 51 data: 52 - id: "usr_123" 53 email: "jane@example.com" 54 name: "Jane Doe" 55 createdAt: "2024-01-15T10:30:00Z" 56 pagination: 57 hasMore: true 58 nextCursor: "eyJpZCI6InVzcl8xMjMifQ==" 59 '401': 60 $ref: '#/components/responses/Unauthorized' 61 62 post: 63 summary: Create user 64 operationId: createUser 65 tags: 66 - Users 67 requestBody: 68 required: true 69 content: 70 application/json: 71 schema: 72 $ref: '#/components/schemas/CreateUserInput' 73 examples: 74 basic: 75 summary: Basic user creation 76 value: 77 email: "jane@example.com" 78 name: "Jane Doe" 79 withRole: 80 summary: Create admin user 81 value: 82 email: "admin@example.com" 83 name: "Admin User" 84 role: "admin" 85 responses: 86 '201': 87 description: User created 88 content: 89 application/json: 90 schema: 91 $ref: '#/components/schemas/User' 92 '400': 93 $ref: '#/components/responses/ValidationError' 94 95components: 96 schemas: 97 User: 98 type: object 99 required: 100 - id 101 - email 102 - name 103 - createdAt 104 properties: 105 id: 106 type: string 107 example: "usr_123" 108 email: 109 type: string 110 format: email 111 name: 112 type: string 113 role: 114 type: string 115 enum: [user, admin] 116 default: user 117 createdAt: 118 type: string 119 format: date-time 120 121 CreateUserInput: 122 type: object 123 required: 124 - email 125 - name 126 properties: 127 email: 128 type: string 129 format: email 130 description: Must be unique 131 name: 132 type: string 133 minLength: 1 134 maxLength: 100 135 role: 136 type: string 137 enum: [user, admin] 138 139 Error: 140 type: object 141 properties: 142 error: 143 type: string 144 code: 145 type: string 146 details: 147 type: array 148 items: 149 type: object 150 properties: 151 field: 152 type: string 153 message: 154 type: string 155 156 responses: 157 Unauthorized: 158 description: Authentication required 159 content: 160 application/json: 161 schema: 162 $ref: '#/components/schemas/Error' 163 example: 164 error: "Unauthorized" 165 code: "AUTH_REQUIRED" 166 167 ValidationError: 168 description: Validation failed 169 content: 170 application/json: 171 schema: 172 $ref: '#/components/schemas/Error' 173 example: 174 error: "Validation failed" 175 code: "VALIDATION_ERROR" 176 details: 177 - field: "email" 178 message: "Must be a valid email" 179 180 securitySchemes: 181 bearerAuth: 182 type: http 183 scheme: bearer 184 bearerFormat: JWT 185 186security: 187 - bearerAuth: []

Code Examples#

1// Include examples in multiple languages 2 3const examples = { 4 curl: ` 5curl -X POST https://api.example.com/v1/users \\ 6 -H "Authorization: Bearer YOUR_API_KEY" \\ 7 -H "Content-Type: application/json" \\ 8 -d '{ 9 "email": "jane@example.com", 10 "name": "Jane Doe" 11 }' 12`, 13 14 javascript: ` 15const response = await fetch('https://api.example.com/v1/users', { 16 method: 'POST', 17 headers: { 18 'Authorization': 'Bearer YOUR_API_KEY', 19 'Content-Type': 'application/json', 20 }, 21 body: JSON.stringify({ 22 email: 'jane@example.com', 23 name: 'Jane Doe', 24 }), 25}); 26 27const user = await response.json(); 28console.log(user); 29`, 30 31 python: ` 32import requests 33 34response = requests.post( 35 'https://api.example.com/v1/users', 36 headers={'Authorization': 'Bearer YOUR_API_KEY'}, 37 json={ 38 'email': 'jane@example.com', 39 'name': 'Jane Doe', 40 } 41) 42 43user = response.json() 44print(user) 45`, 46 47 ruby: ` 48require 'net/http' 49require 'json' 50 51uri = URI('https://api.example.com/v1/users') 52http = Net::HTTP.new(uri.host, uri.port) 53http.use_ssl = true 54 55request = Net::HTTP::Post.new(uri) 56request['Authorization'] = 'Bearer YOUR_API_KEY' 57request['Content-Type'] = 'application/json' 58request.body = { 59 email: 'jane@example.com', 60 name: 'Jane Doe' 61}.to_json 62 63response = http.request(request) 64user = JSON.parse(response.body) 65puts user 66`, 67};

Interactive Documentation#

1// Generate interactive docs with Swagger UI or Redoc 2import swaggerUi from 'swagger-ui-express'; 3import YAML from 'yamljs'; 4 5const swaggerDocument = YAML.load('./openapi.yaml'); 6 7app.use( 8 '/docs', 9 swaggerUi.serve, 10 swaggerUi.setup(swaggerDocument, { 11 customCss: '.swagger-ui .topbar { display: none }', 12 customSiteTitle: 'API Documentation', 13 swaggerOptions: { 14 persistAuthorization: true, 15 tryItOutEnabled: true, 16 }, 17 }) 18); 19 20// Or use Redoc for cleaner documentation 21import redoc from 'redoc-express'; 22 23app.get( 24 '/docs', 25 redoc({ 26 title: 'API Documentation', 27 specUrl: '/openapi.json', 28 nonce: '', 29 redocOptions: { 30 theme: { 31 colors: { 32 primary: { main: '#3b82f6' }, 33 }, 34 }, 35 hideDownloadButton: false, 36 expandResponses: '200,201', 37 }, 38 }) 39);

Error Documentation#

1# Document all error responses 2components: 3 responses: 4 BadRequest: 5 description: Invalid request 6 content: 7 application/json: 8 schema: 9 $ref: '#/components/schemas/Error' 10 examples: 11 validation: 12 summary: Validation error 13 value: 14 error: "Validation failed" 15 code: "VALIDATION_ERROR" 16 details: 17 - field: "email" 18 message: "Invalid email format" 19 missing_field: 20 summary: Missing required field 21 value: 22 error: "Missing required field: name" 23 code: "MISSING_FIELD" 24 25 NotFound: 26 description: Resource not found 27 content: 28 application/json: 29 example: 30 error: "User not found" 31 code: "NOT_FOUND" 32 33 RateLimited: 34 description: Rate limit exceeded 35 headers: 36 X-RateLimit-Limit: 37 description: Request limit per minute 38 schema: 39 type: integer 40 X-RateLimit-Remaining: 41 description: Remaining requests 42 schema: 43 type: integer 44 X-RateLimit-Reset: 45 description: Reset timestamp 46 schema: 47 type: integer 48 content: 49 application/json: 50 example: 51 error: "Rate limit exceeded" 52 code: "RATE_LIMITED" 53 retryAfter: 30

Changelog#

1# API Changelog 2 3## v1.2.0 (2024-02-01) 4 5### Added 6- `GET /users/{id}/activity` - Get user activity history 7- `role` field to User object 8 9### Changed 10- Increased rate limit from 60 to 100 requests/minute 11 12### Deprecated 13- `GET /users/{id}/logs` - Use `/users/{id}/activity` instead 14 15## v1.1.0 (2024-01-15) 16 17### Added 18- Pagination support with cursor-based pagination 19- `filter` query parameter on `GET /users` 20 21### Fixed 22- Email validation now accepts plus signs 23 24## v1.0.0 (2024-01-01) 25 26Initial release with: 27- User CRUD operations 28- Authentication endpoints 29- Rate limiting

SDK Generation#

1// Generate SDKs from OpenAPI spec 2// package.json script 3{ 4 "scripts": { 5 "generate:sdk:ts": "openapi-typescript-codegen --input ./openapi.yaml --output ./sdk/typescript", 6 "generate:sdk:python": "openapi-python-client generate --path ./openapi.yaml --output-path ./sdk/python" 7 } 8} 9 10// Generated TypeScript client 11import { UserApi, Configuration } from '@yourcompany/api-client'; 12 13const config = new Configuration({ 14 basePath: 'https://api.example.com/v1', 15 headers: { 16 Authorization: `Bearer ${apiKey}`, 17 }, 18}); 19 20const api = new UserApi(config); 21 22// Fully typed! 23const users = await api.listUsers({ limit: 10 }); 24const newUser = await api.createUser({ 25 createUserInput: { 26 email: 'jane@example.com', 27 name: 'Jane Doe', 28 }, 29});

Best Practices#

Content: ✓ Start with quickstart guide ✓ Include authentication examples ✓ Document all error codes ✓ Provide code examples in multiple languages Structure: ✓ Group endpoints logically ✓ Use consistent naming ✓ Include request/response examples ✓ Document pagination patterns Maintenance: ✓ Keep docs in sync with code ✓ Version your API docs ✓ Maintain a changelog ✓ Test documentation accuracy

Conclusion#

Good API documentation is essential for developer adoption. Use OpenAPI for specification, provide interactive docs, include code examples, and document errors thoroughly. Keep documentation in sync with your API and maintain a changelog for updates.

Share this article

Help spread the word about Bootspring