Swagger, or OpenAPI, is a spec for describing REST APIs. When you’re building an API with Express in Node.js, you can auto-generate these docs from your code, which is super handy.
Let’s see it in action. Imagine you have a simple Express app:
const express = require('express');
const swaggerJsdoc = require('swagger-jsdoc');
const swaggerUi = require('swagger-ui-express');
const app = express();
const port = 3000;
// Swagger definition
const options = {
definition: {
openapi: '3.0.0',
info: {
title: 'My Express API',
version: '1.0.0',
description: 'A simple API to demonstrate Swagger generation'
},
servers: [
{
url: 'http://localhost:3000'
}
]
},
apis: ['./routes/*.js'] // Path to your route files
};
const specs = swaggerJsdoc(options);
// Routes
app.get('/', (req, res) => {
res.send('Hello World!');
});
// Swagger UI
app.use('/api-docs', swaggerUi.serve, swaggerUi.setup(specs));
// Example route with JSDoc comments
/**
* @swagger
* /users:
* get:
* summary: Get a list of users
* responses:
* 200:
* description: A list of users
* content:
* application/json:
* schema:
* type: array
* items:
* type: object
* properties:
* id:
* type: integer
* name:
* type: string
*/
app.get('/users', (req, res) => {
res.json([
{ id: 1, name: 'Alice' },
{ id: 2, name: 'Bob' }
]);
});
app.listen(port, () => {
console.log(`App listening at http://localhost:${port}`);
});
When you run this and navigate to /api-docs in your browser, you’ll see a beautifully rendered documentation page for your /users endpoint. This page isn’t just static HTML; it’s interactive. You can expand endpoints, see their request/response schemas, and even try them out directly from the UI.
The magic happens with swagger-jsdoc and swagger-ui-express. swagger-jsdoc parses JSDoc comments in your route files (specified in apis) and converts them into a OpenAPI (Swagger) JSON specification. swagger-ui-express then takes this JSON spec and serves it through a web interface.
The core problem this solves is the disconnect between your API’s actual implementation and its documentation. Without auto-generation, documentation quickly becomes outdated. With it, your docs are always a reflection of your code. You control the details by adding structured JSDoc comments to your route handlers. Each comment block, starting with /** @swagger ... */, defines an endpoint, its HTTP method, summary, parameters, and detailed response schemas, including the types and properties of the data returned.
The apis option in swaggerJsdoc is crucial. It tells the library where to look for your route files. You can specify individual files, directories, or use glob patterns like './routes/**/*.js' to include all .js files within a routes directory and its subdirectories. This allows you to keep your route definitions and their API documentation comments together, making maintenance much cleaner.
What most people don’t realize is how granular you can get with the JSDoc comments. You’re not limited to just describing simple responses. You can define complex nested objects, arrays of objects, specify required fields, add examples, and even define security schemes and authentication methods. For instance, defining a POST request body would look something like this within the JSDoc:
/**
* @swagger
* /users:
* post:
* summary: Create a new user
* requestBody:
* required: true
* content:
* application/json:
* schema:
* type: object
* properties:
* name:
* type: string
* description: The name of the user
* email:
* type: string
* format: email
* description: The email address of the user
* required:
* - name
* - email
* responses:
* 201:
* description: User created successfully
* content:
* application/json:
* schema:
* $ref: '#/components/schemas/User' // Referencing a schema defined elsewhere
*/
This example shows how to define a request body for creating a user, specifying required fields and their types. The $ref property is particularly powerful for reusing schema definitions across multiple endpoints, keeping your documentation DRY (Don’t Repeat Yourself).
The next step is to integrate this into your CI/CD pipeline to automatically validate your API specification against your code.