0.0.1 • Published 2 years ago
@kontsedal/node-burst v0.0.1
node-burst
A small utility library to implement rate limits in Node.js applications, based on redis.
Installation
npm i @kontsedal/node-burst
Usage
Rate limit function
Let's say you don't want the same phone number to attempt a registration more than 3 times per day. You can do it like this:
import Redis from "ioredis";
import { checkRateLimit } from "@kontsedal/node-burst";
const redis = new Redis();
export async function checkRegistrationRateLimit(phoneNumber: string) {
const result = await checkRateLimit({
redisClient: redis,
name: "registration",
identifier: phoneNumber,
limit: 3,
period: { days: 1 },
});
if (!result.allowed) {
throw new Error("Too many attempts");
}
}
Rate limit express endpoint
In this example, we will rate limit an endpoint to 1000 requests per hour.
const express = require("express");
const Redis = require("ioredis");
const { checkRateLimit } = require("@kontsedal/node-burst");
const app = express();
const port = 3000;
const redis = new Redis();
app.get(
"/",
rateLimitEndpoint({ limit: 1000, period: { hours: 1 } }),
(req, res) => {
res.send("Hello World!");
}
);
app.listen(port, () => {
console.log(`Example app listening on port ${port}`);
});
function rateLimitEndpoint({ limit, period }) {
return async (req, res, next) => {
const result = await checkRateLimit({
redisClient: redis,
name: `[${req.method}]${req.route.path}`,
limit,
period,
});
if (!result.allowed) {
res.status(429).send("Too many attempts");
} else {
next();
}
};
}
Rate limit express endpoint for an IP address
In this example, we will rate limit an endpoint to 1000 requests per hour for an IP address.
const express = require("express");
const Redis = require("ioredis");
const { checkRateLimit } = require("@kontsedal/node-burst");
const app = express();
const redis = new Redis();
const port = 3000;
app.get(
"/",
rateLimitEndpointForIp({ limit: 1000, period: { hours: 1 } }),
(req, res) => {
res.send("Hello World!");
}
);
app.listen(port, () => {
console.log(`Example app listening on port ${port}`);
});
function rateLimitEndpointForIp({ limit, period }) {
return async (req, res, next) => {
const result = await checkRateLimit({
redisClient: redis,
name: `[${req.method}]${req.route.path}`,
identifier: req.ip,
limit,
period,
});
if (!result.allowed) {
res.status(429).send("Too many attempts");
} else {
next();
}
};
}
How it works
This library uses redis to store a list of request timestamps. When a request comes in, there are three possible cases:
- The list is empty, or the length of the list is less than the limit. In this case, the request is allowed and the current timestamp is added to the list.
- The length of the list is equal to the limit and the oldest timestamp is older than the period. In this case, the request is allowed and the current timestamp is added to the list, the oldest timestamp is removed from the list.
- The length of the list is equal to the limit and the oldest timestamp is not older than the period. In this case, the request is not allowed.
Example:
API
checkRateLimit
Params object:
parameter | type | isRequired | description |
---|---|---|---|
params.name | string | true | Name of the rate limit |
params.identifier | string | false | Specific case of the limit, for example: user id, IP, email, phone number etc. Defaults to "global" |
params.limit | number | true | Maximum requests amount to allow for a time period |
params.period | object | true | Rate limit time period |
params.period.days | number | false | |
params.period.hours | number | false | |
params.period.minutes | number | false | |
params.period.seconds | number | false | |
params.period.milliseconds | number | false | |
params.requestTime | number | false | Time in milliseconds of the request. Defaults to current time |
params.redisClient | RedisClient | true | ioredis instance |
params.keyPrefix | string | false | Prefix for redis keys. Defaults to "NB-rate" |
Result object:
key | type | description |
---|---|---|
allowed | bool | is request allowed |
listLength | number | list length when a request happened |
lastCallTimestamp | number or undefined | last call timestamp when a request happened |
firstCallTimestamp | number or undefined | first call timestamp when a request happened |
0.0.1
2 years ago