npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

distrilimit

v1.1.0

Published

Distrilimit is a TypeScript rate-limiting middleware for Express. It exposes a simple `rateLimit(options)` API and lets you choose the storage backend.

Readme

Distrilimit

Distrilimit is a TypeScript rate-limiting middleware for Express. It exposes a simple rateLimit(options) API and lets you choose the storage backend.

Badges

Install

npm install distrilimit

What You Get

Distrilimit currently supports these strategies:

  • token-bucket
  • sliding-window-log
  • sliding-window-counter
  • fixed-winodw
  • leaky-bucket

Store choices:

  • MemoryStore as the default when no store is passed
  • RedisStore for production or shared limits

Package Imports

If you want to import the helpers directly, use this public API:

export { RedisStore } from "./store/RedisStore";
export { MemoryStore } from "./store/MemoryStore";
export { rateLimit } from "./rateLimit";

Then you can do:

import {
  rateLimit,
  RedisStore,
  MemoryStore,
} from "distrilimit";

Quick Start

The most common usage is to install the package and use it directly in an Express app.

import express from "express";
import { rateLimit } from "distrilimit";

const app = express();

app.use(
  rateLimit({
    strategy: "token-bucket",
    capacity: 10,
    refillRatePerSecond: 2,
  })
);

app.get("/profile", (req, res) => {
  res.send("ok");
});

app.listen(3000, () => {
  console.log("Server listening on 3000");
});

If you do not pass a store, Distrilimit uses MemoryStore automatically.

How To Use It

1. Default MemoryStore

Use this for local development, testing, or single-process apps.

import express from "express";
import { rateLimit } from "distrilimit";

const app = express();

app.use(
  rateLimit({
    strategy: "leaky-bucket",
    capacity: 5,
    leakRatePerSecond: 1,
  })
);

app.listen(3000);

2. RedisStore For Production

Use this when you want shared limits across multiple app instances.

import express from "express";
import { rateLimit, RedisStore } from "distrilimit";

const app = express();

const store = new RedisStore("127.0.0.1", 6379);

app.use(
  rateLimit({
    strategy: "leaky-bucket",
    capacity: 5,
    leakRatePerSecond: 1,
    store,
  })
);

app.listen(3000);

3. Fixed Window

Use the current fixed-window strategy literal from the codebase.

import express from "express";
import { rateLimit } from "distrilimit";

const app = express();

app.use(
  rateLimit({
    strategy: "fixed-winodw",
    capacity: 100,
    windowSizeMs: 60_000,
  })
);

app.listen(3000);

4. Sliding Window Counter

import express from "express";
import { rateLimit } from "distrilimit";

const app = express();

app.use(
  rateLimit({
    strategy: "sliding-window-counter",
    capacity: 100,
    windowSizeMs: 60_000,
  })
);

app.listen(3000);

Strategy Reference

Token Bucket

Best when you want burst tolerance.

Options:

  • capacity: maximum tokens in the bucket
  • refillRatePerSecond: tokens added every second

Sliding Window Log

Best when you want exact per-window tracking.

Options:

  • capacity: max requests in the window
  • windowSizeMs: size of the moving window

Sliding Window Counter

Best when you want a cheaper approximation than the log.

Options:

  • capacity: max requests in the window
  • windowSizeMs: size of the moving window

Fixed Window

Best when you want simple rate limiting behavior.

Options:

  • capacity: max requests in the window
  • windowSizeMs: size of the window

Leaky Bucket

Best when you want smooth output with controlled draining.

Options:

  • capacity: max queue/bucket size
  • leakRatePerSecond: leak rate in requests per second

Response Shape

The middleware sets standard rate-limit headers:

  • RateLimit-Limit
  • RateLimit-Remaining
  • RateLimit-Reset
  • Retry-After

If the request is blocked, the response result includes:

{
  allowed: boolean;
  retryAfterMs: number;
  limit: number;
  remaining: number;
}

Custom Key Generator

By default, Distrilimit identifies clients using req.ip.

You can provide a custom keyGenerator to rate limit by user ID, API key, tenant ID, or any other unique identifier.

Rate limit by authenticated user

app.use(
  rateLimit({
    strategy: "token-bucket",
    capacity: 10,
    refillRatePerSecond: 2,

    keyGenerator: (req) => req.user.id,
  })
);

Rate limit by API key

app.use(
  rateLimit({
    strategy: "token-bucket",
    capacity: 20,
    refillRatePerSecond: 5,

    keyGenerator: (req) =>
      req.headers["x-api-key"] as string,
  })
);

The value returned from keyGenerator is used as the unique identifier for rate limiting.


Custom Block Handler

By default, Distrilimit responds with HTTP 429 (Too Many Requests) when a client exceeds the configured limit.

You can customize this response using the handler option.

app.use(
  rateLimit({
    strategy: "token-bucket",
    capacity: 5,
    refillRatePerSecond: 1,

    handler: (req, res, result) => {
      return res.status(429).json({
        success: false,
        message: "Rate limit exceeded",
        retryAfterMs: result.retryAfterMs,
      });
    },
  })
);

The custom handler is invoked only when a request is blocked. If no handler is provided, Distrilimit sends its default 429 Too Many Requests response.

Throughput Benchmark

Example table:

| Algorithm | Time (ms) | Latency (ms/request) | Throughput (req/s) | Allowed | | --- | ---: | ---: | ---: | ---: | | Token Bucket | 369 | 0.369 | 2710.0271 | 1000 | | Sliding Window Log | 358 | 0.358 | 2793.2960 | 1000 | | Sliding Window Counter | 404 | 0.404 | 2475.2475 | 1000 |

Rate-Limit Behavior Benchmark

Example table:

| Algorithm | Requests | Allowed | Rejected | Total Time (ms) | | --- | ---: | ---: | ---: | ---: | | Token Bucket | 1000 | 100 | 900 | xxx | | Sliding Window Log | 1000 | 100 | 900 | 326 | | Sliding Window Counter | 1000 | 100 | 900 | 273 |

app.use(
  rateLimit({
    strategy: "token-bucket",
    capacity: 10,
    refillRatePerSecond: 2,
  })
);

Router-Level Rate Limiting

You can apply Distrilimit to an entire Express router. Every route registered after the middleware will share the configured rate limit.

import express from "express";
import { rateLimit } from "distrilimit";
import { protectRoute } from "./middleware/auth.js";

const router = express.Router();

router.use(protectRoute);

router.use(
  rateLimit({
    strategy: "token-bucket",
    capacity: 10,
    refillRatePerSecond: 2,
  })
);

router.get("/profile", getProfile);
router.post("/message", sendMessage);
router.get("/notifications", getNotifications);

export default router;

This applies the rate limiter to every route within the router.

If you need different limits for different routes, apply separate rateLimit() middleware to those specific routes or create multiple routers with different configurations.

Frontend Integration

When a client exceeds the configured rate limit, Distrilimit responds with HTTP 429 and includes retryAfterMs in the response body. You can use this value to notify users when they should retry.

Example using Axios:

try {
  const res = await axiosInstance.get("/messages/unreadmessages");

  set({ unreadCounts: res.data.unreadMap });
} catch (error: any) {
  if (error.response?.status === 429) {
    toast.error(
      `Too many requests. Please try again in ${Math.ceil(
        error.response.data.retryAfterMs / 1000
      )} seconds.`
    );
    return;
  }

  toast.error("Failed to load unread messages.");
}

Tested in a real-world application (Privex) to verify middleware integration and frontend handling of rate-limited responses.

Project Structure

src/
  createRateLimit.ts
  index.ts
  server.ts
  config/
  Factory/
  middleware/
  models/
  scripts/
  store/
  strategies/
  types/

Contributing

  1. Clone the repo.
  2. Install dependencies.
  3. Run npm run dev for the example app.
  4. Make a focused change.
  5. Open a pull request.

License

MIT