Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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

like image 225
Tomas Avatar asked Jan 26 '14 15:01

Tomas


2 Answers

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

like image 98
Marco Godínez Avatar answered Oct 04 '22 02:10

Marco Godínez


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.

like image 29
thataustin Avatar answered Oct 04 '22 02:10

thataustin