Express API Documentation
Introduction
Proper documentation is a crucial aspect of API development that is often overlooked. Well-documented APIs are easier to understand, use, maintain, and troubleshoot. In this guide, we'll explore different approaches to documenting Express REST APIs, from simple README files to advanced automated documentation tools. Whether you're working on a personal project or a large team application, good documentation will save time and reduce frustration for both you and your API consumers.
Why Document Your API?
Before diving into the 'how', let's understand why API documentation is essential:
- Usability: Clear documentation helps other developers understand how to use your API correctly
- Maintainability: Documentation serves as a reference for future maintenance
- Onboarding: New team members can get up to speed faster with well-documented code
- Testing: Documentation can serve as a basis for creating test cases
- Client Development: Frontend or mobile developers can start building against your API even before it's fully implemented
Basic Documentation Approaches
Method 1: README Files
The simplest approach is to document your API endpoints in your project's README.md file. This is suitable for small projects with few endpoints.
## API Endpoints
### Users
- `GET /api/users` - Get all users
- Query parameters:
- `limit` (optional): Limit the number of results
- `page` (optional): Page number for pagination
- Response: Array of user objects
- `GET /api/users/:id` - Get a specific user
- Response: User object or 404 error
- `POST /api/users` - Create a new user
- Body: { "name": "John Doe", "email": "[email protected]", "password": "secret" }
- Response: Created user object and 201 status code
Method 2: Separate API Documentation File
For larger APIs, create a dedicated API.md
file in your project repository with more details:
# API Documentation
## Authentication
All endpoints except `/api/auth/login` require a valid JWT token sent as an Authorization header:
`Authorization: Bearer <your-token>`
## Endpoints
### Authentication
#### POST /api/auth/login
Authenticates a user and returns a JWT token.
**Request Body:**
```json
{
"email": "[email protected]",
"password": "password123"
}
Success Response:
{
"token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
"user": {
"id": "123",
"name": "John Doe",
"email": "[email protected]"
}
}
## JSDoc for API Documentation
JSDoc is a markup language that can be used to annotate JavaScript code with documentation. When combined with Express, it provides a clean way to document your API endpoints directly in your code.
Here's how to use JSDoc with Express routes:
```javascript
/**
* @api {get} /users Get all users
* @apiName GetUsers
* @apiGroup User
* @apiVersion 1.0.0
*
* @apiQuery {Number} [limit=10] The maximum number of users to return
* @apiQuery {Number} [page=1] The page number for pagination
*
* @apiSuccess {Object[]} users List of user objects
* @apiSuccess {String} users._id User's unique ID
* @apiSuccess {String} users.name User's name
* @apiSuccess {String} users.email User's email
* @apiSuccess {Date} users.createdAt Registration date
*
* @apiError (404) {String} message No users found
*/
router.get('/users', async (req, res) => {
const limit = parseInt(req.query.limit) || 10;
const page = parseInt(req.query.page) || 1;
try {
const users = await User.find()
.limit(limit)
.skip((page - 1) * limit);
if (users.length === 0) {
return res.status(404).json({ message: 'No users found' });
}
res.json(users);
} catch (err) {
res.status(500).json({ message: err.message });
}
});
You can use tools like jsdoc-to-markdown
to convert these comments into markdown documentation automatically.
Automated Documentation with Swagger/OpenAPI
Swagger (now known as OpenAPI) is a powerful tool for documenting REST APIs. It not only generates documentation but also provides an interactive UI to test your endpoints directly from the documentation page.
Setting Up Swagger in Express
First, install the required packages:
npm install swagger-jsdoc swagger-ui-express
Then, set up Swagger in your Express app:
const express = require('express');
const swaggerJsDoc = require('swagger-jsdoc');
const swaggerUi = require('swagger-ui-express');
const app = express();
// Swagger configuration
const swaggerOptions = {
swaggerDefinition: {
openapi: '3.0.0',
info: {
title: 'My API Documentation',
version: '1.0.0',
description: 'Documentation for my Express REST API',
contact: {
name: 'Your Name',
email: '[email protected]'
}
},
servers: [
{
url: 'http://localhost:3000',
description: 'Development server'
}
],
components: {
securitySchemes: {
bearerAuth: {
type: 'http',
scheme: 'bearer',
bearerFormat: 'JWT'
}
}
}
},
apis: ['./routes/*.js'] // Path to the API routes files
};
const swaggerDocs = swaggerJsDoc(swaggerOptions);
app.use('/api-docs', swaggerUi.serve, swaggerUi.setup(swaggerDocs));
Documenting Routes with Swagger
Now, you can document your routes using Swagger annotations:
/**
* @swagger
* /api/users:
* get:
* summary: Returns a list of users
* description: Retrieves a list of users with optional pagination
* parameters:
* - in: query
* name: limit
* schema:
* type: integer
* description: The maximum number of users to return
* - in: query
* name: page
* schema:
* type: integer
* description: The page number for pagination
* responses:
* 200:
* description: A list of users
* content:
* application/json:
* schema:
* type: array
* items:
* type: object
* properties:
* id:
* type: string
* name:
* type: string
* email:
* type: string
* 404:
* description: No users found
*/
router.get('/users', async (req, res) => {
// Route implementation
});
After setting this up, you can access your API documentation at http://localhost:3000/api-docs
.
Real-world Example: Building a Complete API Documentation
Let's walk through a more complete example of documenting a blog API with Swagger.
Project Setup
// app.js
const express = require('express');
const mongoose = require('mongoose');
const swaggerJsDoc = require('swagger-jsdoc');
const swaggerUi = require('swagger-ui-express');
const app = express();
app.use(express.json());
// Connect to MongoDB
mongoose.connect('mongodb://localhost:27017/blog_api')
.then(() => console.log('Connected to MongoDB'))
.catch(err => console.error('MongoDB connection error:', err));
// Swagger configuration
const swaggerOptions = {
swaggerDefinition: {
openapi: '3.0.0',
info: {
title: 'Blog API',
version: '1.0.0',
description: 'A simple blog API'
},
servers: [
{
url: 'http://localhost:3000',
description: 'Development server'
}
]
},
apis: ['./routes/*.js', './models/*.js']
};
const swaggerDocs = swaggerJsDoc(swaggerOptions);
app.use('/api-docs', swaggerUi.serve, swaggerUi.setup(swaggerDocs));
// Routes
app.use('/api/posts', require('./routes/posts'));
const PORT = process.env.PORT || 3000;
app.listen(PORT, () => console.log(`Server running on port ${PORT}`));
module.exports = app;
Defining the Model with Swagger Annotations
// models/Post.js
const mongoose = require('mongoose');
/**
* @swagger
* components:
* schemas:
* Post:
* type: object
* required:
* - title
* - content
* - author
* properties:
* id:
* type: string
* description: Auto-generated ID of the post
* title:
* type: string
* description: Post title
* content:
* type: string
* description: Post content
* author:
* type: string
* description: Name of the author
* createdAt:
* type: string
* format: date-time
* description: Date the post was created
* example:
* id: 60d21b4667d0d8992e610c85
* title: My First Blog Post
* content: This is the content of my first blog post
* author: John Doe
* createdAt: 2021-06-22T09:12:28.546Z
*/
const PostSchema = new mongoose.Schema({
title: {
type: String,
required: true
},
content: {
type: String,
required: true
},
author: {
type: String,
required: true
},
createdAt: {
type: Date,
default: Date.now
}
});
module.exports = mongoose.model('Post', PostSchema);
Documenting Routes with Swagger
// routes/posts.js
const express = require('express');
const router = express.Router();
const Post = require('../models/Post');
/**
* @swagger
* /api/posts:
* get:
* summary: Get all blog posts
* description: Retrieve a list of all blog posts
* responses:
* 200:
* description: A list of posts
* content:
* application/json:
* schema:
* type: array
* items:
* $ref: '#/components/schemas/Post'
*/
router.get('/', async (req, res) => {
try {
const posts = await Post.find();
res.json(posts);
} catch (err) {
res.status(500).json({ message: err.message });
}
});
/**
* @swagger
* /api/posts/{id}:
* get:
* summary: Get a post by ID
* description: Retrieve a specific blog post by its ID
* parameters:
* - in: path
* name: id
* required: true
* schema:
* type: string
* description: The post ID
* responses:
* 200:
* description: A single post object
* content:
* application/json:
* schema:
* $ref: '#/components/schemas/Post'
* 404:
* description: Post not found
*/
router.get('/:id', async (req, res) => {
try {
const post = await Post.findById(req.params.id);
if (!post) {
return res.status(404).json({ message: 'Post not found' });
}
res.json(post);
} catch (err) {
res.status(500).json({ message: err.message });
}
});
/**
* @swagger
* /api/posts:
* post:
* summary: Create a new post
* description: Create a new blog post
* requestBody:
* required: true
* content:
* application/json:
* schema:
* type: object
* properties:
* title:
* type: string
* description: Post title
* example: My New Blog Post
* content:
* type: string
* description: Post content
* example: This is the content of my new blog post
* author:
* type: string
* description: Name of the author
* example: John Doe
* responses:
* 201:
* description: Created post object
* content:
* application/json:
* schema:
* $ref: '#/components/schemas/Post'
* 400:
* description: Invalid request data
*/
router.post('/', async (req, res) => {
const post = new Post({
title: req.body.title,
content: req.body.content,
author: req.body.author
});
try {
const newPost = await post.save();
res.status(201).json(newPost);
} catch (err) {
res.status(400).json({ message: err.message });
}
});
module.exports = router;
Best Practices for API Documentation
- Keep it updated: Outdated documentation is worse than no documentation at all
- Be consistent: Use a consistent style and format throughout your documentation
- Include examples: Provide request and response examples for each endpoint
- Document errors: Clearly document possible error responses and status codes
- Use versioning: Include API version information in your documentation
- Explain authentication: Clearly explain how authentication works
- Add context: Give context about when and why to use specific endpoints
- Use proper status codes: Document which HTTP status codes your API returns
Documentation Tools Comparison
Tool | Pros | Cons | Best For |
---|---|---|---|
README/Markdown | Simple, no setup | Manual updates, limited functionality | Small projects |
JSDoc | In-code documentation | Requires a generator for prettier output | Medium projects |
Swagger/OpenAPI | Interactive UI, industry standard | More complex setup | Professional projects |
Postman | Great for testing and sharing | Not directly integrated with code | Team collaboration |
Slate | Beautiful static documentation | Requires separate build process | Public APIs |
Summary
Documenting your Express APIs is a crucial part of the development process that ensures your API is usable, maintainable, and professional. We've explored multiple approaches, from simple README files to sophisticated tools like Swagger/OpenAPI.
For small projects, a well-written markdown file might be sufficient. As your API grows, consider using automated tools like Swagger to maintain comprehensive and interactive documentation. Remember that the best documentation is kept up-to-date and written with its users in mind.
Additional Resources
- OpenAPI Specification
- Swagger UI Express
- JSDoc Documentation
- Postman Learning Center
- API Documentation Best Practices
Exercises
- Create a simple Express API with 3-4 endpoints and document it using markdown in a README file.
- Update your API to include JSDoc comments for each route.
- Implement Swagger/OpenAPI documentation for your API and test the interactive documentation.
- Create a Postman collection for your API and export it as documentation.
- Review a public API's documentation and identify three things they do well and one thing they could improve.
If you spot any mistakes on this website, please let me know at [email protected]. I’d greatly appreciate your feedback! :)