API Versioning Strategies
API versioning lets you evolve your backend without breaking existing clients. When you change a response shape, rename a field, or deprecate an endpoint, older clients should continue to work until they migrate. Express supports multiple versioning strategies — URI path versioning, query parameter versioning, header versioning, and Accept-header (media type) versioning — each with different trade-offs in visibility, cacheability, and client ergonomics.
URI Path Versioning
The most common strategy: embed the version in the URL path, e.g., /api/v1/users and /api/v2/users. Clients know exactly which version they are calling, URLs are bookmarkable and cache-friendly, and server logs make version usage immediately visible. The downside is that URIs should ideally identify resources, not API contracts, but this pragmatic trade-off is widely accepted.
// Route-level URI versioning
const v1Router = require('./routes/v1');
const v2Router = require('./routes/v2');
app.use('/api/v1', v1Router);
app.use('/api/v2', v2Router);
// routes/v1/users.js
router.get('/users', (req, res) => {
res.json({ users: getUsersV1() }); // flat array
});
// routes/v2/users.js
router.get('/users', (req, res) => {
res.json({ // paginated envelope
data: getUsersV2(),
meta: { page: 1, total: 200 }
});
});Header and Query Parameter Versioning
Header versioning uses a custom request header such as X-API-Version: 2 or Accept: application/vnd.myapp.v2+json. Query parameter versioning appends ?version=2 to the URL. Both keep the canonical resource URL clean but are less visible in browser address bars and harder to test without tools like curl or Postman.
// Header-based versioning middleware
function versionMiddleware(req, res, next) {
const version = req.headers['x-api-version'] || '1';
req.apiVersion = parseInt(version, 10);
next();
}
app.use(versionMiddleware);
app.get('/api/users', (req, res) => {
if (req.apiVersion >= 2) {
return res.json({ data: getUsersV2(), meta: {} });
}
res.json(getUsersV1());
});
// Query parameter versioning
app.get('/api/users', (req, res) => {
const v = parseInt(req.query.version || '1', 10);
res.json(v >= 2 ? { data: getUsersV2() } : getUsersV1());
});