A minor bug fix for hapi-swagger

 
Updated 25th December, 2013: The bug fix described below has been merged into the hapi-swagger codebase. The npm package hasn’t yet been updated but I expect it will be in the next few days.

Updated 26th December, 2013: The npm package has now been updated; version 0.0.4 contains this fix.

 

If you’ve recently spent any time at all building a RESTful API using Spumko’s HAPI then it’s more than likely that you’ve come across Swagger. Developed for use on the popular Wordnik site, Swagger is “a specification and complete framework implementation for describing, producing, consuming, and visualizing RESTful web services”. The beauty of the Swagger  approach is that with the hapi-swagger plugin and a few lines of code it’s possible to automatically generate comprehensive, living documentation for your API that is always in sync with your codebase updates… no more excuses for documentation trailing days or weeks behind code changes.

forms_js
Code extract showing the tags used to generate Swagger-based documentation.

 

The Problem

That’s the theory anyway. Unfortunately, there was (and still is) a fairly trivial bug in the hapi-swagger plugin that prevents the documentation being generated as it should.
In the file lib/index.js from line 55 onwards is a small block of code that ensures that Swagger documentation is generated for each defined route:


routes = routes.filter(function (item) {
if (request.query.path &&
item.path.indexOf('/' + request.query.path) !== 0 ) {
return false;
}
return item.settings.plugins['hapi-swagger'] !== false && item.method !== 'options';
 });

What this short snippet of code does is process the routes defined by your app one at a time and selects only those that match a value passed in as request.query.path. For example, if request.query.path equals ‘movies’ then this code will include all routes which begin with the string ‘/movies’. This would mean that we would then generate Swagger formatted documentation for the routes ‘/movies‘, ‘/movies/{id}’, ‘/movies/{id}/actors’ and so on, which is exactly what we want. Unfortunately it would also include routes such as ‘/moviescenes’ which we do not want.

It’s worth mentioning that an earlier version of this code appended an extra “/” symbol to the search string in line 57 so that, instead of ‘/movies’ the route would have to begin with ‘/movies/’. This prevented false positives like ‘/moviescenes’ but, unfortunately, also ignored the top level routes such as ‘/movies’.

The solution

The simplest way to make sure that all the matching routes and only the matching routes generate Swagger documentation is to replace the indexOf in line 57 with a search using a regular expression. With a regular expression, we are able to specify that, for example, only routes which begin ‘/movies/’ be selected (as they were originally) and routes that contain only ‘/movies’. In that way we can make sure we generate documentation for ‘/movies’, ‘/movies/’, ‘/movies/{id}’ and so on, but not ‘/moviescenes’.

The changed code is below (only line 57 has changed):


routes = routes.filter(function (item) {
if (request.query.path &&
item.path.search('^/' + request.query.path + '(/|$)') !== 0 ) {
return false;
}
return item.settings.plugins['hapi-swagger'] !== false && item.method !== 'options';
 });

I have submitted this bug fix to the hapi-swagger repo on Github but if you need it before it is approved and merged (assuming it is accepted) then you can edit the file directly or pull it from my fork of the repo (link below).

I’ve also created a jsFiddle that illustrates the difference between the indexOf approach and the search approach. You can use that to quickly try out some of your own routes.


More:

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s