swagger-ui-express
swagger-ui-express is a small CommonJS adapter that mounts Swagger UI inside an Express application. Give setup() an OpenAPI or Swagger document and combine the returned middleware with swaggerUi.serve on a route such as /api-docs; the browser receives the Swagger UI application and can browse operations or send test requests. The adapter can also point the UI at one or more remote spec URLs, inject custom CSS or JavaScript, and serve separate documents at separate routes. It does not create an API specification, validate requests, secure the documentation route, parse YAML by itself, or connect documented operations to Express handlers.
Still the direct, unsurprising way to put Swagger UI in an Express app. Pin swagger-ui-dist, protect the route, and do not mistake an attractive document viewer for API validation.
Use it if
- You already run Express 4 or 5 and want interactive OpenAPI documentation served by the same application
- You have a JSON OpenAPI document in memory and want the shortest path to a familiar Swagger UI route
- You need to brand Swagger UI with custom CSS, scripts, favicon, site title, or client configuration
- You need multiple Swagger UI routes or a dropdown that loads several specification URLs
- You are not using Express: this package is middleware for Express and offers no framework-neutral server adapter
- You expect documentation to enforce the contract: the README points to separate routing tools, and this package does not validate requests or responses
- You want reproducible frontend assets without extra pinning: swagger-ui-dist is declared as >=5.0.0, and the README explicitly tells users to use a lockfile or specify its version
- You need first-party TypeScript declarations: version 5.0.1 publishes no types field, so TypeScript projects usually add @types/swagger-ui-express
- You need an actively evolving integration layer: npm 5.0.1 was published in May 2024, while the repository has 54 open issues and pull requests and its last push was January 2025
Setup reality
Install swagger-ui-express beside Express; Express is a peer dependency accepting Express 4 or 5 beta-and-newer versions. The package is CommonJS and ships no TypeScript declarations, so TypeScript users normally need @types/swagger-ui-express plus compiler interop that matches their project. swagger-ui-dist is a direct dependency with the range >=5.0.0, not a bounded major range. The README warns that UI behavior can therefore change between installs unless a lockfile or explicit override pins the asset package. A JSON spec can be required directly. YAML needs a separate parser such as yaml, file I/O, and error handling before the server starts. Mount both swaggerUi.serve and swaggerUi.setup(document) at the same base route; with an Express Router, the documented form mounts static middleware with use() and the HTML handler with get(). For multiple documents, use serveFiles(document, options) per route because the shared serve middleware can expose the wrong initialization assets. A URL-based setup means the browser fetches the spec, so the URL must be reachable from the browser and permitted by CORS when it is cross-origin. The adapter does not authenticate /api-docs, redact internal paths, or disable Swagger UI's request executor. Add your normal authorization middleware before it if the spec or Try it out requests are sensitive. customJs and customJsStr execute code on the documentation page and may conflict with a strict Content Security Policy; only load trusted scripts. Dynamic request-specific documents go on req.swaggerDoc and need serveFiles rather than the simplest serve form.
Patterns
Mount Swagger UI from a JSON documentserve-json-spec
const express = require('express');
const swaggerUi = require('swagger-ui-express');
const openapi = require('./openapi.json');
const app = express();
app.use('/api-docs', swaggerUi.serve, swaggerUi.setup(openapi));Place authentication middleware before this mount if the specification or interactive request console should not be public.
Mount the UI on an Express Routermount-on-router
const router = require('express').Router();
const swaggerUi = require('swagger-ui-express');
const openapi = require('./openapi.json');
router.use('/api-docs', swaggerUi.serve);
router.get('/api-docs', swaggerUi.setup(openapi));The static asset middleware uses router.use(), while the generated HTML handler can be mounted with router.get().
Require authorization before serving docsprotect-docs-route
app.use(
'/api-docs',
requireInternalUser,
swaggerUi.serve,
swaggerUi.setup(openapi),
);swagger-ui-express does not add access control. Your middleware must reject unauthorized requests before static files and HTML are served.
Parse an OpenAPI YAML file at startupload-yaml-spec
const fs = require('node:fs');
const YAML = require('yaml');
const swaggerUi = require('swagger-ui-express');
const source = fs.readFileSync('./openapi.yaml', 'utf8');
const openapi = YAML.parse(source);
app.use('/api-docs', swaggerUi.serve, swaggerUi.setup(openapi));YAML support is not built in. Install yaml separately and let parse errors fail startup rather than serving an empty document.
Pass Swagger UI client optionsconfigure-swagger-ui
const options = {
explorer: true,
swaggerOptions: {
validatorUrl: null,
displayRequestDuration: true,
persistAuthorization: false,
},
};
app.use('/api-docs', swaggerUi.serve, swaggerUi.setup(openapi, options));explorer is a wrapper option; Swagger UI runtime settings belong under swaggerOptions.
Brand the documentation pagecustomize-page
const options = {
customSiteTitle: 'Payments API',
customfavIcon: '/assets/favicon.ico',
customCss: '.swagger-ui .topbar { display: none }',
customCssUrl: '/assets/api-docs.css',
};
app.use('/api-docs', swaggerUi.serve, swaggerUi.setup(openapi, options));customCssUrl is resolved by the browser. Make sure Express serves that path and your Content Security Policy permits the stylesheet.
Let the browser load a specification URLload-spec-url
const options = {
swaggerOptions: { url: '/api-docs/openapi.json' },
};
app.get('/api-docs/openapi.json', (_req, res) => res.json(openapi));
app.use(
'/api-docs',
swaggerUi.serveFiles(null, options),
swaggerUi.setup(null, options),
);The browser fetches the URL after page load. Cross-origin URLs need suitable CORS headers and network reachability from the user's browser.
Offer multiple specifications in a dropdownserve-multiple-specs
const options = {
explorer: true,
swaggerOptions: {
urls: [
{ name: 'Public API', url: '/specs/public.json' },
{ name: 'Admin API', url: '/specs/admin.json' },
],
},
};
app.use('/api-docs', swaggerUi.serveFiles(null, options), swaggerUi.setup(null, options));Explorer must be true for the document selector to appear, and every listed URL is fetched by the browser.
Mount two independent Swagger UI routesmount-two-instances
app.use(
'/public-docs',
swaggerUi.serveFiles(publicSpec),
swaggerUi.setup(publicSpec),
);
app.use(
'/admin-docs',
requireAdmin,
swaggerUi.serveFiles(adminSpec),
swaggerUi.setup(adminSpec),
);Use serveFiles per document. Reusing swaggerUi.serve for multiple initialized documents can mix the generated assets and configuration.
Set document data from the requestbuild-request-specific-spec
app.use(
'/api-docs',
(req, _res, next) => {
req.swaggerDoc = {
...openapi,
servers: [{ url: `${req.protocol}://${req.get('host')}` }],
};
next();
},
swaggerUi.serveFiles(),
swaggerUi.setup(),
);Create a new top-level document instead of mutating a shared object with request-specific host data. Trust proxy settings affect req.protocol.
Load a trusted customization scriptinject-custom-script
const options = {
customJs: '/assets/swagger-custom.js',
};
app.use('/api-docs', swaggerUi.serve, swaggerUi.setup(openapi, options));customJs and customJsStr execute in the documentation page. Only load code you control and account for script-src in your Content Security Policy.
Serve a specification produced by swagger-jsdocuse-generated-spec
const swaggerJSDoc = require('swagger-jsdoc');
const spec = swaggerJSDoc({
definition: { openapi: '3.1.0', info: { title: 'Orders API', version: '1.0.0' } },
apis: ['./src/routes/**/*.js'],
});
app.use('/api-docs', swaggerUi.serve, swaggerUi.setup(spec));swagger-jsdoc generates the object; swagger-ui-express only displays it. Add separate validation if the generated contract must be enforced.
Alternatives
| Package | Registry | Pick it when |
|---|---|---|
| @scalar/express-api-reference | npm | Use it for a newer Express-hosted API reference UI with Scalar's presentation and client |
| redoc-express | npm | Use it when readable reference documentation matters more than an interactive request console |
| express-openapi-validator | npm | Use it when the main requirement is enforcing OpenAPI request and response contracts in Express |