REST API Documentation

Publish beautiful, interactive API docs from your OpenAPI spec using Scalar, Redoc, and Swagger UI

Last Updated:

The 5 Pillars of Great API Documentation

Great API documentation is the difference between developers adopting your API in hours vs abandoning it in frustration. It combines five complementary content types:

Content TypePurposeExample
ReferenceComplete endpoint list with parameters, schemas, examplesSwagger UI / Redoc / Scalar
GuidesExplain concepts and design decisions"Authentication Overview", "Pagination Guide"
TutorialsStep-by-step for a specific goal"Build Your First Order in 5 Minutes"
Code ExamplesCopy-paste snippets in multiple languagescURL, Node.js, Python, Ruby
ChangelogWhat changed, when, and what to do about itCHANGELOG.md with migration notes

OpenAPI as the Single Source of Truth

Write your OpenAPI 3.1 spec once — all documentation tools, SDK generators, mock servers, and test suites derive from it. This eliminates documentation drift where prose docs and actual API behaviour diverge.

# Directory structure: API-first
my-api/
├── openapi.yaml          # Single source of truth
├── schemas/
│   ├── user.yaml         # JSON Schema $ref'd from openapi.yaml
│   └── order.yaml
├── docs/
│   ├── guides/           # Concept guides (Markdown)
│   └── tutorials/        # Step-by-step tutorials
└── src/                  # Implementation

Swagger UI

The original OpenAPI UI — "try it out" functionality lets users test endpoints directly from the browser. Best for APIs consumed by developers who want to experiment.

npm install swagger-ui-express yaml
const express = require('express');
const swaggerUi = require('swagger-ui-express');
const YAML = require('yaml');
const fs = require('fs');

const app = express();
const spec = YAML.parse(fs.readFileSync('./openapi.yaml', 'utf8'));

app.use('/docs', swaggerUi.serve, swaggerUi.setup(spec, {
  customCss: '.swagger-ui .topbar { display: none }',
  customSiteTitle: 'My API Reference',
  swaggerOptions: {
    persistAuthorization: true,   // keep auth token between page refreshes
    deepLinking: true,            // shareable URLs to specific endpoints
    displayRequestDuration: true  // show response time in UI
  }
}));

// Also serve the raw OpenAPI spec (for SDK generators, Postman, etc.)
app.get('/openapi.yaml', (req, res) => {
  res.set('Content-Type', 'application/yaml');
  res.send(fs.readFileSync('./openapi.yaml'));
});

Redoc

Redoc generates a beautiful 3-panel read-only reference (navigation, endpoint detail, code samples). Best for consumer-facing documentation where you want polished, professional appearance.

npm install redoc-express
const redoc = require('redoc-express');

app.get('/reference', redoc({
  title: 'My API Reference',
  specUrl: '/openapi.yaml',
  redocOptions: {
    theme: { colors: { primary: { main: '#1e40af' } } },
    hideDownloadButton: false,
    expandResponses: '200,201',
    requiredPropsFirst: true,
    sortPropsAlphabetically: false
  }
}));

To generate a standalone static HTML file (for hosting on S3/GitHub Pages):

npx @redocly/cli build-docs openapi.yaml --output docs/index.html

Scalar (Rising in 2026)

Scalar is the fastest-growing API documentation tool in 2026 — modern design, dark mode, interactive "API Client" playground, and zero-config setup. Increasingly chosen over Swagger UI for new projects.

<!-- Serve Scalar as a self-contained HTML page -->
app.get('/reference', (req, res) => {
  res.send(`
    <!DOCTYPE html>
    <html>
      <head>
        <title>My API Reference</title>
        <meta charset="utf-8" />
        <meta name="viewport" content="width=device-width, initial-scale=1" />
      </head>
      <body>
        <script
          id="api-reference"
          data-url="/openapi.yaml"
          data-configuration='{"theme":"purple","defaultHttpClient":{"targetKey":"node","clientKey":"fetch"}}'
        ></script>
        <script src="https://cdn.jsdelivr.net/npm/@scalar/api-reference"></script>
      </body>
    </html>
  `);
});

Stoplight

Stoplight is an API design platform — collaborative OpenAPI editor + documentation + style guide linting. Best for teams that want an API-first workflow with governance.

  • Stoplight Studio — visual OpenAPI editor (no YAML knowledge required)
  • Stoplight Spectral — OpenAPI linting with custom rules
  • Stoplight Prism — mock server from OpenAPI spec
# Lint your OpenAPI spec with Spectral
npm install -g @stoplight/spectral-cli
spectral lint openapi.yaml --ruleset .spectral.yaml

# .spectral.yaml
extends: ["spectral:oas"]
rules:
  operation-operationId: error
  operation-tag-defined: warn
  info-contact: warn

Tool Comparison

ToolTypeTry-it-outDark ModeBest For
ScalarHosted/self✅ API ClientModern developer experience, new projects
Swagger UISelf-hostedLimitedQuick setup, familiar to all devs
RedocSelf-hosted❌ Read-onlyPolished consumer-facing reference
StoplightPlatformTeam API design + governance
Readme.comSaaSDeveloper portal + changelog + guides

Docs-as-Code

Treat your API documentation like code: version-controlled in Git, reviewed in PRs, automatically tested and deployed.

# Validate OpenAPI spec in CI
npx @stoplight/spectral-cli lint openapi.yaml

# Generate client SDKs on every release
npx @openapitools/openapi-generator-cli generate \
  -i openapi.yaml \
  -g typescript-fetch \
  -o sdk/typescript

# Check for breaking changes
npx @redocly/cli diff openapi-main.yaml openapi-branch.yaml

CI/CD Publish Pipeline

# .gitlab-ci.yml — publish docs to GitLab Pages on every main merge
docs:
  stage: deploy
  image: node:20
  script:
    - npx @stoplight/spectral-cli lint openapi.yaml   # lint first
    - npx @redocly/cli build-docs openapi.yaml --output public/index.html
    - cp openapi.yaml public/openapi.yaml
  artifacts:
    paths:
      - public
  pages: true
  only:
    - main

Changelog Best Practices

Follow Keep a Changelog format and Semantic Versioning:

# CHANGELOG.md
## [2.4.0] - 2026-05-13

### Added
- POST /orders now accepts an `idempotency_key` field
- GET /users supports `?status=active|inactive` filter

### Changed
- GET /orders now returns `total_cents` (integer) instead of `total` (string)
  Migration: multiply old `total` by 100, or use new field directly

### Deprecated
- GET /v1/users — use /v2/users instead. Sunset: 2026-12-31

### Removed
- DELETE /v1/orders/:id — use /v2/orders/:id (same behaviour, better error messages)

Publish the changelog as part of your API documentation and link to it from your Deprecation Link header. For full deprecation strategy, see our API Deprecation guide.