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

@rishabsikka/rate-limitter

v0.1.0

Published

Pluggable rate limit strategies (fixed window, token bucket, leaky bucket) with memory or Redis storage

Readme

@rishabsikka/rate-limitter

Pluggable rate limiting strategies for Node.js:

  • Fixed window — count requests per time window
  • Token bucket — burst-friendly with steady refill
  • Leaky bucket — smooth outgoing / queue-style pressure

Each strategy can run on in-memory state (single process) or Redis (multiple instances / pods behind a load balancer).

Requirements

  • Node.js ≥ 18
  • Redis + ioredis — only if you use driver: "redis" (peer dependency)

Install

npm install @rishabsikka/rate-limitter

If you use Redis-backed limiters:

npm install @rishabsikka/rate-limitter ioredis

Quick start (memory — single process)

const {
  RateLimiterFactory,
  createRateLimiterChecker,
} = require("@rishabsikka/rate-limitter");

const storage = { driver: "memory" };

const limiter = RateLimiterFactory.createLimiter(
  "TOKEN_BUCKET",
  "api-v1",
  { capacity: 100, refillRatePerSecond: 10 },
  storage,
);

const check = createRateLimiterChecker(limiter);

// In your HTTP handler / middleware:
async function handleRequest(userId) {
  const key = `user:${userId}`;
  const { allowed, retryAfterMs } = await check(key);
  if (!allowed) {
    // return HTTP 429, set Retry-After: Math.ceil(retryAfterMs / 1000), etc.
    return { status: 429, retryAfterMs };
  }
  return { status: 200 };
}

Create one limiter instance per policy (per route / per API family) and reuse it. Do not call createLimiter on every request unless you intentionally want isolated counters.

Redis (multi-instance / production)

Use the same factory with a Redis client you construct (connection string, TLS, pool — your choice):

const Redis = require("ioredis");
const {
  RateLimiterFactory,
  createRateLimiterChecker,
} = require("@rishabsikka/rate-limitter");

const redis = new Redis(process.env.REDIS_URL);
const storage = {
  driver: "redis",
  redis,
  keyPrefix: "myapp:rl", // isolates keys from other apps on the same Redis
};

const limiter = RateLimiterFactory.createLimiter(
  "FIXED_WINDOW",
  "login",
  { windowMs: 60_000, maxRequests: 5 },
  storage,
);
const check = createRateLimiterChecker(limiter);

Redis implementations use atomic Lua where needed so concurrent pods see consistent limits.

API surface

| Export | Role | |--------|------| | RateLimiterFactory | createLimiter(type, name, options, storage) | | createRateLimiterChecker(limiter) | Returns async (key) => ({ allowed, retryAfterMs }) | | RateLimiterStrategy | Abstract base class if you extend strategies | | Memory classes | FixedWindowStrategy, TokenBucketStrategy, LeakyBucketStrategy | | Redis classes | RedisFixedWindowStrategy, RedisTokenBucketStrategy, RedisLeakyBucketStrategy | | Types | StorageConfig, FixedWindowOptions, TokenBucketOptions, LeakyBucketOptions, RateLimiterType |

RateLimiterType

  • "FIXED_WINDOW" — options: { windowMs, maxRequests }
  • "TOKEN_BUCKET" — options: { capacity, refillRatePerSecond }
  • "LEAKY_BUCKET" — options: { capacity, leakRatePerSecond }

StorageConfig

  • { driver: "memory" }
  • { driver: "redis", redis, keyPrefix }redis is an ioredis instance; keyPrefix namespaces keys (e.g. myapp:rl).

Limiter instance methods

  • allow(key): Promise<boolean>
  • getRetryAfterMs(key): Promise<number>
  • snapshot(key): Promise<object> — read-only metrics for dashboards / debugging

Choosing a key

The key string is your identity for limiting (must be stable per client you want to throttle):

  • Authenticated: user:${userId} or org:${orgId}:user:${userId}
  • API key: key:${apiKeyId}
  • Anonymous: ip:${hashedIp} (be careful with NAT / mobile carriers)

Keep keys bounded in length; avoid raw huge query strings.

Framework note

This package is not tied to Express. You call createRateLimiterChecker inside your middleware (Express, Fastify, Hono, Nest, etc.) and map allowed === false to 429 + Retry-After.

Memory vs Redis

| Mode | When to use | |------|----------------| | memory | Tests, local dev, single Node process | | redis | Multiple replicas, Kubernetes, any setup where traffic hits more than one process |

Load balancers alone do not share counters; use Redis (or another shared store) for global limits.