Back to Blog
OpenAPISwaggerAPI DocumentationDeveloper Experience

API Documentation with OpenAPI and Swagger

Document APIs that developers love. From OpenAPI specs to interactive docs to code generation.

B
Bootspring Team
Engineering
December 12, 2023
5 min read

Good API documentation is the difference between developers loving or abandoning your API. OpenAPI provides a standard way to describe REST APIs.

OpenAPI Specification Basics#

1# openapi.yaml 2openapi: 3.1.0 3info: 4 title: My API 5 description: API for managing users and posts 6 version: 1.0.0 7 contact: 8 email: api@example.com 9 license: 10 name: MIT 11 12servers: 13 - url: https://api.example.com/v1 14 description: Production 15 - url: https://staging-api.example.com/v1 16 description: Staging 17 18tags: 19 - name: Users 20 description: User management 21 - name: Posts 22 description: Blog post operations

Defining Endpoints#

1paths: 2 /users: 3 get: 4 tags: 5 - Users 6 summary: List all users 7 description: Returns a paginated list of users 8 operationId: listUsers 9 parameters: 10 - name: page 11 in: query 12 description: Page number 13 schema: 14 type: integer 15 default: 1 16 minimum: 1 17 - name: limit 18 in: query 19 description: Items per page 20 schema: 21 type: integer 22 default: 20 23 minimum: 1 24 maximum: 100 25 responses: 26 '200': 27 description: Successful response 28 content: 29 application/json: 30 schema: 31 $ref: '#/components/schemas/UserList' 32 '401': 33 $ref: '#/components/responses/Unauthorized' 34 35 post: 36 tags: 37 - Users 38 summary: Create a new user 39 operationId: createUser 40 requestBody: 41 required: true 42 content: 43 application/json: 44 schema: 45 $ref: '#/components/schemas/CreateUserRequest' 46 responses: 47 '201': 48 description: User created 49 content: 50 application/json: 51 schema: 52 $ref: '#/components/schemas/User' 53 '400': 54 $ref: '#/components/responses/BadRequest' 55 '409': 56 description: Email already exists 57 58 /users/{id}: 59 get: 60 tags: 61 - Users 62 summary: Get user by ID 63 operationId: getUser 64 parameters: 65 - $ref: '#/components/parameters/UserId' 66 responses: 67 '200': 68 description: User found 69 content: 70 application/json: 71 schema: 72 $ref: '#/components/schemas/User' 73 '404': 74 $ref: '#/components/responses/NotFound'

Defining Schemas#

1components: 2 schemas: 3 User: 4 type: object 5 required: 6 - id 7 - email 8 - name 9 properties: 10 id: 11 type: string 12 format: uuid 13 example: '123e4567-e89b-12d3-a456-426614174000' 14 email: 15 type: string 16 format: email 17 example: 'john@example.com' 18 name: 19 type: string 20 minLength: 1 21 maxLength: 100 22 example: 'John Doe' 23 role: 24 type: string 25 enum: [user, admin, editor] 26 default: user 27 createdAt: 28 type: string 29 format: date-time 30 avatar: 31 type: string 32 format: uri 33 nullable: true 34 35 CreateUserRequest: 36 type: object 37 required: 38 - email 39 - name 40 - password 41 properties: 42 email: 43 type: string 44 format: email 45 name: 46 type: string 47 minLength: 1 48 password: 49 type: string 50 minLength: 8 51 format: password 52 53 UserList: 54 type: object 55 properties: 56 data: 57 type: array 58 items: 59 $ref: '#/components/schemas/User' 60 pagination: 61 $ref: '#/components/schemas/Pagination' 62 63 Pagination: 64 type: object 65 properties: 66 page: 67 type: integer 68 limit: 69 type: integer 70 total: 71 type: integer 72 totalPages: 73 type: integer 74 75 Error: 76 type: object 77 properties: 78 code: 79 type: string 80 message: 81 type: string 82 details: 83 type: object

Reusable Components#

1components: 2 parameters: 3 UserId: 4 name: id 5 in: path 6 required: true 7 description: User ID 8 schema: 9 type: string 10 format: uuid 11 12 responses: 13 NotFound: 14 description: Resource not found 15 content: 16 application/json: 17 schema: 18 $ref: '#/components/schemas/Error' 19 example: 20 code: NOT_FOUND 21 message: Resource not found 22 23 Unauthorized: 24 description: Authentication required 25 content: 26 application/json: 27 schema: 28 $ref: '#/components/schemas/Error' 29 30 BadRequest: 31 description: Invalid request 32 content: 33 application/json: 34 schema: 35 $ref: '#/components/schemas/Error' 36 37 securitySchemes: 38 BearerAuth: 39 type: http 40 scheme: bearer 41 bearerFormat: JWT 42 43 ApiKeyAuth: 44 type: apiKey 45 in: header 46 name: X-API-Key 47 48security: 49 - BearerAuth: []

Express with Swagger UI#

1import express from 'express'; 2import swaggerUi from 'swagger-ui-express'; 3import YAML from 'yamljs'; 4 5const app = express(); 6 7// Load OpenAPI spec 8const openApiSpec = YAML.load('./openapi.yaml'); 9 10// Serve Swagger UI 11app.use('/docs', swaggerUi.serve, swaggerUi.setup(openApiSpec, { 12 customCss: '.swagger-ui .topbar { display: none }', 13 customSiteTitle: 'My API Documentation', 14})); 15 16// Serve raw spec 17app.get('/openapi.json', (req, res) => { 18 res.json(openApiSpec); 19});

Auto-Generate from Code#

1// Using tsoa for TypeScript 2import { Controller, Get, Post, Route, Body, Path, Query, Security } from 'tsoa'; 3 4@Route('users') 5export class UsersController extends Controller { 6 /** 7 * Get a list of users 8 * @param page Page number 9 * @param limit Items per page 10 */ 11 @Get() 12 public async getUsers( 13 @Query() page: number = 1, 14 @Query() limit: number = 20 15 ): Promise<UserListResponse> { 16 // Implementation 17 } 18 19 /** 20 * Create a new user 21 * @param body User creation data 22 */ 23 @Post() 24 @Security('bearerAuth') 25 public async createUser( 26 @Body() body: CreateUserRequest 27 ): Promise<User> { 28 // Implementation 29 } 30 31 /** 32 * Get user by ID 33 * @param id User's unique identifier 34 */ 35 @Get('{id}') 36 public async getUser(@Path() id: string): Promise<User> { 37 // Implementation 38 } 39}

Code Generation#

1# Generate TypeScript client 2npx openapi-generator-cli generate \ 3 -i openapi.yaml \ 4 -g typescript-axios \ 5 -o ./generated/client 6 7# Generate server stubs 8npx openapi-generator-cli generate \ 9 -i openapi.yaml \ 10 -g typescript-express-server \ 11 -o ./generated/server
1// Using generated client 2import { UsersApi, Configuration } from './generated/client'; 3 4const api = new UsersApi(new Configuration({ 5 basePath: 'https://api.example.com/v1', 6 accessToken: 'your-token', 7})); 8 9const users = await api.listUsers({ page: 1, limit: 20 }); 10const user = await api.createUser({ 11 createUserRequest: { 12 email: 'john@example.com', 13 name: 'John', 14 password: 'secure123', 15 }, 16});

Validation with OpenAPI#

1import { OpenAPIValidator } from 'express-openapi-validator'; 2 3app.use( 4 OpenAPIValidator.middleware({ 5 apiSpec: './openapi.yaml', 6 validateRequests: true, 7 validateResponses: true, 8 }) 9); 10 11// Requests are automatically validated 12// Invalid requests return 400 with details

Best Practices#

1# Good documentation practices 2 3# 1. Use descriptive summaries 4paths: 5 /users/{id}/posts: 6 get: 7 summary: Get all posts by a specific user 8 description: | 9 Returns a paginated list of posts authored by the user. 10 Posts are sorted by creation date in descending order. 11 12# 2. Provide examples 13components: 14 schemas: 15 User: 16 properties: 17 email: 18 type: string 19 example: 'jane@example.com' 20 21# 3. Document error responses 22responses: 23 '400': 24 description: | 25 Invalid request. Possible reasons: 26 - Missing required fields 27 - Invalid email format 28 - Password too short 29 30# 4. Use tags for organization 31tags: 32 - name: Users 33 description: | 34 User management endpoints. 35 All endpoints require authentication except registration.

Conclusion#

OpenAPI documentation serves as a contract between API providers and consumers. Invest in good documentation—it reduces support burden and improves developer adoption.

Generate docs from code when possible, validate requests against the spec, and keep documentation in sync with implementation.

Share this article

Help spread the word about Bootspring