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

@fluojs/throttler

v2.0.2

Published

Decorator-based rate limiting for Fluo applications with in-memory and Redis store adapters.

Readme

@fluojs/throttler

Decorator-based rate limiting for fluo applications with in-memory and Redis store adapters.

Table of Contents

Installation

@fluojs/throttler declares engines.node >=24.0.0 <27 in its published package manifest as its package-owned support contract. Upgrade Node 20 and Node 22 hosts to Node.js >=24.0.0 <27; Node versions below 24 and Node 27+ are unsupported.

npm install @fluojs/throttler

When to Use

  • To prevent brute-force attacks on sensitive endpoints (e.g., login, registration).
  • To protect your API from being overwhelmed by too many requests from a single client.
  • To implement usage quotas or tiered rate limits for different types of users.
  • When you need a simple way to apply rate limits using decorators on controllers or methods.

Quick Start

This controller/module fragment belongs in an existing HTTP app. Retain the decorator/entrypoint configuration from Application Bootstrap Protocol. The module below registers the declared AuthController in controllers.

Register the ThrottlerModule, wire ThrottlerGuard with @UseGuards(...), and apply the Throttle decorator to controllers or methods that need route-specific limits.

import { Module } from '@fluojs/core';
import { ThrottlerGuard, ThrottlerModule, Throttle, SkipThrottle } from '@fluojs/throttler';
import { Controller, Post, UseGuards } from '@fluojs/http';

@Controller('/auth')
@UseGuards(ThrottlerGuard)
class AuthController {
  @Post('/login')
  @Throttle({ ttl: 60, limit: 5 }) // Override: 5 requests per minute
  login() {
    return { success: true };
  }

  @Post('/public-info')
  @SkipThrottle() // Bypass throttling
  getInfo() {
    return { info: '...' };
  }
}

@Module({
  imports: [
    ThrottlerModule.forRoot({
      ttl: 60,   // 60 seconds
      limit: 10, // 10 requests
    }),
  ],
  controllers: [AuthController],
})
class AppModule {}

Common Patterns

Redis Storage

For multi-instance deployments, use RedisThrottlerStore to share the rate limit state across all instances. Redis-backed windows are anchored to Redis server time, so distributed app nodes with clock skew still enforce one shared reset boundary.

RedisThrottlerStore accepts the package-local structural RedisThrottlerClient contract: a Redis command client with an eval(script, numberOfKeys, ...args) method. @fluojs/redis, ioredis, and compatible custom clients can be passed without making the root @fluojs/throttler import depend on a concrete ioredis constructor type.

import { ThrottlerModule, RedisThrottlerStore } from '@fluojs/throttler';
import { REDIS_CLIENT } from '@fluojs/redis';

// Inside a provider or module factory
const redisClient = await container.resolve(REDIS_CLIENT);
const redisStore = new RedisThrottlerStore(redisClient);

ThrottlerModule.forRoot({
  ttl: 60,
  limit: 100,
  store: redisStore,
});

You can also pass any object that implements the ThrottlerStore contract through the store option. ThrottlerModule.forRoot(...) validates that a custom store exposes a consume(...) function before request handling starts.

Custom Key Generation

By default, the throttler resolves client identity from the raw socket remoteAddress only. For a deployment behind a trusted reverse proxy that rewrites Forwarded, X-Forwarded-For, or X-Real-IP, configure trustProxy as the primary recommendation and restrict it to the proxy addresses your application actually observes. trustProxyHeaders: true is broad legacy compatibility only and is not recommended for new deployments. If no trusted socket or proxy identity is available, it throws instead of collapsing unrelated callers into a shared bucket. You can also customize this to use API keys, user IDs, or other identifiers.

Counters are scoped by route identity and client identity. The route portion includes module, controller, method, path, version, and the deterministic source/method position assigned by the HTTP route compiler. Distinct compiled handlers therefore remain isolated even when their display names and emitted source match, while identical artifact layouts produce the same identity across application instances for distributed bucket sharing. When a request is rejected, ThrottlerGuard returns 429 and sets Retry-After.

ThrottlerModule.forRoot({
  ttl: 60,
  limit: 100,
  // Accept forwarding metadata only from this known reverse proxy.
  trustProxy: ['192.0.2.10/32'],
});

trustProxy is the explicit forwarding boundary for the default client key. It accepts false, a trusted-hop count, address/CIDR rules, or a predicate. Untrusted or malformed forwarding data cannot replace the direct transport identity. trustProxyHeaders: true preserves broad legacy full-header compatibility only; use trustProxy for new deployments.

ThrottlerModule.forRoot({
  ttl: 60,
  limit: 100,
  keyGenerator: (context) => {
    const apiKeyHeader = context.request.headers['x-api-key'];
    const apiKey = Array.isArray(apiKeyHeader) ? apiKeyHeader[0] : apiKeyHeader;

    if (!apiKey) {
      throw new Error('Missing API key for throttler tracking.');
    }

    return `api-key:${apiKey}`;
  },
});

NestJS Migration Boundaries

When migrating from @nestjs/throttler, treat @fluojs/throttler as an explicit guard-stage package rather than a drop-in global limiter:

  • ThrottlerModule.forRoot(...) registers validated options and providers, but it does not automatically enforce throttling on every route. Activate ThrottlerGuard with Fluo guard metadata such as @UseGuards(ThrottlerGuard) wherever enforcement is required.
  • The public policy shape is one module default plus class- or method-level @Throttle({ ttl, limit }) overrides. Named multi-window definitions such as burst plus sustained limits require explicit composition through HTTP middleware, a custom ThrottlerStore, or an application-owned guard wrapper.
  • NestJS ttl values are milliseconds; fluo ttl values are seconds. Convert ttl: 60_000 to ttl: 60 rather than copying the value directly.
  • @SkipThrottle() has no named or false form. Class- and method-level skips combine additively, so move a reactivated method outside a skipped controller or use an application-owned guard wrapper.
  • Resolve async secrets, configuration, and store construction before synchronous ThrottlerModule.forRoot(...) registration; fluo does not provide NestJS forRootAsync(...) shapes.
  • ThrottlerGuard and keyGenerator are HTTP-only. Apply separate transport-owned guards or middleware to WebSocket, GraphQL, RPC, and queue policies.
  • Persisted NestJS windows do not continue by default because bucket keys and storage call contracts differ. Use an application-owned compatibility store or a bounded cutover when continuity is required.
  • Forwarded client IP headers are ignored by default. Prefer trustProxy to declare a hop count, CIDR list, or predicate for a trusted proxy boundary. trustProxyHeaders: true is broad legacy compatibility mode that trusts the complete forwarding chain, not only the direct peer, and is not recommended for new deployments.
  • The guaranteed limit-exceeded response contract is HTTP 429 with Retry-After. Additional rate-limit headers or response bodies should be added at the application boundary, for example with an exception filter.

For migration examples and the complete compatibility map, see the NestJS → fluo Migration Map.

Public API Overview

Modules

  • ThrottlerModule.forRoot(options): Provides validated throttler options and ThrottlerGuard to the module graph.
  • ThrottlerModuleOptions: Public options shape accepted by ThrottlerModule.forRoot(...).
  • Package-level registration is supported through ThrottlerModule.forRoot(options). Internal provider-composition helpers and DI tokens are not part of the public contract.

ttl and limit must be positive finite integers. global defaults to true; set global: false when the throttler providers should stay scoped to the importing module. trustProxy is the recommended explicit boundary and uses TrustProxyPolicy from @fluojs/http; trustProxyHeaders is broad legacy compatibility only and is not recommended for new deployments. keyGenerator, when provided, must be a function. Module options are validated and captured by value when the guard is wired so later mutation of the caller's options object does not change live throttling policy. If no store option is supplied, each ThrottlerGuard instance owns its own in-memory store; pass a ThrottlerStore implementation such as RedisThrottlerStore when storage must be shared or externally managed.

Decorators

  • @Throttle({ ttl, limit }): Sets a specific rate limit for a class or method.
  • @SkipThrottle(): Disables throttling for a class or method.
  • ThrottlerHandlerOptions: Public { ttl, limit } policy shape accepted by @Throttle(...). Both values must be positive finite integers; method-level policies override class-level policies, which override module defaults.
  • Existing root-barrel metadata helpers (throttleRouteMetadataKey, getThrottleMetadata, getSkipThrottleMetadata, getClassThrottleMetadata, and getClassSkipThrottleMetadata) remain exported for compatibility with advanced integrations that already inspect decorator metadata directly.

Guards

  • ThrottlerGuard: The guard responsible for enforcing rate limits. ThrottlerModule.forRoot() makes it injectable; route handlers still activate it through Fluo guard metadata such as @UseGuards(ThrottlerGuard).

Stores

  • createMemoryThrottlerStore(): Creates a simple in-memory store (default).
  • RedisThrottlerStore: Store adapter for Redis.
  • RedisThrottlerClient: Structural Redis command client contract accepted by RedisThrottlerStore.
  • ThrottlerStore: Public contract for custom stores.
  • ThrottlerConsumeInput: Public input shape passed to ThrottlerStore.consume(key, input) so custom stores can share the guard's current time and TTL window.
  • ThrottlerStoreEntry: Public result shape returned by ThrottlerStore.consume(...); count is the post-consume request count for the active window and resetAt is the epoch-millisecond reset boundary used for Retry-After calculation. Custom stores may return optional retryAfterMs when a backing store has a more authoritative clock than the application process; the guard uses it for Retry-After when the limit is exceeded.

Status and diagnostics

  • createThrottlerPlatformStatusSnapshot(...): Creates a platform status snapshot.
  • createThrottlerPlatformDiagnosticIssues(...): Creates diagnostic issues for invalid throttler state.
  • ThrottlerStatusAdapterInput: Public input shape for status and diagnostic helpers. It carries store kind, ownership mode, operation mode, backing-store readiness, dependency linkage, and readiness criticality hints collected during bootstrap.
  • ThrottlerPlatformStatusSnapshot: Public output shape returned by createThrottlerPlatformStatusSnapshot(...), containing readiness, health, ownership, and details sections compatible with runtime platform snapshots.
  • ThrottlerStoreKind: Store categories recognized by the status adapter: memory, redis, or custom.
  • ThrottlerStoreOwnershipMode: Ownership modes reported in status snapshots: framework for guard-owned resources or external for externally managed stores.
  • ThrottlerOperationMode: Operation modes reported in status details: local-only, distributed, local-fallback, or custom.

Method-level @Throttle(...) overrides class-level settings, class-level settings override module defaults, and @SkipThrottle() bypasses throttling at either class or method level.

Related Packages

  • @fluojs/http: Required for HTTP context and Exception handling.
  • @fluojs/redis: Official Redis client integration for RedisThrottlerStore; ioredis and compatible structural clients are also supported.

Example Sources

  • packages/throttler/src/module.test.ts: Tests for module configuration, decorator overrides, and HTTP guard integration through Test.createApp(...).
  • packages/throttler/src/guard.ts: The core logic for request throttling and header management.
  • packages/throttler/src/redis-store.test.ts: Redis store contract and server-time behavior.
  • packages/throttler/src/status.test.ts: Status and diagnostic helper behavior.

Compatible service copies

ThrottlerGuard supports compatible-copy class injection; throttler options remain isolated.