This little piece of Express middleware will help split your routes based on a ApiVersion
header. This allows you to deliver different content or handle different business logic for various clients.
My specific use case was that I needed to change the payload and logic of an API endpoint. However, I still needed to support legacy mobile clients that expected the older payload and logic. The solution was to have the clients pass an ApiVersion
header with a proper semver value. This allowed us to ensure the semver value being requested meets the minimum version requirements. Otherwise, it falls back to using the older endpoint logic.
const express = require('express');
const semver = require('semver');
// Middleware
const satisfiesApiVersion = (minApiVersion) => {
return (req, res, next) => {
if (semver.lt(req.headers.apiversion, minApiVersion)) {
return next('route');
}
next();
}
};
const app = express();
// New Content Route
app.get('/my-route', satisfiesApiVersion('2.0.0'), (req, res) => {
res.send('Version 2.0.0 content');
});
// Legacy Content Route
app.get('/my-route', satisfiesApiVersion('1.0.0'), (req, res) => {
res.send('Version 1.0.0 content');
});
app.listen(8080);
We're using the semver
npm package, which provides semver comparison utilities. If the ApiVersion
sent in the headers is less than the minApiVersion
specified on the route, then we call next('route')
which skips the rest of the route and looks for the next matching route. We could have many different versions of this endpoint as long as they are listed newest to oldest.
Example cURL request which receives the legacy content:
> curl --location --request GET 'localhost:8080/my-route' --header 'ApiVersion: 1.6.3'
Version 1.0.0 content
Example cURL request which receives the new content:
> curl --location --request GET 'localhost:8080/my-route' --header 'ApiVersion: 2.8.1'
Version 2.0.0 content
One last note. HTTP headers are case insensitive so ApiVersion
, apiversion
, and ApIvErSiOn
should all be treated the same.