Learn about our RFC process, Open RFC meetings & more.Join in the discussion! »

redimiter

1.0.1 • Public • Published

Redimiter

<img width="320" src="https://i.ibb.co/1rvrF22/redimiter.png" alt="Redimiter">

A simple, customizable, Redis based Express middleware and Node.js rate limiter.

Build Status Coverage Status

Quick Start

via npm

Install

$ npm install redimiter

Basic Usage

import Redimiter from "redimiter";

Connect to Redis

Create a simple Redimiter instance and pass a redis client as an argument.

I recommend the terrific ioredis Redis client. You can also use node_redis.

const redis = new Redis();
 
const redimiter = new Redimeter(redis);

If you pass no arguments to the Redis constructor, as shown above, it will automatically connect to a local Redis server which is great for developement.

Using as express middleware

Simply add it as middleware with no args and it will rate limit 10 requests/second using the client's ip address:

app = express();
 
const { rateLimiter } = redimiter
 
app.get("/pages", rateLimiter(), getPages);

You can easily add customizable rate limiting on each path using the rateLimiter method options argument,

app = express();
 
const { rateLimiter } = redimiter;
 
const pagesOptions = {
  path: "GET/pages",
  expire: 30000,
  limit: 20
};
 
const catsOptions = { path: "/cats" };
 
app.get("/pages", rateLimiter(pagesOptions), getPages);
 
app.post("/cats", rateLimiter(catsOptions), postCats);

group similar requests easily,

app = express();
 
const { rateLimiter } = redimiter;
 
const getLimit = rateLimiter({ path: "/GET" });
 
const postLimit = rateLimiter({ path: "/POST", expire: 3000 });
 
app.get("/pages", getLimit, getPages);
app.get("/books", getLimit, getBooks);
app.get("/chapters", getLimit, getChapters);
 
app.post("/cats", postLimit, postCats);
app.post("/dogs", postLimit, postDogs);
app.post("/bats", postLimit, postBats);

or use a general one size fits all:

app = express();
 
const { rateLimiter } = redimiter;
 
const limit = rateLimiter();
 
app.get("/pages", limit, getPages);
 
app.post("/cats", limit, postCats);

Using as Promise

You can also return a promise:

const { rateLimiterPromise } = redimiter;
const options = {
  username: 'name',
  action: 'createComment',
}
rateLimiterPromise(options)
.then(() => doSomething()}
// rejects with error if client is over rate limit
.catch(err => rateLimterErr(err))

API

Redimiter(redisClient)


Redimiter is a contructor that takes a redis client as an argument.

example

import Redimiter from "redimiter";
import * as Redis from "ioredis";
 
redis = new Redis();
 
redimiter = new Redimiter(redis);

It is tested to work with both ioredis and node_redis clients.


.rateLimiter({options})


.rateLimiter is an Express middleware.

It stores the ip of the client + optional path (ip:path) as a Redis list and for each client request it adds an item. This sum of items can be called the rate score and is compared to the rate limit to determine if the client request should be allowed. Once over the rate limit the score is no longer increased and requests are blocked, unless overdrive is set to true (see below). The list has an expiration time and when it expires the the whole process repeats after the next client request from the same ip.

Option Default Description
path A string that will be appended to the client's ip, used as the key in Redis.
expire 1,000 The miliseconds before Redis key expires.
limit 10 The number at which client requests will be limited.
overdrive false If true keeps count of requests after limit has been surpased. At limit x 10 it will set expire to x1000 and then discontinue counting requests and will simpy block them until expiration. Otherwise if false once the limit is surpassed requests will not be counted and will simply be blocked until the key expires.

examples

With no option object arg, rateLimiter() defaults to 10 requests per second.

app.get("/pages", rateLimiter(), getPages);

When no path is specified, Redimiter uses the clients ip as the redis key. This means that all requests will be counted indesciminate of request type.

To change the limit to 2 per second, simply add:

app.post("/faces", limit({ limit: 2 }), postPages);

With the path option defined, the redis key will be ip:path and will now be able to set seperate limits on different requests and also not have all requests count towards the same rate score.

app.get("/pages", rateLimiter({ path: "get/pages" }), getPages);

With these path and expire values, it defaults to a max of 10 requests in the specified 3 seconds. The 3 seconds will begin at the client's first request and each subsequent request that happens when no matching Redis key exists.

app.get("/pages", rateLimiter({ path: "getPages", expire: 3000 }), getPages);

With these path, expire, and limit values, it allows a max of 20 requests per 4 seconds.

app.get(
  "/pages",
  rateLimiter({ path: "getPages", expire: 4000, limit: 20 }),
  getPages
);

The following rate limiter has overdrive set to true. It will continue to increase the rate score value per request until it gets to 10x the limit, at which it will kick into 'overdrive' and limit the client for 1000x the expire, thus blocking the oboxious user/bot for quite some time... in this case it would be 3000ms x 1000, or 50 minutes. While in 'overdrive' it will discontinue keeping score and simply block requests until the time expires.

app.get(
  "/pages",
  rateLimiter({
    path: "getPages",
    expire: 3000,
    limit: 200,
    overdrive: true
  }),
  getPages
);

.rateLimiterPromise({options})


This method returns a promise and can be used in a Nodejs application anywhere you may need rate limiting but express middleware is not available or appropriate.

Option Default Description
username (REQUIRED) A string that will be used as the key in Redis for the client request.
action (REQUIRED) A string that will be appended to username.
expire 1,000 The miliseconds before redis key expires.
limit 10 The limit at which client requests will be limited
overdrive false If true keeps count of requests after limit has been surpased. At limit x 10 it will set expire to x1000 and then discontinue counting requests and will simpy block them until expiration. Otherwise if false once the limit is surpassed requests will not be counted and will simply be blocked until the key expires.

examples

This works much like .rateLimiter() only here you need to specify the username and action. If the client is over the limit, the promise will reject with an error and the .then() will be skipped.

{ rateLimiterPromise } = redimiter
 
const options = {
  limit: 20,
  username: clientUsername,
  action: 'addComment',
}
 
rateLimiterPromise(options)
  .then(() => addComment())
  .catch((err) => errorHandler(err))

Install

npm i redimiter

DownloadsWeekly Downloads

4

Version

1.0.1

License

MIT

Unpacked Size

263 kB

Total Files

45

Last publish

Collaborators

  • avatar