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

nestjs-advanced-rate-limiter

v0.3.0

Published

Advanced rate limiter for NestJS. Token bucket (GCRA), sliding window, fixed window. In-memory or Redis. Drop-in replacement for @nestjs/throttler.

Readme

nestjs-advanced-rate-limiter

Advanced rate limiter for NestJS. Drop-in replacement for @nestjs/throttler with real algorithms: token bucket (GCRA), sliding window, and fixed window. In-memory or Redis.

Built on elysia-advanced-rate-limiter core -- the same algorithms Stripe and Cloudflare run in production.

Why not @nestjs/throttler?

| | @nestjs/throttler | nestjs-advanced-rate-limiter | |---|---|---| | Algorithms | TTL-based counter | Token bucket (GCRA), sliding window, fixed window | | Burst control | None | GCRA allows controlled bursts while enforcing steady rate | | Boundary accuracy | Resets at TTL expiry | Sliding window eliminates 2x burst at boundaries | | Storage efficiency | Stores timestamps | 21-64 bytes per key depending on algorithm | | Redis | Via separate adapter | Built-in RedisStore with circuit breaker (ResilientStore) | | Per-route override | Multiple throttler configs | @Throttle() decorator with any algorithm | | User tracking | Manual | Built-in JWT/session userTracker | | IP resolution | Basic | Cloudflare, nginx, proxy-depth-aware |

Install

npm install nestjs-advanced-rate-limiter

Quick Start

import { Module } from "@nestjs/common";
import { APP_GUARD } from "@nestjs/core";
import { ThrottlerModule, ThrottlerGuard } from "nestjs-advanced-rate-limiter";

@Module({
  imports: [
    ThrottlerModule.forRoot({
      algorithm: { algorithm: "token-bucket", capacity: 100, refillRate: 10 },
    }),
  ],
  providers: [
    {
      provide: APP_GUARD,
      useExisting: ThrottlerGuard,
    },
  ],
})
export class AppModule {}

Works out of the box. In-memory, token bucket, 100 capacity, 10 tokens/sec. Every route is protected.

Algorithms

Three algorithms. All O(1) time and space per request.

Token Bucket (GCRA) -- Default

Allows bursts up to capacity while enforcing a steady refillRate per second. Uses GCRA (Generic Cell Rate Algorithm) internally -- no refill loops, no locks, one number comparison.

ThrottlerModule.forRoot({
  algorithm: { algorithm: "token-bucket", capacity: 100, refillRate: 10 },
})
capacity = 5,  refillRate = 1/sec

t=0ms   Req #1  ALLOWED  (5 tokens -> 4)
t=0ms   Req #2  ALLOWED  (4 tokens -> 3)
t=0ms   Req #3  ALLOWED  (3 tokens -> 2)
t=0ms   Req #4  ALLOWED  (2 tokens -> 1)
t=0ms   Req #5  ALLOWED  (1 tokens -> 0)
t=0ms   Req #6  DENIED   retryAfter=1s
t=1000  Req #7  ALLOWED  (1 token refilled)

Storage: 21 bytes per key

Fixed Window

Simplest option. Divides time into equal blocks, counts requests per block. Counter resets at each boundary.

ThrottlerModule.forRoot({
  algorithm: { algorithm: "fixed-window", limit: 100, windowMs: 60_000 },
})

Storage: 39 bytes per key

Note: Clients can send limit requests at the end of one window and limit at the start of the next, getting 2x the limit in a short span. If this matters, use sliding window.

Sliding Window

Blends current and previous window counts to eliminate the boundary burst. Uses the two-counter approximation.

ThrottlerModule.forRoot({
  algorithm: { algorithm: "sliding-window", limit: 100, windowMs: 60_000 },
})

Storage: 64 bytes per key

Algorithm Comparison

               Fixed Window     Sliding Window    Token Bucket (GCRA)
             +----------------+------------------+--------------------+
 Time        |     O(1)       |      O(1)        |       O(1)         |
 Space/key   |   39 bytes     |    64 bytes      |     21 bytes       |
 Redis cmds  |   1 INCR       |  INCR + GET      |    1 EVAL (Lua)    |
 Burst       | 2x at edges    |  Smoothed        |  Controlled        |
 Precision   |   Exact        |  Approximate     |     Exact          |
 Best for    |  Simplicity    |  Smooth limiting |  APIs / billing    |
             +----------------+------------------+--------------------+

Storage

In-Memory (default)

No dependencies. Good for single-process deployments.

import { MemoryStore } from "nestjs-advanced-rate-limiter";

ThrottlerModule.forRoot({
  store: new MemoryStore({ maxKeys: 100_000, cleanupIntervalMs: 60_000 }),
})

Redis

For multi-instance deployments. Works with ioredis or any compatible client.

import Redis from "ioredis";
import { RedisStore } from "nestjs-advanced-rate-limiter";

ThrottlerModule.forRoot({
  store: new RedisStore(new Redis()),
})

Resilient Store

Wraps any store with circuit breaker. If Redis goes down, your app keeps running.

import { ResilientStore, RedisStore } from "nestjs-advanced-rate-limiter";

ThrottlerModule.forRoot({
  store: new ResilientStore(new RedisStore(redis), {
    failMode: "open",     // allow traffic when store is down (default)
    threshold: 5,         // open circuit after 5 consecutive failures
    cooldownMs: 30_000,   // retry after 30s
    onError: (err) => console.error(err),
  }),
})

Decorators

@Throttle() -- Per-Route Override

Override the default algorithm for specific routes or controllers.

import { Controller, Post, Get } from "@nestjs/common";
import { Throttle } from "nestjs-advanced-rate-limiter";

@Controller("auth")
export class AuthController {
  @Throttle({ algorithm: "fixed-window", limit: 5, windowMs: 60_000 })
  @Post("login")
  login() {
    // strict: 5 attempts per minute
  }

  @Get("profile")
  profile() {
    // uses the global default
  }
}

@SkipThrottle() -- Skip Rate Limiting

Disable rate limiting for specific routes or entire controllers.

import { Controller, Get } from "@nestjs/common";
import { SkipThrottle } from "nestjs-advanced-rate-limiter";

@SkipThrottle()
@Controller("health")
export class HealthController {
  @Get()
  check() {
    return "ok";
  }
}

User Tracking

Built-in JWT/session-based tracker. Identifies users from Authorization: Bearer <token> or session cookies. Falls back to IP.

import { userTracker } from "nestjs-advanced-rate-limiter";

ThrottlerModule.forRoot({
  getTracker: userTracker(),
})

// Custom cookie name
ThrottlerModule.forRoot({
  getTracker: userTracker({ cookieName: "session" }),
})

// Custom JWT parser
ThrottlerModule.forRoot({
  getTracker: userTracker({
    parseJwt: (token) => {
      const payload = JSON.parse(atob(token.split(".")[1]));
      return payload.sub;
    },
  }),
})

// Disable IP fallback
ThrottlerModule.forRoot({
  getTracker: userTracker({ fallbackToIp: false }),
})

IP Resolution

The default tracker (and userTracker fallback) checks headers in this order:

| Priority | Header | Set by | |---|---|---| | 1 | cf-connecting-ip | Cloudflare | | 2 | x-real-ip | nginx / load balancer | | 3 | x-forwarded-for (first IP) | Any proxy | | 4 | req.ip / req.socket.remoteAddress | Express / Fastify |

Works with both Express and Fastify.

Async Configuration

Use forRootAsync when you need to inject dependencies (e.g., ConfigService).

import { ThrottlerModule } from "nestjs-advanced-rate-limiter";
import { ConfigModule, ConfigService } from "@nestjs/config";

@Module({
  imports: [
    ThrottlerModule.forRootAsync({
      imports: [ConfigModule],
      inject: [ConfigService],
      useFactory: (config: ConfigService) => ({
        algorithm: {
          algorithm: "token-bucket",
          capacity: config.get("RATE_LIMIT_CAPACITY", 100),
          refillRate: config.get("RATE_LIMIT_REFILL", 10),
        },
      }),
    }),
  ],
})
export class AppModule {}

All Options

ThrottlerModule.forRoot({
  // Algorithm (default: token-bucket, 100 capacity, 10/sec)
  algorithm: { algorithm: "token-bucket", capacity: 100, refillRate: 10 },

  // Storage backend (default: in-memory)
  store: new MemoryStore(),

  // Key prefix for storage (default: "rl:")
  prefix: "rl:",

  // Custom tracker function (default: IP-based)
  getTracker: (req) => req.ip,

  // Programmatic skip (default: none)
  skipIf: (req) => req.url.includes("/health"),

  // Ignore specific user agents (default: [])
  ignoreUserAgents: [/googlebot/i, /bingbot/i],

  // Custom error message or response factory (default: "Too Many Requests")
  errorMessage: "Rate limit exceeded",
  // or
  errorMessage: (result) => ({
    error: "rate_limited",
    retryAfter: Math.ceil(result.retryAfterMs / 1000),
  }),
})

Response Headers

Every response includes rate limit headers:

| Header | When | Example | |---|---|---| | X-RateLimit-Limit | Always | 100 | | X-RateLimit-Remaining | Always | 95 | | X-RateLimit-Reset | Always | 1712678460 (Unix timestamp) | | Retry-After | 429 only | 5 (seconds) |

Migrating from @nestjs/throttler

- import { ThrottlerModule, ThrottlerGuard } from "@nestjs/throttler";
+ import { ThrottlerModule, ThrottlerGuard } from "nestjs-advanced-rate-limiter";

 @Module({
   imports: [
-    ThrottlerModule.forRoot({ ttl: 60000, limit: 10 }),
+    ThrottlerModule.forRoot({
+      algorithm: { algorithm: "fixed-window", limit: 10, windowMs: 60_000 },
+    }),
   ],
   providers: [{ provide: APP_GUARD, useExisting: ThrottlerGuard }],
 })
 export class AppModule {}

The @SkipThrottle() decorator works the same way. Replace @Throttle() with the new config-based syntax:

- @Throttle({ default: { limit: 5, ttl: 60000 } })
+ @Throttle({ algorithm: "fixed-window", limit: 5, windowMs: 60_000 })

License

MIT