0.0.1 • Published 2 years ago

@kontsedal/node-burst v0.0.1

Weekly downloads
-
License
MIT
Repository
github
Last release
2 years ago

Buuild and Test Coverage Badge

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:

  1. 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.
  2. 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.
  3. 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:

image

API

checkRateLimit

Params object:

parametertypeisRequireddescription
params.namestringtrueName of the rate limit
params.identifierstringfalseSpecific case of the limit, for example: user id, IP, email, phone number etc. Defaults to "global"
params.limitnumbertrueMaximum requests amount to allow for a time period
params.periodobjecttrueRate limit time period
params.period.daysnumberfalse
params.period.hoursnumberfalse
params.period.minutesnumberfalse
params.period.secondsnumberfalse
params.period.millisecondsnumberfalse
params.requestTimenumberfalseTime in milliseconds of the request. Defaults to current time
params.redisClientRedisClienttrueioredis instance
params.keyPrefixstringfalsePrefix for redis keys. Defaults to "NB-rate"

Result object:

keytypedescription
allowedboolis request allowed
listLengthnumberlist length when a request happened
lastCallTimestampnumber or undefinedlast call timestamp when a request happened
firstCallTimestampnumber or undefinedfirst call timestamp when a request happened
0.0.1

2 years ago