Reputation: 111
I'd like to set up an API versioning, similar to how Stripe does it but I'm not quite sure how to make express do what I need. https://stripe.com/docs/api#versioning
What I'm looking for is, the proper route would be something like:
/api/v1/call
The kicker is, I'd like them to pass in a version revision like stripe allows, so if they sent a header like "API-Version: 2015-08-15", it would map to that specific version of the major version. So, v1 but the version updated on 2015-08-15.
Essentially, if there is an update to the API call that is not backwards compatible, I'd roll a new version for that particular call. Express would be smart enough to know that if a version isn't passed, use the latest. If a version is passed, use the latest version for each call up until the version date.
I'd assume the directory structure would be something like:
And maybe in the call directories, there is a index that checks for the header version and uses the proper file.
So maybe for instance
Thoughts? Ideas?
Upvotes: 7
Views: 11101
Reputation: 21
https://medium.com/@vgjohn/node-js-api-versioning-with-totoro-node-c2ea1ef3dfba
There's a small package called totoro-node that helps deal with route management for api versioning. It might help to solve some of the problems you're facing. You just write a simple api definition like this and you can control which endpoints or api versions to deprecate or inherit into subsequent api versions. https://www.npmjs.com/package/totoro-node
var app = express()
app.use('/api', totoro.rain({
v1: {
"/oAuth": {
method: "GET",
deprecated: true,
endpointImplementation: routes.authRoutes.oAuth
},
"/ssoToken": {
method: "GET",
endpointImplementation: routes.authRoutes.sso
}
},
v2: {
"/ssoToken": {
method: "GET",
endpointImplementation: routes.authRoutes.sso
}
}
}))
Upvotes: 1
Reputation: 211
If you are managing version in routes(url) and client sends version in headers then express doesn't provide any elegant way to handle versioning. Also, doing versioning in routes is not restful.
I wrote an simple npm module to solve this problem. https://www.npmjs.com/package/express-routes-versioning
Module allows individual routes to be versioned separately. It is agnostic about specific versioning strategies and allows the application to set the version, so you should be able to parse version from headers and set it to req.version in a middleware. It supports semver versioning format and symbols to map multiple versions to single function. Sample code on how it works.
var app = require('express')();
var versionRoutes = require('express-routes-versioning')();
app.listen(3000);
app.use(function(req, res, next) {
//req.version is used to determine the version
req.version = req.headers['accept-version'];
next();
});
app.get('/users', versionRoutes({
"1.0.0": respondV1,
"~2.2.1": respondV2
}));
// curl -s -H 'accept-version: 1.0.0' localhost:3000/users
// version 1.0.0 or 1.0 or 1 !
function respondV1(req, res, next) {
res.status(200).send('ok v1');
}
//curl -s -H 'accept-version: 2.2.0' localhost:3000/users
//Anything from 2.2.0 to 2.2.9
function respondV2(req, res, next) {
res.status(200).send('ok v2');
}
By default, if the client version doesn't match the version provided in the server, module servers the latest version callback available in that route. This behavior can be overridden by providing an additional callback. More info and source code available at https://github.com/Prasanna-sr/express-routes-versioning
Upvotes: 6
Reputation: 4381
This how I'm handling versioning. Basically you create a new router
object and use app.use
so that only /api/v1
routes are sent to it. I then use a "fall through" route which catches anything which didn't match and returns a unknown command message. I also renamed the res.json
function so that I can add APIversion = 1
to each object that went out (That's in the router.use
function call).
Whenever I have a v2 api I'll do this exact same thing but create a new file and use a different app.use
path. See below:
app.js
....
app.use('/api/v1', require('./api1.js'));
....
api1.js
var express = require('express');
var router = express.Router();
router.use(function (req, res, next) {
res._json = res.json;
res.json = function json(obj) {
obj.APIversion = 1;
res._json(obj);
};
next();
});
/* ADD ALL YOUR ROUTES HERE */
//Done - catch all - return command failed
router.get('*', function (req, res) {
res.status = 404;
res.json({
success: false,
message: 'Unknown command'
});
});
module.exports = router;
Upvotes: 3
Reputation: 1213
I think you could set a middleware before all your routes to check headers.
app.use("*",function (req,res,next) {
var headers = req.headers
//Process
req.apiVersion = "version"
next()
}
//all your routes
this is a example , but you could manipulate headers in your router instance and then pass req to other route
//v1/call/index.js
//all your routes
app.use("/v1/call",function (req,res){
var version = req.apiVersion;
//execute something depending on version
})
Upvotes: 0