patova: simple throttling for hapi.js

tl;dr: If you don’t want to read the post check out the sample here.


If you are building an API, it is likely that at some point you will have the need to throttle requests. There might be several reasons for that but the core part is that you want to limit the amount of requests that a certain party (could be a user, ip, etc.) can perform over a period of time.

At Auth0 we are working on limitd, which implements the token bucket algorithm to support throttling scenarios. Since our API v2 uses hapi.js, we also decided to create patova, a limitd plug-in for hapi.js.

This post shows how easy it is to enable this feature in your application.

Requirements

Let’s assume that we have an API that uses basic authentication and we want to limit requests by user.

Implementation

Create a new directory and install the required modules:

> npm i -S hapi patova hapi-auth-basic

Then create a file named server.js with the following code (the sample is based on the hapi-basic-auth readme):

var Hapi = require('hapi');

var users = {
  john: {
    username: 'john',
    password: 'secret',
    name: 'John Doe',
    id: '2133d32a'
  }
};

var server = new Hapi.Server();
server.connection({
  host: 'localhost',
  port: 8000
});

var validate = function (username, password, callback) {
  var user = users[username];
  if (!user) { return callback(null, false); }

  var isValid = password === user.password;
  callback(null, isValid, { id: user.id, name: user.name });
};

var plugins = [ require('hapi-auth-basic') ];

server.register(plugins, function (err) {
  server.auth.strategy('simple', 'basic', { validateFunc: validate });
  server.route({
    method: 'GET',
    path: '/',
    config: {
      auth: 'simple',
      handler: function(request, reply){
        reply(request.auth.credentials.name);
      }
    }
  });
});

server.start();

Start the server node server.js and perform a request to make sure everything is working:

> curl http://127.0.0.1:8000 -H "Authorization: Basic am9objpzZWNyZXQ="
John Doe

Create a file named limitd.config to hold limitd’s configuration:

#port to listen
port: 9001

#path where the data will be stored
db: /tmp/hapi-throttle-example.db

#Define the types of buckets
buckets:
  user:
    size: 5
    per_second: 5

And start the limitd server:

> node_modules/patova/node_modules/limitd/bin/limitd --config-file limitd.config

Finally, add the patova plug-in to the hapi server:

var patovaConfig = {
  register: require('patova'),
  options: {
    event: 'onPostAuth',
    type: 'user',
    address: { host: '127.0.0.1', port: 9001 },
    extractKey: function(request, reply, done){
      var key = request.auth.credentials.id;
      done(null, key);
    }
  },
};

plugins.push(patovaConfig);

That’s all there is to it!

Trying it out

Let’s make sure everything is working. Restart the server and perform 6 requests (remember we configured 5 req/sec as the limit):

for i in {1..6}                                                      
do
  curl http://127.0.0.1:8000 -H "Authorization: Basic am9objpzZWNyZXQ=" | xargs echo
done

The last one should display the following

{statusCode:429,error:Too Many Requests}

As you can see, adding throttling to you hapi.js API is very simple. If you have questions or comments feel free to post them here or as issues in the project.