Rate limiter middleware, backed by Redis
Branch: master
Clone or download
Fetching latest commit…
Cannot retrieve the latest commit at this time.
Permalink
Type Name Latest commit message Commit time
Failed to load latest commit information.
lib - add support for ioredis Nov 1, 2018
test - add support for ioredis Nov 1, 2018
.gitignore - add support for ioredis Nov 1, 2018
.npmignore Exclude unnecessary files from npm package Oct 3, 2014
.travis.yml Drop node v4 support Nov 3, 2018
README.md Fix repository organization. Nov 30, 2017
package-lock.json 1.2.0 Nov 4, 2018
package.json 1.2.0 Nov 4, 2018

README.md

redis-rate-limiter

NPM License

Build Status Dependencies Dev dependencies Known Vulnerabilities

Rate-limit any operation, backed by Redis.

  • Inspired by ratelimiter
  • But uses a fixed-window algorithm
  • Great performance (>10000 checks/sec on local redis)
  • No race conditions

Very easy to plug into Express or Restify to rate limit your Node.js API.

Usage

Step 1: create a Redis connection

var redis = require('redis');
var client = redis.createClient(6379, 'localhost', {enable_offline_queue: false});

Step 2: create your rate limiter

var rateLimiter = require('redis-rate-limiter');
var limit = rateLimiter.create({
  redis: client,
  key: function(x) { return x.id },
  rate: '100/minute'
});

And go

limit(request, function(err, rate) {
  if (err) {
    console.warn('Rate limiting not available');
  } else {
    console.log('Rate window: '  + rate.window);  // 60
    console.log('Rate limit: '   + rate.limit);   // 100
    console.log('Rate current: ' + rate.current); // 74
    if (rate.over) {
      console.error('Over the limit!');
    }
  }
});

Options

redis

A pre-created Redis client. Make sure offline queueing is disabled.

var client = redis.createClient(6379, 'localhost', {
  enable_offline_queue: false
});

key

The key is how requests are grouped for rate-limiting. Typically, this would be a user ID, a type of operation.

You can also specify any custom function:

// rate-limit each user separately
key: function(x) { return x.user.id; }

// rate limit per user and operation type
key: function(x) { return x.user.id + ':' + x.operation; }

// rate limit everyone in the same bucket
key: function(x) { return 'single-bucket'; }

You can also use the built-in ip shorthand, which gets the remote address from an HTTP request.

key: 'ip'

window

This is the duration over which rate-limiting is applied, in seconds.

// rate limit per minute
window: 60

// rate limit per hour
window: 3600

Note that this is not a rolling window. If you specify 10 requests / minute, a user would be able to execute 10 requests at 00:59 and another 10 at 01:01. Then they won't be able to make another request until 02:00.

limit

This is the total number of requests a unique key can make during the window.

limit: 100

rate

Rate is a shorthand notation to combine limit and window.

rate: '10/second'
rate: '100/minute'
rate: '1000/hour'

Or the even shorter

rate: '10/s'
rate: '100/m'
rate: '100/h'

Note: the rate is parsed ahead of time, so this notation doesn't affect performance.

HTTP middleware

This package contains a pre-built middleware, which takes the same options

var rateLimiter = require('redis-rate-limiter');

var middleware = rateLimiter.middleware({
  redis: client,
  key: 'ip',
  rate: '100/minute'
});

server.use(middleware);

It rejects any rate-limited requests with a status code of HTTP 429, and an empty body.

Note: if you want to rate limit several routes individually, don't forget to use the route name as part of the key, for example using Restify:

function ipAndRoute(req) {
  return req.connection.remoteAddress + ':' + req.route.name;
}

server.get(
  {name: 'routeA', path: '/a'},
  rateLimiter.middleware({redis: client, key: ipAndRoute, rate: '10/minute'}),
  controllerA
);

server.get(
  {name: 'routeB', path: '/b'},
  rateLimiter.middleware({redis: client, key: ipAndRoute, rate: '20/minute'}),
  controllerB
);