@lob/steve

1.0.0 • Public • Published

redis-rate-limiter

NPM License

Build Status Dependencies Dev dependencies

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 limits = { window: 60, limit: { test: 100, live: 1000 }};
var limit = rateLimiter.create({
  redis: client,
  key: function(x) { return x.id },
  rate: '100/minute'
}
limits);

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... There are several helpers built-in:

// identify users by IP
key: 'ip'

// identify users by their IP network (255.255.255.0 mask)
key: 'ip/32'

// identify users by the X-Forwarded-For header
// careful: this is just an HTTP header and can easily be spoofed
key: 'x-forwarded-for'

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'; }

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.

You can also specify any custom function:

// rate-limit different users
limit: function (x, limits) {
  return {
    limit: x.is_test ? limits.test : limits.live,
    window: limits.window ? limits.window : 60, // default to 60s
  }
}

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

Readme

Keywords

none

Package Sidebar

Install

npm i @lob/steve

Weekly Downloads

5

Version

1.0.0

License

MIT

Last publish

Collaborators

  • erik.forsman-lob
  • jkleung11
  • tanya.sah
  • hunteryoakum
  • rdimouro7373
  • lobstertroy
  • joshnkoy
  • michel_lob
  • haroutrs
  • kjones_lob
  • eamon-barisone
  • nathanielwaldschmidtlob
  • zach.reed
  • richpodrazalob
  • kencrim
  • jorgelob
  • nick-place-lob
  • andrew.guterres
  • ajorczak
  • hanqingchen-lob
  • juan.frissdekereki
  • mmorgan-lob
  • karankwatra-lob
  • joey-bates-lob
  • luke.birdeau
  • mg-lob
  • vmangwani
  • sachinlob
  • nick.perri
  • siddharthpant92
  • bethqiang
  • kplob
  • samkitsheth95
  • erin-doyle
  • rich_seviora_lob
  • jfdavidson
  • meussdorffer
  • shannamurry
  • davidnutting-lob
  • amaan_lob
  • team.platform.lob.com
  • elijah-lob
  • jlowsley-lob
  • barnabygo
  • james.cho
  • douglaje
  • lob-owner
  • graeme.lowe.lob