NodeJS/Express API versioning - node.js

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:
/router/
/router/v1
/router/v1/call
/router/v1/anotherCall
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
/router/v1/call/index.js
/router/v1/call/20150810.js -- First version
/router/v1/call/20150815.js -- Updated version that isn't backwards compat.
Thoughts? Ideas?

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
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

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;

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
}
}
}))

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
})

Related

Node.js REST API versioning the right way?

I would like to manage my REST API based on URL version specifying.
For example:
api.mydomain.com/v1/rides/
// will return all rides based on v1.
api.mydomain.com/v2/rides/
// will return all rides based on v2 (probably with some braking changes).
api.mydomain.com/rides/
// will return all rides based on v2, since v2 is the newest.
Thats awesome.
Before we get started dealing with the practical way of handling this,
we should talk about the logical "default newest versioning" - I mean, if user does not going to specify any kind of version, should I serve him with the newest version or throw a 404 not found error?
Should I oblige the user for specifying an API version?
If I do, is there any standard of "parsing" the specific / newest version?
I tell you why im concern about this: Lets say that "Dan" have app installed which relays on the newest API endpoint (V1 for example), then I release V2 which has braking changes.
Since Dans "listens" to the newest version by default, Dans app is going to be crashed.
That is not a good behaviour at all.
Maybe should I prevent using the "default newest versioning"?
Maybe should I use Dans app to listen for a specific version, while remote developers accessing my API as a web service can have the privilege to choose between specific version or the newest by default?
Is there any standard?
**
Now lets talk practically. Lets say that I have a router handling those requests, maybe something like this:
// app.js file
app.use((req, res, next) => {
try {
require('../resources/' + req.url.split('/')[1] + '/' + req.url.split('/')[1] + '-router')(app);
next();
} catch(err) {
dep.cast(res, 404, new Error("Not Found"));
}
});
And some handler, like this:
// resources/rides/rides-router.js file
module.exports = function(app) {
// GET ride - select a ride
app.get("/v1/rides/:id", dep.verifyToken(), require('./api/v1/get-ride'));
app.get("/v2/rides/:id", dep.verifyToken(), require('./api/v2/get-ride'));
// POST ride - insert a new ride
app.post("/v1/rides", dep.verifyToken(), require('./api/v1/set-ride'));
}
As you can see, I have handler which sends the requests to the specific divisions in the API, split by V1, V2, etc..
It makes me wonder if its right to have the same page containing the same function over and over in different folders, one for V1 and one for V2.
Ofcourse, with some braking changes, but they are probably going to be similar. Is not it bordering with repetitive code?
Look at the project structure:
What do you think about this?
Instead of adding version in every route you can add it in app level. So It won't be tightly coupled with API route.
import * as express from 'express';
// v1/get-ride.js
const router = express.Router();
router.post('/rides/:id', dep.verifyToken(), (req, res) => {
// Your code
});
app.use('/v1', router);
// v2/get-ride.js
const router = express.Router();
router.post('/rides/:id', dep.verifyToken(), (req, res) => {
// Your code
});
app.use('/v2', router);
I would recommend using node-express-versioning module instead.
It would help you to support multiple versions without changing the url of API, just send the version of API and direct the call to that version route-controller.
*
*//version taken out from header
app.use(function(req, res, next)
{
req.version = req.headers['accept-version'];
console.log(req.version);
next();
});
//version path defined
app.use('/api', versionRoutes({
"1.0.0": respondV1,
"2.0.0": respondV2
}));
function respondV1(req, res, next)
{
app.use('/api',routeV1);
next();
}
function respondV2(req, res, next)
{
app.use('/api',routeV2);
next();
}*
*
There's no "right way" to do API versioning.
However, URI based global versioning is not at all RESTful.
Phil Sturgeon of "APIs you won't hate" recommends an API Evolution approach instead.
See: https://apisyouwonthate.com/blog/api-evolution-for-rest-http-apis
Have you considered this as an option?

Node express api routes for multilingual directory like url

Does any one knows an example or could explain here how node.js and express would have to route for a multilanguage site? I'm using i18n-node for translation and folder like routing ( /es/, /de/ , etc ) for different languages. This all are static routes but I also have routes like apiRoutes.route('/user/profile') using 'app' at the begining ( app.get('/app/user/profile') so please consider this in your answer so is NOT necesary route to : app.get('/es/app/user/profile') .
having 15 routes like this now:
app.get('/terms', function(req, res) {
res.render('terms',{
...
});
});
how it have to be set for routes like:
app.get('/es/terms', function(req, res) {
res.render('terms',{
...
});
});
Should I duplicate this routes and add for example a locale for
each like:
app.get('/es/terms', function(req, res) {
res.render('terms',{
...
});
});
Or Should do something like:
if cookie['lang'] && cookie['lang'] is in locales
// then redirect to /:lang/terms
else
// show default language in /terms
if req.headers["accept-language"] && req.headers["accept-language"]
// then redirect to /:lang/terms
else
//show default language in /terms
Or there is another way I should approach this that follows good practices or is better respecting standards?
Miro's Answer in :
How can I get the browser language in node.js (express.js)? says I should use app.all('*', ...
Is this all I need?, ..still, it might have a syntax error or i'm not understanding well this two parts
var rxLocal = /^\/(de|en)/i;
...
app.get(/\/(de|en)\/login/i, routes.login);
thanks in advance
You need to consider 2 things :
1. How get the local :
Accept-Language
The HTTP protocole define the Accept-Language header to manage the local. This is a normalized method. You can access it with the req.acceptsLanguages method of express.
+Normalized
+Natively support by brower
-Not easy to by passe by the end user
Path / Cookies
You can get the local from the path. In express it can be do with a parameter patter like /:local/rest/of/path and retrieve in the request object with the req.param method.
You can also get the information from the cookies with the req.cookies properties (don't forgot to set it).
Both
To increase the user experience you can mix the both method. For exemple get the default language from the HTTP header send by the browser but permite to the user to override this in you application and store this parameter in the cookies.
2. Use the local:
Each methods to get the local can be used from different way. I will
use random of them in exemple but they are all compatible.
Top level configuration.
In case of you use a template Engine and you controller can be local agnostic. You can use a middleware to get the local information and configure the render engine.
app.use('/:local' (req, res, next) => {
let localKey = req.param('local');
res.locals = // Some ingenious method to get the locales from localKey
next();
}
Check res.locals and your engine documentation.
Use it in controller.
If the local is part of the contoller process. You can get directly is value in controller.
In case of you use a complexe method to determine the final value of the local, you can also use a middleware to determine this value and enrich the request with it.
app.use((req, res, next) => {
let local = req.cookies.local;
if(!local) local = req.acceptsLanguages();
if(!local) local = 'en-US';
req.local = local;
}
Both
You can use both method too. It depend of what you need. Find the best way to get a maintainable code and avoid replication for your use case.
When you use middle where witch impact the controllers, be sure you declare them before your routes.
You can use a route parameter to get the locale from the URL, like this:
app.get('/:lang/terms', function (req, res) {
if (req.params === 'es') {
res.send('¡Hola!');
else {
res.send('Hi!');
}
});
The colon character tells Express to put whatever is between the first to slashes of the path in req.params.lang.
See express routing documentation for details.

Express.js unique var per request outside routing

In my express application I have a module called helpers thats is required in almost all my routes and modules. This module has a logger method that logs to fluentd (but that's unimportant). While building the data to log I'd like to add a unique identifier of the request, so that all the logs written for the same request have the same unique ID. Using a global var in the app entry point app.use doesn't work because this var would be overwritten every time a new request hits, so the global uuid will change would obviously change in case of high load or long running tasks. The res.locals is not available outside routing, so I can't use it for this matter. Is there a way to create a var that would be unique per request and available in every module or maybe a way to access the res.locals data outside routing? Thank you
EDIT
Maybe an example will help understand better the question.
Suppose I have a module called helpers.js like this:
let helpers = {};
helpers.log = (logData, logName) => {
fluentLogger.emit('', {
name: logName,
//uuid: the needed uuid,
message: logData
});
}
module.exports = helpers;
Now obviously I can do this in my app.js entry point:
app.use(function (req, res, next) {
res.locals.uuid = uuid.v4();
next();
});
and then in every loaded middleware module that requires helpers(adding a new param to the helpers.log method):
const helpers = require('helpers');
router.post('/', (req, res, next) => {
helpers.log('my log message', 'myLogName', res.locals.uuid);
next();
});
and this will normally work. But suppose a big or middle size project where there are hundreds of custom modules and models (not middlewares) and a module may require other modules that require other modules that require finally the helpers module. In this case I should pass the res.locals.uuid as a parameter to every method of every method so that I have it available in the logger method. Not a very good idea. Suppose I have a new module called dbmodel.js that is required in a middleware function:
const helpers = require('helpers');
let dbmodel = {};
dbmodel.getSomeData = (someParam) => {
//some logic
helpers.log('my log message', 'myLogName');
}
module.exports = dbmodel;
The dbmodel has no idea about the res.locals data if I don't pass it from the middleware, so the helpers.log method will also have no idea about this.
In PHP one would normally write a GLOBAL var in the application's entry point so a hypothetical logger function would have access to this global on every method request from whichever class of the application.
Hope this explanation will help :) Thank you
EDIT 2
The solution for this kind of problems is CLS. Thanks to #robertklep for the hint. A good slideshare explaining exactly the same problem (logger with unique ID) and explaining the CLS solutions can be found here: https://www.slideshare.net/isharabash/cls-and-asynclistener
I answered a very similar question here which will solve this problem.
I used to solve the problem the libraries node-uuid and continuation-local-storage. Take a look to the answer of this question and see if it helps:
NodeJS Express - Global Unique Request Id
And you want a bigger explanation, take a look here:
Express.js: Logging info with global unique request ID – Node.js
Yes you can do so by one method .
Every request comes to his routes pass that request inside the middleware.
Suppose you have
app.get('/', function(req, res) {
res.sendFile(path.join(public + "index.html"));
});
a request.
Place Middleware in it .and edit req field coming , in this way you will get the unique variable values for each request
check out this .
https://expressjs.com/en/guide/writing-middleware.html
Like this
var requestTime = function (req, res, next) {
req.requestTime = Date.now()
next()
}
app.use(requestTime)
app.get('/', function (req, res) {
var responseText = 'Hello World!<br>'
responseText += '<small>Requested at: ' + req.requestTime + '</small>'
res.send(responseText)
})
Here req.requestTime is unique for each request.

per-request session in meteor server?

I am adding an auth layer and I think I have it figured out except for one tricky detail.
My Meteor app doesn't have any routes but I've added a hook into the connect middleware so that the "/" route errors if there isn't a correct API token. If the token is okay then I call next() to forward the route to Meteor.
The problem is that, depending on the token, I need to set server-side parameters for the connection, and I don't know how to do this. For example, say I have a static list of API keys mapped to permission levels. If a user sends a request with "ADMIN_API_KEY" then I would like to set Session.permission_level = "admin" for use by the Meteor server's functions. Session is just for the client in Meteor, though.
# this code's in coffeescript
WebApp.connectHandlers.use '/', (req, res, next) ->
validator = new RequestValidator(req, next)
validations = [
"valid_namespace",
"only_https"
]
error = validator.validate(validations)
next(error)
# <<<<<<<<<<<<<<<<<<<<<<<<
# Here I want to set some config option which can be
# read by the server in the same way it can read things like
# Meteor.user()
In Rails I would just say session[:permission_level] = "admin". But it seems to not work this way in Meteor.
By the way, I am not using a Routing package yet in Meteor, though if that would make this easier than I would.
I'm not sure about Session I've been doing something like
import { DDP } from 'meteor/ddp';
import { DDPCommon } from 'meteor/ddp-common';
export const authMiddleware = (req, res, next) => {
const userId = identifyUser(req); // parse the request to get the token you expect
if (!userId) {
return next();
}
DDP._CurrentInvocation.withValue(new DDPCommon.MethodInvocation({
isSimulation: false,
userId,
}), () => {
next();
// in that context, Meteor.userId corresponds to userId
});
};
for my REST api and that works well regarding the user Id and being able to call Meteor function that should be invoke in a DDP context, like Users.find(...).

Restify: API version in URL

Currently under development of API with restify and still cannot get used to specifying the API version in headers. It just doesn't seem very user friendly.
Is there any way for version to be part of url?
Example would be:
http://domain.com/api/v1/action
Or even better in my case:
http://api.domain.com/v1/action
Thanks
Also you can use Restify to define your versions:
var server = restify.createServer({
name: 'myAPI',
versions: ['1.0.0', '2.0.0']
});
Then using this middleware with server.pre:
server.pre(function (req, res, next) {
var pieces = req.url.replace(/^\/+/, '').split('/');
var version = pieces[0];
// only if you want to use these routes:
// /api/v1/resource
// /api/v1.0/resource
// /api/v1.0.0/resource
if (!semver.valid(version)) {
version = version.replace(/v(\d{1})\.(\d{1})\.(\d{1})/, '$1.$2.$3');
version = version.replace(/v(\d{1})\.(\d{1})/, '$1.$2.0');
version = version.replace(/v(\d{1})/, '$1.0.0');
}
if (semver.valid(version) && server.versions.indexOf(version) > -1) {
req.url = req.url.replace(version + '/', '');
req.headers['accept-version'] = version;
}
return next();
});
Finally, in your routes you can do something like this:
server.get({ path: '/resource/:id', version: '1.0.0' }, function () {
// send object in version 1.0.0
});
server.get({ path: '/resource/:id', version: '2.0.0' }, function () {
// send object in version 2.0.0
});
Examples:
http://api.domain.com/resource/10
http://api.domain.com/2.0.0/resource/10
http://api.domain.com/1.0.0/resource/10
The above examples follow the standards, because if version is not specified through header or url, shows the last version.
UPDATE:
I made a plugin to have API versions in URL:
https://www.npmjs.com/package/restify-url-semver
People are correct that it's not supported by restify, but I thought I'd throw my solution to this problem into the mix. I'm doing something like this (warning, untested code to follow):
After I create a server, but BEFORE I declare the routes, I register a custom parser to translate the url-style version specifier into an HTTP-style specifier:
server.use(versionParser);
And versionParser.js looks something like this:
var semver = require('semver');
var restify = require('restify');
module.exports = function (req, res, next) {
// we expect every request to have the form "/api/[api-version]/..."
// verify that the api-version is a valid semver value
var urlPieces = req.url.replace(/^\/+/, '').split('/');
var api = urlPieces[0];
var apiVersion = urlPieces[1];
if (api !== 'api' || !semver.valid(apiVersion)) {
return next(new restify.InvalidContentError({message: "Invalid Version Specifier"}));
}
req.header('Accept-Version', apiVersion);
return next();
}
This way, the restify routes can inspect the Accept-Version header as they do naturally.
Sidenote: The semver part is probably not relevant to this answer, but I wanted to verify that the API version in the URL was a valid semver value, as it allows flexibility in the URL values such that a user could take advantage of restify's flexibility in version specifiers.

Resources