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.
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.
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
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
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));
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));
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.
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).
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: {}
};
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));
});
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));
| Feature | swagger-jsdoc | swagger-ui-express |
|---|---|---|
| Primary Role | Specification Generator | UI Renderer / Middleware |
| Input | Source code files with JSDoc comments | OpenAPI JSON/YAML object |
| Output | JavaScript Object (JSON spec) | HTML/CSS/JS Interface |
| Dependency | None (standalone parser) | Requires Express.js |
| Visuals | None (Headless) | Full Interactive Swagger UI |
| Workflow | Code-First (Docs in comments) | Presentation-First (Serving the view) |
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.
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.
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.
This library reads your JSDoc-annotated source code and generates an OpenAPI (Swagger) specification.
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.

You are viewing swagger-jsdoc v6 which is published in CommonJS module system.
npm install swagger-jsdoc --save
Or
yarn add swagger-jsdoc
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);
Click on the version you are using for further details: