Docs are a work in progress - contributions welcome
Logonestjs-openapi
Recipes

Swagger UI

Serve interactive API documentation with Swagger UI

Swagger UI provides interactive documentation where developers can explore and test your API. nestjs-openapi includes built-in Swagger UI support via OpenApiModule.

Built-in Swagger UI

The simplest way to serve Swagger UI:

import { Module } from '@nestjs/common';
import { OpenApiModule } from 'nestjs-openapi';

@Module({
  imports: [
    OpenApiModule.forRoot({
      specFile: 'openapi.json',
      swagger: true,
    }),
  ],
})
export class AppModule {}

Access at http://localhost:3000/api-docs.

Configuration Options

// Enable with defaults
OpenApiModule.forRoot({
  specFile: 'openapi.json',
  swagger: true,
});

// Enable with custom options
OpenApiModule.forRoot({
  specFile: 'openapi.json',
  swagger: {
    path: '/docs',
    title: 'My API Documentation',
  },
});
OptionTypeDefaultDescription
pathstring'/api-docs'URL path for Swagger UI
titlestringspec titleCustom page title

Environment-Based Display

Show Swagger UI only in development:

OpenApiModule.forRoot({
  specFile: 'openapi.json',
  enabled: process.env.NODE_ENV !== 'production',
  swagger: true,
});

Or use a dedicated environment variable:

OpenApiModule.forRoot({
  specFile: 'openapi.json',
  swagger: process.env.SWAGGER_UI_ENABLED === 'true',
});

Alternative: swagger-ui-express

For more control over Swagger UI, use swagger-ui-express directly:

npm install swagger-ui-express
npm install -D @types/swagger-ui-express
import { NestFactory } from '@nestjs/core';
import { SwaggerModule } from '@nestjs/swagger';
import * as swaggerUi from 'swagger-ui-express';
import * as fs from 'fs';
import { AppModule } from './app.module';

async function bootstrap() {
  const app = await NestFactory.create(AppModule);

  // Load the pre-generated spec
  const spec = JSON.parse(fs.readFileSync('openapi.json', 'utf-8'));

  // Serve Swagger UI with custom options
  app.use(
    '/docs',
    swaggerUi.serve,
    swaggerUi.setup(spec, {
      customCss: '.swagger-ui .topbar { display: none }',
      customSiteTitle: 'My API Docs',
      customfavIcon: '/favicon.ico',
      swaggerOptions: {
        persistAuthorization: true,
        displayRequestDuration: true,
        filter: true,
        showExtensions: true,
      },
    }),
  );

  await app.listen(3000);
}
bootstrap();

Swagger UI Options

Common swagger-ui-express options:

swaggerUi.setup(spec, {
  // Custom CSS
  customCss: `
    .swagger-ui .topbar { display: none }
    .swagger-ui .info { margin-bottom: 20px }
  `,

  // Custom CSS URL
  customCssUrl: '/custom-swagger.css',

  // Custom JavaScript
  customJs: '/custom-swagger.js',

  // Custom page title
  customSiteTitle: 'My API Documentation',

  // Custom favicon
  customfavIcon: '/favicon.ico',

  // Swagger UI options
  swaggerOptions: {
    // Keep authorization after refresh
    persistAuthorization: true,

    // Show request duration
    displayRequestDuration: true,

    // Enable filtering
    filter: true,

    // Show extensions
    showExtensions: true,

    // Expand/collapse options: 'list', 'full', 'none'
    docExpansion: 'list',

    // Default models expansion depth
    defaultModelsExpandDepth: 1,

    // Default model expansion depth
    defaultModelExpandDepth: 3,

    // Enable "Try it out" by default
    tryItOutEnabled: true,

    // Syntax highlighting theme
    syntaxHighlight: {
      activate: true,
      theme: 'monokai',
    },
  },

  // Explorer plugin (search bar)
  explorer: true,
});

Alternative: Redoc

Redoc is another popular OpenAPI documentation tool:

npm install redoc-express
import { NestFactory } from '@nestjs/core';
import * as redoc from 'redoc-express';
import * as fs from 'fs';
import { AppModule } from './app.module';

async function bootstrap() {
  const app = await NestFactory.create(AppModule);

  const spec = JSON.parse(fs.readFileSync('openapi.json', 'utf-8'));

  app.use(
    '/docs',
    redoc.default({
      title: 'API Docs',
      specUrl: '/openapi.json',
      spec,
      redocOptions: {
        theme: {
          colors: {
            primary: { main: '#6200ee' },
          },
          typography: {
            fontSize: '16px',
            fontFamily: 'Inter, sans-serif',
          },
        },
        hideDownloadButton: false,
        expandResponses: '200,201',
      },
    }),
  );

  // Serve raw spec
  app.getHttpAdapter().get('/openapi.json', (req, res) => {
    res.json(spec);
  });

  await app.listen(3000);
}
bootstrap();

Alternative: Scalar

Scalar is a modern API reference:

npm install @scalar/nestjs-api-reference
import { NestFactory } from '@nestjs/core';
import { apiReference } from '@scalar/nestjs-api-reference';
import * as fs from 'fs';
import { AppModule } from './app.module';

async function bootstrap() {
  const app = await NestFactory.create(AppModule);

  const spec = JSON.parse(fs.readFileSync('openapi.json', 'utf-8'));

  app.use(
    '/docs',
    apiReference({
      spec: {
        content: spec,
      },
      theme: 'purple',
    }),
  );

  await app.listen(3000);
}
bootstrap();

Static Hosting

For production, consider hosting documentation statically:

Build Static HTML

# Using Redoc CLI
npm install -g @redocly/cli
redocly build-docs openapi.json -o docs/index.html

# Using Swagger UI
# Download from https://github.com/swagger-api/swagger-ui/releases

Deploy to CDN

Host the static files on any CDN:

  • Netlify
  • Vercel
  • GitHub Pages
  • CloudFront + S3

GitHub Pages Example

# .github/workflows/docs.yml
name: Deploy API Docs

on:
  push:
    branches: [main]

jobs:
  deploy:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4

      - uses: actions/setup-node@v4
        with:
          node-version: '20'

      - run: npm ci

      - name: Generate spec
        run: npx nestjs-openapi generate -c openapi.config.ts

      - name: Build Redoc
        run: npx @redocly/cli build-docs openapi.json -o docs/index.html

      - name: Deploy to GitHub Pages
        uses: peaceiris/actions-gh-pages@v3
        with:
          github_token: ${{ secrets.GITHUB_TOKEN }}
          publish_dir: ./docs

Multiple API Versions

Serve documentation for multiple API versions:

import * as swaggerUi from 'swagger-ui-express';
import * as fs from 'fs';

const specV1 = JSON.parse(fs.readFileSync('openapi-v1.json', 'utf-8'));
const specV2 = JSON.parse(fs.readFileSync('openapi-v2.json', 'utf-8'));

// V1 docs
app.use('/docs/v1', swaggerUi.serveFiles(specV1), swaggerUi.setup(specV1));

// V2 docs
app.use('/docs/v2', swaggerUi.serveFiles(specV2), swaggerUi.setup(specV2));

// Landing page with version selector
app.get('/docs', (req, res) => {
  res.send(`
    <h1>API Documentation</h1>
    <ul>
      <li><a href="/docs/v1">API v1</a></li>
      <li><a href="/docs/v2">API v2</a></li>
    </ul>
  `);
});

Authentication in Swagger UI

Pre-configure authentication for testing:

swaggerUi.setup(spec, {
  swaggerOptions: {
    persistAuthorization: true,
    // Pre-fill auth header
    authAction: {
      bearerAuth: {
        name: 'bearerAuth',
        schema: {
          type: 'http',
          scheme: 'bearer',
        },
        value: 'your-dev-token-here',
      },
    },
  },
});

Never hardcode production tokens. Use environment-specific configurations.

Next Steps

On this page