swagger-jsdoc vs swagger-ui-express
Building and Serving OpenAPI Documentation in Node.js
swagger-jsdocswagger-ui-express

Building and Serving OpenAPI Documentation in Node.js

swagger-jsdoc and swagger-ui-express are complementary tools used to generate and visualize API documentation in Node.js applications, but they solve distinct parts of the workflow. swagger-jsdoc acts as a parser that extracts OpenAPI (Swagger) definitions from JSDoc comments in your source code, compiling them into a valid JSON or YAML specification object. It does not serve any UI; it only produces the data structure. swagger-ui-express, on the other hand, is an Express middleware designed to take an existing OpenAPI specification object (whether generated by swagger-jsdoc or written manually) and serve the interactive Swagger User Interface at a specific route. Together, they form a complete pipeline: one generates the spec, and the other displays it.

Npm Package Weekly Downloads Trend

3 Years

Github Stars Ranking

Stat Detail

Package
Downloads
Stars
Size
Issues
Publish
License
swagger-jsdoc01,788710 kB433 months agoMIT
swagger-ui-express01,49724 kB542 years agoMIT

Building and Serving OpenAPI Docs: swagger-jsdoc vs swagger-ui-express

In the Node.js ecosystem, documenting REST APIs often involves two distinct steps: defining the API specification (the "what") and presenting it to developers (the "how"). swagger-jsdoc and swagger-ui-express are the industry-standard tools for these respective tasks. While they are frequently used together, understanding their separate roles is critical for architectural clarity. One parses your code to create a spec; the other takes that spec and renders a UI.

πŸ“ Defining the Spec: Code Comments vs Static Files

swagger-jsdoc focuses entirely on generating the OpenAPI specification object. It scans your JavaScript or TypeScript files for JSDoc comments tagged with @swagger or @openapi. This allows you to define paths, parameters, and responses right next to the route logic.

// Using swagger-jsdoc to parse comments
const swaggerJsdoc = require('swagger-jsdoc');

const options = {
  definition: {
    openapi: '3.0.0',
    info: { title: 'My API', version: '1.0.0' },
  },
  apis: ['./routes/*.js'], // Path to the API docs
};

const swaggerSpec = swaggerJsdoc(options);
// swaggerSpec is now a JSON object containing the full API definition

In your route file, you define the endpoint documentation using standard JSDoc blocks:

// routes/users.js
/**
 * @swagger
 * /users:
 *   get:
 *     summary: Returns a list of users
 *     responses:
 *       200:
 *         description: A successful response
 */
router.get('/users', (req, res) => {
  res.json([{ id: 1, name: 'Alice' }]);
});

swagger-ui-express does not generate specifications. It expects you to provide a valid OpenAPI object. If you aren't using swagger-jsdoc, you would typically load a static swagger.json or swagger.yaml file manually.

// Manual spec loading (alternative to swagger-jsdoc)
const swaggerDocument = require('./swagger-output.json'); 
// This object is then passed to swagger-ui-express

πŸ–₯️ Serving the Interface: Middleware Setup

swagger-ui-express is purely a presentation layer. It is an Express middleware that serves the Swagger UI assets and injects the specification you provide into it. Without this package (or a similar UI library), you would only have a raw JSON object, which is hard for humans to read and test.

// Using swagger-ui-express to serve the UI
const swaggerUi = require('swagger-ui-express');
const express = require('express');
const app = express();

// Assume swaggerSpec was generated by swagger-jsdoc or loaded manually
const swaggerSpec = require('./swagger-spec'); 

app.use('/api-docs', swaggerUi.serve, swaggerUi.setup(swaggerSpec));

// Now visiting http://localhost:3000/api-docs shows the interactive UI

swagger-jsdoc has no capability to serve HTTP requests or render HTML. If you try to use it alone, you will have a valid JSON object in memory but no way to visualize it in a browser without building your own viewer or attaching a different UI library.

// swagger-jsdoc alone - no UI served
const spec = swaggerJsdoc(options);
console.log(spec); // Only logs the JSON to the console
// No web interface is available

πŸ”„ Workflow Integration: How They Connect

The most common architectural pattern is to chain these two libraries. swagger-jsdoc runs first to compile the documentation from your source code, and the resulting object is passed directly into swagger-ui-express.

Combined Implementation:

const express = require('express');
const swaggerJsdoc = require('swagger-jsdoc');
const swaggerUi = require('swagger-ui-express');

const app = express();

// 1. Generate the spec from code comments
const options = {
  definition: {
    openapi: '3.0.0',
    info: { title: 'Combined Example', version: '1.0.0' },
  },
  apis: ['./src/routes/*.js'],
};
const swaggerSpec = swaggerJsdoc(options);

// 2. Serve the UI using the generated spec
app.use('/docs', swaggerUi.serve, swaggerUi.setup(swaggerSpec));

app.listen(3000, () => {
  console.log('Docs available at http://localhost:3000/docs');
});

If you choose not to use swagger-jsdoc (perhaps you prefer writing YAML by hand), the integration with swagger-ui-express remains identical; only the source of the swaggerSpec variable changes.

// Alternative: Loading static YAML with swagger-ui-express
const yaml = require('js-yaml');
const fs = require('fs');
const swaggerUi = require('swagger-ui-express');

const swaggerDocument = yaml.load(fs.readFileSync('./swagger.yaml', 'utf8'));

app.use('/docs', swaggerUi.serve, swaggerUi.setup(swaggerDocument));

βš™οΈ Configuration and Customization

swagger-jsdoc configuration revolves around file discovery and the base OpenAPI definition. You must explicitly tell it which files to scan (apis) and provide the root metadata (definition). It supports glob patterns, making it easy to include documentation from distributed feature modules.

// swagger-jsdoc options
const options = {
  definition: {
    openapi: '3.0.0',
    info: { title: 'My App', version: '1.0.0' },
    components: { securitySchemes: { bearerAuth: { type: 'http', scheme: 'bearer' } } }
  },
  apis: ['./src/**/*.js', './src/**/*.ts'], // Recursive scanning
  failOnErrors: true, // Throw if JSDoc syntax is invalid
};

swagger-ui-express configuration focuses on the visual experience and URL routing. You can pass a second argument to setup() to customize the UI title, enable deep linking, or hide specific sections of the interface.

// swagger-ui-express customization
const options = {
  explorer: true, // Allows loading external specs
  customSiteTitle: 'My API Docs',
  swaggerOptions: {
    docExpansion: 'list', // Expand operations by default
    filter: true, // Add a search box
  },
  customCss: '.swagger-ui .topbar { display: none }' // Hide default top bar
};

app.use('/docs', swaggerUi.serve, swaggerUi.setup(swaggerSpec, options));

πŸ›‘ Common Pitfalls and Limitations

A frequent mistake is assuming swagger-jsdoc validates your API logic. It does not. It only parses comments. If your JSDoc syntax is wrong, the tool might ignore the block or throw an error (if failOnErrors is on), but it won't check if your actual route handler matches the documented response.

// ❌ Incorrect: swagger-jsdoc won't catch this mismatch
/**
 * @swagger
 * /users:
 *   get:
 *     responses:
 *       200:
 *         description: Returns a STRING (but code returns JSON)
 */
router.get('/users', (req, res) => {
  res.json({ id: 1 }); // Code returns JSON, doc says string
});
// swagger-jsdoc will successfully generate the spec with the wrong description

Similarly, swagger-ui-express requires the spec to be fully valid before it renders. If swagger-jsdoc produces an incomplete object (e.g., missing paths due to glob errors), the UI may load but show no endpoints, leading to confusion about whether the middleware is broken or the spec is empty.

// ❌ Incorrect: Passing undefined spec
const swaggerSpec = swaggerJsdoc(options); // Returns {} if no files match
app.use('/docs', swaggerUi.serve, swaggerUi.setup(swaggerSpec));
// UI loads but is empty. Developer thinks swagger-ui-express is broken.

🀝 Similarities: Shared Ground

Despite their different roles, both libraries share a commitment to the OpenAPI 3.0 standard and are designed to be framework-agnostic within the Node.js ecosystem (though swagger-ui-express specifically targets Express).

1. πŸ“„ OpenAPI 3.0 Compliance

Both tools strictly adhere to the OpenAPI 3.0 specification. swagger-jsdoc outputs valid 3.0 JSON, and swagger-ui-express expects 3.0 input. This ensures compatibility with other tools in the ecosystem like Postman or Insomnia.

// Both rely on this standard structure
const openApiStructure = {
  openapi: "3.0.0",
  info: { title: "Standard", version: "1.0.0" },
  paths: {}
};

2. πŸš€ Runtime Generation

Both operate at runtime in a typical development setup. swagger-jsdoc parses files when the server starts, and swagger-ui-express serves the result dynamically. This allows for rapid iteration without a separate build step (though production setups often pre-generate the spec).

// Typical dev server startup
app.listen(port, () => {
  // Spec generated here
  const spec = swaggerJsdoc(options);
  // UI served here
  app.use('/docs', swaggerUi.serve, swaggerUi.setup(spec));
});

3. πŸ”Œ Express Middleware Pattern

While swagger-jsdoc is a utility function, it is almost exclusively used in Express projects alongside swagger-ui-express, which is a classic Express middleware. They fit naturally into the app.use() workflow.

// Both integrate into the Express chain
app.use('/api-docs', swaggerUi.serve, swaggerUi.setup(spec));

πŸ“Š Summary: Key Differences

Featureswagger-jsdocswagger-ui-express
Primary RoleSpecification GeneratorUI Renderer / Middleware
InputSource code files with JSDoc commentsOpenAPI JSON/YAML object
OutputJavaScript Object (JSON spec)HTML/CSS/JS Interface
DependencyNone (standalone parser)Requires Express.js
VisualsNone (Headless)Full Interactive Swagger UI
WorkflowCode-First (Docs in comments)Presentation-First (Serving the view)

πŸ’‘ The Big Picture

Think of swagger-jsdoc as the author. It reads your code comments and writes the official documentation book (the JSON spec). It keeps the content close to the source of truthβ€”your implementation.

Think of swagger-ui-express as the librarian. It takes the book written by the author (or anyone else) and sets up the reading room (the website) where people can actually read, search, and interact with it.

Final Recommendation: For most professional Node.js teams, the best approach is to use both. Use swagger-jsdoc to enforce a code-first documentation style that reduces drift, and pair it with swagger-ui-express to provide an immediate, interactive testing environment for frontend developers and QA engineers. If you prefer writing YAML files manually, skip swagger-jsdoc but keep swagger-ui-express to serve the result.

How to Choose: swagger-jsdoc vs swagger-ui-express

  • swagger-jsdoc:

    Choose swagger-jsdoc if you want to maintain your API documentation directly alongside your route handlers using JSDoc comments. This approach ensures your docs stay in sync with your code logic, as the specification is generated at runtime or build time from the source. It is ideal for teams that prefer a 'code-first' workflow and want to avoid maintaining a separate, large YAML file that might drift from the actual implementation.

  • swagger-ui-express:

    Choose swagger-ui-express if you need to expose an interactive API documentation interface (the Swagger UI) within your Express application. You must select this package regardless of how you created your OpenAPI spec (manually, via swagger-jsdoc, or other tools). It is the standard solution for rendering the visual layer, handling the HTML, CSS, and JavaScript required to let users test endpoints directly in the browser.

README for swagger-jsdoc

swagger-jsdoc

This library reads your JSDoc-annotated source code and generates an OpenAPI (Swagger) specification.

npm Downloads CI

Getting started

Imagine having API files like these:

/**
 * @openapi
 * /:
 *   get:
 *     description: Welcome to swagger-jsdoc!
 *     responses:
 *       200:
 *         description: Returns a mysterious string.
 */
app.get('/', (req, res) => {
  res.send('Hello World!');
});

The library will take the contents of @openapi (or @swagger) with the following configuration:

const swaggerJsdoc = require('swagger-jsdoc');

const options = {
  definition: {
    openapi: '3.0.0',
    info: {
      title: 'Hello World',
      version: '1.0.0',
    },
  },
  apis: ['./src/routes*.js'], // files containing annotations as above
};

const openapiSpecification = swaggerJsdoc(options);

The resulting openapiSpecification will be a swagger tools-compatible (and validated) specification.

swagger-jsdoc example screenshot

System requirements

  • Node.js 20.x or higher

You are viewing swagger-jsdoc v6 which is published in CommonJS module system.

Installation

npm install swagger-jsdoc --save

Or

yarn add swagger-jsdoc

Supported specifications

  • OpenAPI 3.x
  • Swagger 2
  • AsyncAPI 2.0

Validation of swagger docs

By default swagger-jsdoc tries to parse all docs to it's best capabilities. If you'd like to you can instruct an Error to be thrown instead if validation failed by setting the options flag failOnErrors to true. This is for instance useful if you want to verify that your swagger docs validate using a unit test.

const swaggerJsdoc = require('swagger-jsdoc');

const options = {
  failOnErrors: true, // Whether or not to throw when parsing errors. Defaults to false.
  definition: {
    openapi: '3.0.0',
    info: {
      title: 'Hello World',
      version: '1.0.0',
    },
  },
  apis: ['./src/routes*.js'],
};

const openapiSpecification = swaggerJsdoc(options);

Documentation

Click on the version you are using for further details: