redimiter
v1.0.1
Published
A Redis based Express middleware and Node.js rate limiter
Downloads
2
Maintainers
Readme
A simple, customizable, Redis based Express middleware and Node.js rate limiter.
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))