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

@anysk/nestjs-redis-throttler

v1.0.0

Published

Redis-backed rate-limiting guard for NestJS with per-route overrides. No @nestjs/throttler dependency — fresh Nest peers, atomic Lua fixed windows, bring your own Redis client.

Readme

@anysk/nestjs-redis-throttler

Redis-backed rate limiting for NestJS — a standalone guard with per-route overrides and no @nestjs/throttler dependency.

Why it exists: rate-limiting packages that wrap or extend @nestjs/throttler inherit its peer range, so every Nest major leaves you waiting. This package depends only on @nestjs/common / @nestjs/core (peers: ^11 || ^12) and a tiny storage contract you can implement for any client. Counters live in Redis via one atomic Lua EVAL, so limits are shared across instances and survive restarts — which the default in-memory throttler storage never was.

  • Fixed-window counting, atomic (INCR + PEXPIRE in one script; no TTL-less stray counters)
  • @Throttle() / @SkipThrottle() per-route overrides, same object shape as @nestjs/throttler
  • Multiple named windows (e.g. a minute burst limit plus an hourly cap)
  • 429 responses carry Retry-After
  • Only HTTP contexts are throttled by default — websocket/microservice/bot-framework contexts pass through
  • Bring-your-own Redis client (structurally matches node-redis v4+; one-line ioredis adapter)
  • Fail-open by default when Redis is briefly unavailable (configurable)
  • Ships dual CJS + ESM, works under compilers that don't emit decorator metadata (esbuild/swc)

Install

npm i @anysk/nestjs-redis-throttler
# peers you already have in a Nest app: @nestjs/common @nestjs/core reflect-metadata

Quickstart

// app.module.ts
import { APP_GUARD } from '@nestjs/core';
import { RedisThrottlerModule, RedisThrottlerGuard, RedisStore } from '@anysk/nestjs-redis-throttler';

@Module({
  imports: [
    RedisThrottlerModule.forRootAsync({
      imports: [RedisModule],
      inject: [RedisService],
      useFactory: (redis: RedisService) => ({
        windows: [
          { name: 'default', ttl: 60_000, limit: 60 },
          { name: 'hourly', ttl: 3_600_000, limit: 10_000 },
        ],
        store: new RedisStore(redis.client), // any node-redis v4+ client
      }),
    }),
  ],
  providers: [{ provide: APP_GUARD, useClass: RedisThrottlerGuard }],
})
export class AppModule {}

Per-route overrides, by window name:

import { Throttle, SkipThrottle } from '@anysk/nestjs-redis-throttler';

@Controller('account')
export class AccountController {
  @Throttle({ default: { ttl: 60_000, limit: 3 } }) // tighten the minute window here
  @Post('sensitive')
  sensitive() {}

  @SkipThrottle()
  @Get('health')
  health() {}
}

An override name that matches no configured window defines an extra window for that route only (both ttl and limit required):

@Throttle({ burst: { ttl: 1_000, limit: 2 } })

Options

| Option | Default | Meaning | | --- | --- | --- | | windows | — | Named windows enforced on every route. ttl in ms. | | store | — | RedisStore in production; InMemoryStore for tests/dev. | | keyPrefix | "nrt" | Redis key prefix. Keys look like nrt:<window>:<Controller>:<handler>:<tracker>. | | contextTypes | ["http"] | Execution context types to throttle; everything else passes through. | | trustProxy | false | Key on the first X-Forwarded-For hop instead of req.ip. Prefer configuring your HTTP adapter's trust proxy; this is the escape hatch when you can't. | | getTracker | req.ip | Custom client identity (e.g. user id). Return "" to skip the request. | | skipIf | — | Predicate over the ExecutionContext to bypass throttling. | | errorMessage | "Too Many Requests" | 429 body message, or a factory receiving the exceeded-limit detail. | | onStoreError | "allow" | "allow" logs and lets requests through when the store throws; "block" rethrows. |

Requests with no resolvable identity are allowed, not pooled into one shared bucket — a misconfigured proxy shouldn't rate-limit all users collectively.

ioredis

new RedisStore({
  eval: (script, { keys, arguments: args }) => ioredis.eval(script, keys.length, ...keys, ...args),
});

Anything implementing ThrottlerStore (increment(key, ttlMs) → { totalHits, timeToExpireMs }) works as a store; the contract requires atomic counting where the first hit starts the TTL.

Migrating from @nestjs/throttler

- ThrottlerModule.forRoot([
-   { name: 'default', ttl: 60_000, limit: 60 },
-   { name: 'hourly', ttl: 3_600_000, limit: 10_000 },
- ]),
+ RedisThrottlerModule.forRootAsync({
+   imports: [RedisModule],
+   inject: [RedisService],
+   useFactory: (redis: RedisService) => ({
+     windows: [
+       { name: 'default', ttl: 60_000, limit: 60 },
+       { name: 'hourly', ttl: 3_600_000, limit: 10_000 },
+     ],
+     store: new RedisStore(redis.client),
+   }),
+ }),
  • @Throttle({ default: { ttl, limit } }) and @SkipThrottle() keep their shape — update the import.
  • A custom "http-only" guard subclass becomes configuration: non-HTTP contexts are skipped out of the box (contextTypes).
  • Behavior parity: buckets are per window × route × client, 429 with Retry-After. Difference: counters are in Redis, so limits hold across instances and restarts.

License

MIT