API Documentation
Documentation Tools: Building Your API Docs Pipeline
Read aloud is unavailable in this browser.
The Documentation Tooling Ecosystem
An API specification is a blueprint. But blueprints don't build houses—tools do. The power of standards like OpenAPI lies not in the specification itself but in the vast ecosystem of tools that transform specifications into interactive documentation, generate client libraries, create mock servers, and validate API behavior.
Choosing the right documentation tools is a critical architectural decision. The right toolchain automates tedious tasks, catches errors early, ensures consistency across environments, and creates documentation experiences that delight developers.
This page surveys the complete API documentation tooling landscape: from specification editors where specifications are authored, through documentation generators that produce consumer-facing docs, to the testing and validation tools that ensure accuracy. By the end, you'll understand how to assemble a documentation pipeline that scales from startup to enterprise.
By the end of this page, you will understand: (1) Categories of API documentation tools and their purposes; (2) In-depth comparison of documentation generators (Swagger UI, ReDoc, Stoplight, elements); (3) Specification editors and linters for authoring; (4) Mock servers for frontend development; (5) Testing and validation tools; (6) CI/CD integration patterns; (7) Tool selection criteria based on project needs.
Documentation Tool Categories
The API documentation tooling ecosystem can be organized into distinct categories, each serving a specific purpose in the documentation lifecycle:
The Documentation Pipeline
┌─────────────────┐ ┌─────────────────┐ ┌─────────────────┐
│ AUTHORING │ -> │ GENERATION │ -> │ PUBLISHING │
│ │ │ │ │ │
│ • Editors │ │ • Doc generators│ │ • Hosting │
│ • Linters │ │ • Code generators │ • CDN │
│ • Validators │ │ • Mock servers │ │ • Portals │
└─────────────────┘ └─────────────────┘ └─────────────────┘
│ │ │
v v v
┌─────────────────────────────────────────────────────────────────┐
│ CI/CD INTEGRATION │
│ • Automated validation • Testing • Deployment • Versioning │
└─────────────────────────────────────────────────────────────────┘
| Category | Purpose | Key Tools | When Used |
|---|---|---|---|
| Specification Editors | Author and edit OpenAPI specs | Swagger Editor, Stoplight Studio, VS Code + extensions | API design phase |
| Linters & Validators | Enforce style, catch errors | Spectral, OpenAPI-validator, Redocly CLI | CI/CD, pre-commit |
| Documentation Generators | Create interactive docs from specs | Swagger UI, ReDoc, Stoplight Elements, RapiDoc | Developer portals |
| Code Generators | Generate SDKs and server stubs | OpenAPI Generator, Orval, openapi-typescript | SDK creation |
| Mock Servers | Simulate API responses | Prism, Mockoon, Stoplight Prism | Frontend development |
| Testing Tools | Validate API against spec | Dredd, Schemathesis, Portman | Integration testing |
| API Portals | Complete documentation platforms | ReadMe, Stoplight, Redocly, SwaggerHub | External developer docs |
For each category, you face a build vs. buy decision. Open-source tools (Swagger UI, Spectral, Prism) provide flexibility and no licensing cost but require self-hosting and integration. Managed platforms (ReadMe, Stoplight, SwaggerHub) offer turnkey solutions with analytics, collaboration features, and support, but at a subscription cost. Evaluate based on team size, budget, and customization needs.
Documentation Generators: From Spec to Portal
Documentation generators transform OpenAPI specifications into interactive, navigable documentation. They're the most visible part of your documentation infrastructure—what developers actually see and use.
Swagger UI
The original and most widely recognized API documentation interface.
Swagger UI was created alongside the Swagger specification and remains the de facto standard for API exploration. It provides:
- Interactive "Try it out" functionality for testing endpoints
- Support for authentication (API keys, OAuth2, Basic)
- Language-agnostic (renders any OpenAPI spec)
- Deep ecosystem integration
12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667
<!DOCTYPE html><html lang="en"><head> <meta charset="UTF-8"> <title>API Documentation</title> <!-- Swagger UI CSS --> <link rel="stylesheet" href="https://unpkg.com/swagger-ui-dist@5/swagger-ui.css"> <style> body { margin: 0; padding: 0; } .topbar { display: none; } /* Hide default topbar */ </style></head><body> <div id="swagger-ui"></div> <!-- Swagger UI JavaScript --> <script src="https://unpkg.com/swagger-ui-dist@5/swagger-ui-bundle.js"></script> <script src="https://unpkg.com/swagger-ui-dist@5/swagger-ui-standalone-preset.js"></script> <script> window.onload = function() { SwaggerUIBundle({ // OpenAPI specification URL or inline spec url: "/openapi.json", // DOM element to render into dom_id: '#swagger-ui', // Enable deep linking to operations deepLinking: true, // Presets for layout and functionality presets: [ SwaggerUIBundle.presets.apis, SwaggerUIStandalonePreset ], // Layout configuration layout: "StandaloneLayout", // Enable "Try it out" by default tryItOutEnabled: true, // Show request duration displayRequestDuration: true, // Syntax highlighting theme syntaxHighlight: { activate: true, theme: "monokai" }, // Persist authorization across page reloads persistAuthorization: true, // Custom request interceptor (e.g., add auth headers) requestInterceptor: (request) => { // Add custom header request.headers['X-Custom-Header'] = 'value'; return request; } }); }; </script></body></html>ReDoc
Clean, three-panel documentation optimized for reading.
ReDoc provides a different philosophy from Swagger UI: prioritizing clean presentation over interactivity. It excels at:
- Beautiful typography and layout
- Comprehensive navigation sidebar
- Nested schema exploration
- SEO-friendly static HTML generation
- Faster load times than Swagger UI
12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455
<!DOCTYPE html><html lang="en"><head> <meta charset="UTF-8"> <title>API Documentation - ReDoc</title> <meta name="viewport" content="width=device-width, initial-scale=1"> <!-- ReDoc styling customization --> <style> body { margin: 0; padding: 0; } </style></head><body> <!-- ReDoc element with configuration --> <redoc spec-url="/openapi.json" hide-hostname="true" expand-responses="200,201" path-in-middle-panel="true" hide-loading="false" native-scrollbars="true" theme='{ "colors": { "primary": { "main": "#6366f1" } }, "typography": { "fontSize": "16px", "fontFamily": "Inter, system-ui, sans-serif", "headings": { "fontFamily": "Inter, system-ui, sans-serif", "fontWeight": "600" }, "code": { "fontSize": "14px", "fontFamily": "JetBrains Mono, monospace" } }, "sidebar": { "width": "280px", "backgroundColor": "#1e1e2e" }, "rightPanel": { "backgroundColor": "#1e1e2e", "width": "40%" } }' > </redoc> <!-- ReDoc JavaScript --> <script src="https://cdn.redoc.ly/redoc/latest/bundles/redoc.standalone.js"></script></body></html>Stoplight Elements
Modern, customizable React components for embedding documentation.
Stoplight Elements provides embeddable React components that can be integrated into existing applications, offering maximum customization:
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051
import React from 'react';import { API } from '@stoplight/elements';import '@stoplight/elements/styles.min.css'; // Full API documentation componentfunction APIDocumentation() { return ( <API apiDescriptionUrl="/openapi.json" // Layout: 'sidebar' | 'stacked' layout="sidebar" // Router: 'hash' | 'memory' | 'history' router="history" // Base path for routing basePath="/docs" // Hide "Try It" panel hideTryIt={false} // Hide mocking toggle hideMocking={true} // Custom logo logo="/logo.svg" /> );} // Embed in existing React appfunction DocsPage() { return ( <div className="docs-container"> <header className="custom-header"> <nav>{/* Your navigation */}</nav> </header> <main className="docs-content"> <APIDocumentation /> </main> <footer className="custom-footer"> {/* Your footer */} </footer> </div> );} export default DocsPage;| Feature | Swagger UI | ReDoc | Stoplight Elements |
|---|---|---|---|
| Interactive "Try It" | ✓ Full support | ✗ No (read-only) | ✓ Full support |
| Layout Style | Single panel, expandable | Three-panel, fixed | Sidebar or stacked |
| Customization | CSS + config options | CSS + theme object | Full React component control |
| Performance | Moderate (larger bundle) | Fast (optimized) | Moderate (React overhead) |
| Static Generation | Limited | ✓ Excellent (CLI) | ✓ Via SSR/SSG |
| Framework Integration | Framework-agnostic | Framework-agnostic | React-focused |
| Best For | API exploration & testing | Reference documentation | Embedded in existing apps |
Specification Editors and Linters
Good documentation starts with good specifications. Editors and linters help authors write correct, consistent, and complete OpenAPI documents.
Specification Editors
Swagger Editor (Browser-Based)
The original OpenAPI editor, providing split-pane editing with live preview:
- Browser-based, no installation required
- Real-time validation with error highlighting
- Integrated Swagger UI preview
- Available as Docker container for self-hosting
Stoplight Studio (Desktop/Web)
A visual editor for non-developers and power users alike:
- Form-based editing for those unfamiliar with YAML/JSON
- Source view for advanced users
- Git integration for version control
- Mock server built-in
- Multi-file specification support
VS Code with Extensions
For developers who prefer their existing IDE:
- OpenAPI (Swagger) Editor: Syntax highlighting, IntelliSense, preview
- YAML: Generic YAML support with JSON Schema validation
- Spectral: In-editor linting
- REST Client: Test endpoints directly from editor
1234567891011121314151617181920212223242526272829
{ // OpenAPI extension configuration "openapi.defaultEditor": "yaml", "openapi.showPreview": true, // YAML extension with OpenAPI schema "yaml.schemas": { "https://raw.githubusercontent.com/OAI/OpenAPI-Specification/main/schemas/v3.1/schema.json": [ "openapi.yaml", "openapi.yml", "**/openapi/*.yaml", "**/*.openapi.yaml" ] }, // Enable YAML formatting "yaml.format.enable": true, "yaml.format.singleQuote": false, "yaml.format.bracketSpacing": true, // Spectral linting "spectral.rulesetFile": ".spectral.yaml", "spectral.run": "onType", // Custom file associations "[yaml]": { "editor.defaultFormatter": "redhat.vscode-yaml" }}Spectral: API Linting
Spectral is the industry-standard linter for OpenAPI specifications. It enforces style guides, catches common errors, and ensures consistency across your API.
Built-in Rulesets
Spectral comes with OpenAPI rulesets covering:
- Missing descriptions
- Invalid schema references
- Inconsistent naming
- Security misconfigurations
- Response code issues
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081
# .spectral.yaml - Comprehensive API linting configuration # Extend the official OpenAPI rulesetextends: - spectral:oas # Override severity levelsrules: # Enforce operation IDs on all operations operation-operationId: severity: error # Require descriptions on operations operation-description: severity: warn # Enforce valid operation IDs (no spaces, special chars) operation-operationId-valid-in-url: severity: error # Require at least one tag per operation operation-tags: severity: warn # Require response examples oas3-valid-media-example: severity: warn # Ensure info contact is present info-contact: severity: warn # Ensure license is specified info-license: severity: hint # Custom rules for your organizationoverrides: - files: - "**/*.yaml" - "**/*.json" rules: # Custom: Enforce camelCase for property names camelCase-properties: given: "$.paths..properties[*]~" then: function: casing functionOptions: type: camel # Custom: Require x-internal extension for internal APIs internal-api-marked: given: "$.paths[*][*]" then: field: x-internal function: truthy severity: off # Enable for internal APIs # Custom: Prevent exposing sensitive fields no-sensitive-data-in-examples: given: "$..*[?(@.example)]" then: - function: pattern functionOptions: notMatch: "(password|secret|token|apikey)" severity: error # Custom: Require pagination for list endpoints list-operations-paginated: given: "$.paths[*].get" then: - function: schema functionOptions: schema: properties: parameters: contains: properties: name: enum: ["limit", "offset", "page", "cursor"] severity: hint12345678910111213141516171819202122232425
# Install Spectral CLInpm install -g @stoplight/spectral-cli # Lint a single filespectral lint openapi.yaml # Lint with custom rulesetspectral lint openapi.yaml --ruleset .spectral.yaml # Output as JSON (for CI/CD parsing)spectral lint openapi.yaml --format json > lint-results.json # Fail on warnings (strict mode for CI)spectral lint openapi.yaml --fail-severity warn # Lint multiple filesspectral lint "specs/**/*.yaml" # Example output# /api/openapi.yaml# 15:9 warning operation-description Operation "getUsers" should have a description.# 23:11 error operation-operationId-valid operationId "list-users" is not a valid C/C++ identifier.# 45:5 warning info-contact Info object should have "contact" object.## ✖ 3 problems (1 error, 2 warnings, 0 hints)Commit your .spectral.yaml to your repository and run Spectral in CI/CD. This ensures every API change is validated against your organization's standards before merge. Consider creating a shared ruleset package that all teams can depend on for org-wide consistency.
Code Generators: From Spec to SDK
Code generators transform OpenAPI specifications into usable code: client SDKs for API consumers, server stubs for implementers, and types for type-safe development.
OpenAPI Generator
The most comprehensive code generation project, supporting 50+ languages and frameworks:
- Client SDKs: TypeScript, Python, Java, Go, Rust, Swift, Kotlin, and many more
- Server stubs: Node.js, Spring, NestJS, FastAPI, Flask, and others
- Documentation: HTML, Markdown, AsciiDoc
- Schemas: JSON Schema, GraphQL
12345678910111213141516171819202122232425262728293031
# Install OpenAPI Generator CLInpm install -g @openapitools/openapi-generator-cli # List available generatorsopenapi-generator-cli list # Generate TypeScript Axios clientopenapi-generator-cli generate \ --input-spec openapi.yaml \ --generator-name typescript-axios \ --output ./sdk/typescript \ --additional-properties=npmName=@acme/api-client \ --additional-properties=npmVersion=1.0.0 \ --additional-properties=supportsES6=true # Generate Python clientopenapi-generator-cli generate \ --input-spec openapi.yaml \ --generator-name python \ --output ./sdk/python \ --additional-properties=packageName=acme_api \ --additional-properties=projectName=acme-api-client # Generate NestJS server stubsopenapi-generator-cli generate \ --input-spec openapi.yaml \ --generator-name typescript-nestjs \ --output ./server/generated # Use config file for reproducible generationopenapi-generator-cli generate -c openapi-config.yaml12345678910111213141516171819202122232425262728293031323334353637
# openapi-generator configuration fileinputSpec: ./openapi.yamloutputDir: ./sdk/typescript-fetchgeneratorName: typescript-fetch globalProperties: # Generate API classes apis: true # Generate model classes models: true # Generate supporting files supportingFiles: true additionalProperties: # NPM package name npmName: "@acme/api-client" # NPM version npmVersion: "1.0.0" # Use ES6+ features supportsES6: true # Use single request parameter object useSingleRequestParameter: true # Generate with interfaces (not classes) withInterfaces: true # Prefix for interfaces modelPropertyNaming: camelCase # Type mappings for custom handlingtypeMappings: DateTime: string # Import mappingsimportMappings: UUID: string # Template directory for customizationtemplateDir: ./templates/typescript-fetchOrval: React Query / SWR Integration
Orval is purpose-built for modern frontend development, generating type-safe API clients with React Query, SWR, or vanilla fetch integration:
123456789101112131415161718192021222324252627282930313233343536373839404142434445
// orval.config.tsimport { defineConfig } from 'orval'; export default defineConfig({ api: { input: { target: './openapi.yaml', validation: true, }, output: { target: './src/api/generated.ts', mode: 'split', // or 'single', 'tags' client: 'react-query', // React Query v5 configuration override: { mutator: { path: './src/api/custom-fetch.ts', name: 'customFetch', }, query: { useQuery: true, useMutation: true, useInfinite: true, useInfiniteQueryParam: 'cursor', signal: true, }, }, }, hooks: { afterAllFilesWrite: 'prettier --write', }, }, // Generate mock service worker handlers mocks: { input: { target: './openapi.yaml', }, output: { target: './src/mocks/handlers.ts', client: 'msw', }, },});123456789101112131415161718192021222324252627282930313233343536373839404142434445
// Using Orval-generated React Query hooksimport { useGetUsers, useCreateUser, useGetUserById } from '../api/generated'; function UserList() { // Type-safe query with automatic caching const { data: users, isLoading, error } = useGetUsers({ limit: 20, offset: 0, }); // Type-safe mutation const createUser = useCreateUser(); const handleCreate = async () => { await createUser.mutateAsync({ data: { email: 'new@example.com', name: 'New User', }, }); }; if (isLoading) return <Loading />; if (error) return <Error error={error} />; return ( <div> {/* users is fully typed: User[] */} {users?.data.map(user => ( <UserCard key={user.id} user={user} /> ))} <button onClick={handleCreate}> Add User </button> </div> );} function UserDetail({ userId }: { userId: string }) { // Path parameters are type-safe const { data: user } = useGetUserById(userId); // user is typed as User | undefined return user ? <Profile user={user} /> : null;}Not all code generators produce production-quality output. Before committing to a generator: (1) Evaluate the generated code manually; (2) Check for active maintenance and community support; (3) Test edge cases in your spec; (4) Consider customizing templates for your coding standards.
Mock Servers: Develop Without Backend
Mock servers simulate API responses based on your OpenAPI specification, enabling frontend development before the backend is complete and providing predictable test fixtures.
Prism (by Stoplight)
Prism is the most feature-rich OpenAPI mock server, providing:
- Dynamic response generation from examples and schemas
- Request validation against the spec
- Callback/webhook simulation
- Proxy mode for partial mocking
12345678910111213141516171819202122232425
# Install Prismnpm install -g @stoplight/prism-cli # Start mock serverprism mock openapi.yaml # Start with specific portprism mock openapi.yaml --port 4010 # Enable dynamic mode (generates random data from schemas)prism mock openapi.yaml --dynamic # Enable request validation (rejects invalid requests)prism mock openapi.yaml --errors # Proxy mode: mock undefined endpoints, forward others to real APIprism proxy openapi.yaml https://api.example.com # Example output# [CLI] ... INFO HTTP server listening on http://127.0.0.1:4010# [HTTP] ... INFO GET /users -> 200 # Make requests to the mock servercurl http://localhost:4010/users# Returns mocked response based on spec examples or generated dataMSW (Mock Service Worker)
MSW runs mocks in the browser or Node.js, intercepting network requests without a separate server process. This is ideal for testing and Storybook:
1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859
// handlers.ts - MSW request handlers generated from OpenAPI (via Orval)import { http, HttpResponse } from 'msw'; // User fixturesconst users = [ { id: '1', email: 'alice@example.com', name: 'Alice Smith' }, { id: '2', email: 'bob@example.com', name: 'Bob Jones' },]; export const handlers = [ // GET /users http.get('/api/users', ({ request }) => { const url = new URL(request.url); const limit = parseInt(url.searchParams.get('limit') || '20'); const offset = parseInt(url.searchParams.get('offset') || '0'); return HttpResponse.json({ data: users.slice(offset, offset + limit), pagination: { total: users.length, limit, offset, hasMore: offset + limit < users.length, }, }); }), // GET /users/:id http.get('/api/users/:id', ({ params }) => { const user = users.find(u => u.id === params.id); if (!user) { return HttpResponse.json( { code: 'NOT_FOUND', message: 'User not found' }, { status: 404 } ); } return HttpResponse.json({ data: user }); }), // POST /users http.post('/api/users', async ({ request }) => { const body = await request.json() as { email: string; name: string }; const newUser = { id: String(users.length + 1), email: body.email, name: body.name, }; users.push(newUser); return HttpResponse.json( { data: newUser }, { status: 201 } ); }),];1234567891011121314151617181920212223242526272829303132333435363738394041
// Browser setup (src/mocks/browser.ts)import { setupWorker } from 'msw/browser';import { handlers } from './handlers'; export const worker = setupWorker(...handlers); // Start in development mode (main.tsx or App.tsx)if (process.env.NODE_ENV === 'development') { const { worker } = await import('./mocks/browser'); await worker.start({ onUnhandledRequest: 'bypass', // Don't mock unhandled requests });} // Node.js setup for testing (src/mocks/server.ts)import { setupServer } from 'msw/node';import { handlers } from './handlers'; export const server = setupServer(...handlers); // Jest/Vitest setupbeforeAll(() => server.listen({ onUnhandledRequest: 'error' }));afterEach(() => server.resetHandlers());afterAll(() => server.close()); // Override handlers for specific testsimport { server } from './mocks/server';import { http, HttpResponse } from 'msw'; test('handles server error', async () => { server.use( http.get('/api/users', () => { return HttpResponse.json( { code: 'SERVER_ERROR', message: 'Database unavailable' }, { status: 500 } ); }) ); // Test error handling...});- •Frontend development — Build UI components before backend endpoints exist
- •Integration testing — Predictable, fast responses without real database
- •Storybook stories — Render components in various API states
- •Demo environments — Show functionality without production data
- •Offline development — Work without network connectivity
- •Edge case testing — Simulate errors, slow responses, empty states
Testing and Validation Tools
Documentation is only valuable if it accurately reflects the API. Testing and validation tools ensure your specification and implementation stay in sync.
Dredd: API Blueprint & OpenAPI Testing
Dredd tests your API implementation against your specification, ensuring every documented endpoint works as described:
123456789101112131415161718192021222324252627282930313233343536373839404142
# dredd.yml - Dredd configurationdry-run: falsehookfiles: - ./dredd/hooks.js # OpenAPI specificationblueprint: ./openapi.yaml # API server endpointendpoint: http://localhost:3000 # Output formatreporter: - cli - html # HTML report outputoutput: - ./dredd-report.html # Only test specific methods/pathsonly: - 'User > /users > Get all users > 200' - 'User > /users/{id} > Get user by ID > 200' # Skip specific transactionsskip: - 'Admin > /admin/* > *' # Skip admin endpoints # Custom headers for all requestsheader: - 'Authorization: Bearer test-token' - 'Accept: application/json' # Sort transactions by pathsorted: true # Fail on first errorbail: false # Color outputcolor: true123456789101112131415161718192021222324252627282930313233343536373839404142434445
// dredd/hooks.js - Custom test setup and teardownconst hooks = require('hooks');const db = require('../test/db'); // Global setup before all testshooks.beforeAll((transactions, done) => { // Seed test database db.seed().then(() => done());}); // Global teardownhooks.afterAll((transactions, done) => { db.cleanup().then(() => done());}); // Setup specific test data before a transactionhooks.before('User > /users/{id} > Get user by ID > 200', (transaction, done) => { // Create the user that will be fetched db.createUser({ id: '123', name: 'Test User' }) .then(() => { // Update path parameter transaction.fullPath = '/users/123'; done(); });}); // Modify request before sendinghooks.beforeEach((transaction, done) => { // Skip deprecated endpoints if (transaction.name.includes('deprecated')) { transaction.skip = true; } // Add dynamic auth token transaction.request.headers['Authorization'] = `Bearer ${getTestToken()}`; done();}); // Validate response beyond schemahooks.afterEach((transaction, done) => { if (transaction.test.status !== 'pass') { console.error('Transaction failed:', transaction.test.message); } done();});Schemathesis: Property-Based API Testing
Schemathesis uses property-based testing to discover edge cases and security vulnerabilities by generating thousands of valid and invalid requests:
12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849
# Install Schemathesispip install schemathesis # Basic test runschemathesis run http://localhost:3000/openapi.json # Run with specific checksschemathesis run http://localhost:3000/openapi.json \ --checks all # Run all validation checks # Stateful testing (tests sequences of requests)schemathesis run http://localhost:3000/openapi.json \ --stateful=links # Authenticationschemathesis run http://localhost:3000/openapi.json \ --auth-header "Authorization: Bearer test-token" # Limit to specific endpointsschemathesis run http://localhost:3000/openapi.json \ --endpoint "/users" "/products" # Exclude specific methodsschemathesis run http://localhost:3000/openapi.json \ --method GET POST # Only test GET and POST # Generate reproducible test casesschemathesis run http://localhost:3000/openapi.json \ --hypothesis-seed=42 # Output JUnit XML for CIschemathesis run http://localhost:3000/openapi.json \ --junit-xml=test-results.xml # Example output:# ================ Schemathesis test session starts ================# Schema location: http://localhost:3000/openapi.json# Base URL: http://localhost:3000# Operations: 15# Hypothesis settings: max_examples=100, database=None## GET /users ............................. [200/200]# POST /users ............................ [200/200]# GET /users/{id} F [100/100]# -> status_code_conformance: Response status code 500 not in schema# -> content_type_conformance: Response content type not application/json## ================ FAILURES ================# 1 failed, 14 passed in 45.23sTools like Schemathesis generate many requests, including edge cases with malformed data. Always run against a staging or test environment, never production. Ensure your test environment can handle the load and has isolated data.
CI/CD Integration: Automated Documentation Pipeline
The true power of documentation tooling emerges when integrated into your CI/CD pipeline. Automated checks ensure specification quality, generated documentation stays current, and implementation matches documentation.
Pipeline Architecture
┌─────────────────────────────────────────────────────────┐
│ PR / Commit │
└───────────────────────────┬─────────────────────────────┘
│
┌───────────────────┼───────────────────┐
│ │ │
v v v
┌───────────────┐ ┌───────────────┐ ┌───────────────┐
│ Lint Spec │ │ Generate Docs │ │ Type Check │
│ (Spectral) │ │ (ReDoc CLI) │ │ (TypeScript) │
└───────────────┘ └───────────────┘ └───────────────┘
│ │ │
└───────────────────┼───────────────────┘
│
v
┌───────────────┐
│ Contract Test │
│ (Dredd) │
└───────────────┘
│
v
┌───────────────┐
│ Deploy Docs │
│ (Pages) │
└───────────────┘
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131
name: API Documentation Pipeline on: push: branches: [main] paths: - 'openapi/**' - 'src/api/**' pull_request: paths: - 'openapi/**' - 'src/api/**' jobs: lint: name: Lint OpenAPI Specification runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - name: Install Spectral run: npm install -g @stoplight/spectral-cli - name: Lint OpenAPI spec run: spectral lint openapi/openapi.yaml --fail-severity warn - name: Upload lint results if: failure() uses: actions/upload-artifact@v3 with: name: spectral-results path: spectral-report.json generate-types: name: Generate TypeScript Types runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - name: Setup Node.js uses: actions/setup-node@v4 with: node-version: '20' cache: 'npm' - name: Install dependencies run: npm ci - name: Generate types run: npx openapi-typescript openapi/openapi.yaml -o src/types/api.ts - name: Type check run: npx tsc --noEmit - name: Check for uncommitted changes run: | if [ -n "$(git status --porcelain src/types/api.ts)" ]; then echo "Generated types are out of sync. Run 'npm run generate:types' and commit." exit 1 fi contract-test: name: Contract Testing needs: [lint, generate-types] runs-on: ubuntu-latest services: api: image: ghcr.io/${{ github.repository }}/api:test ports: - 3000:3000 steps: - uses: actions/checkout@v4 - name: Install Dredd run: npm install -g dredd - name: Run contract tests run: dredd openapi/openapi.yaml http://localhost:3000 - name: Upload test results if: always() uses: actions/upload-artifact@v3 with: name: dredd-results path: dredd-report.html build-docs: name: Build Documentation needs: [lint] runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - name: Build ReDoc static HTML run: | npx @redocly/cli build-docs openapi/openapi.yaml \ --output docs/index.html \ --title "API Documentation" - name: Upload docs artifact uses: actions/upload-artifact@v3 with: name: documentation path: docs/ deploy-docs: name: Deploy Documentation needs: [build-docs, contract-test] if: github.ref == 'refs/heads/main' runs-on: ubuntu-latest permissions: pages: write id-token: write environment: name: github-pages url: ${{ steps.deployment.outputs.page_url }} steps: - name: Download docs artifact uses: actions/download-artifact@v3 with: name: documentation path: docs/ - name: Upload to Pages uses: actions/upload-pages-artifact@v2 with: path: docs/ - name: Deploy to Pages id: deployment uses: actions/deploy-pages@v2- •Fail fast on lint errors — Block PRs that violate specification standards
- •Regenerate types on every build — Ensure generated code matches spec
- •Run contract tests against staging — Verify implementation matches documentation
- •Version documentation with releases — Tag docs matching API versions
- •Automate SDK publishing — Generate and publish client SDKs on version bumps
- •Monitor documentation builds — Alert if generation fails
KEEP LEARNING
There’s more to understand.
You’re reading the introduction. Create a free account to read the complete lesson, save notes and keep track of your progress.