nestrs-throttle workspace member. It is
the in-tree analogue of @nestjs/throttler: a ThrottlerGuard for
declarative per-route limits, a throttler_middleware that applies the
same skip → decorated → global precedence (via RouteRegistry lookup,
because app-level layers cannot read HandlerKey), a ThrottlerService
for manual checks from inside a handler,
and pluggable storage (InMemoryThrottler for single-instance,
RedisThrottler for distributed).
ThrottleSpec is { limit, window_secs }. Parse decorator strings with
ThrottleSpec::parse("5/minute") (second / minute / hour only).
There is no ThrottlerOptions::builder(), no window_ms field,
and no #[Throttle] PascalCase attribute.
Define a spec, register the module
NestApplication::use_throttler (feature
throttler), which installs the 429 middleware and registers
ThrottlerService for the guard:
ThrottlerOptions.global is the fallback for routes without
#[throttle(...)]. None (the default) means only explicitly
decorated routes are throttled.
Per-route limits
#[throttle(n, "second" | "minute" | "hour")] records metadata
"throttle" => "n/per" and overrides the module global for that
handler. #[skip_throttle] records "skip_throttle" => "true" and
exempts the route entirely (wins over both the decorator and the
global). One of each per handler — they do not stack into multiple
windows.
Rejected requests answer 429 with Retry-After and
X-RateLimit-Remaining.
Manual checks
When a rate-limit decision depends on something the module can’t see (resource ownership, tenant tier), reach for the service directly.ThrottlerService::check takes (handler, key, spec) and scopes the
backend key as {handler}:{key}:
Custom key generators
The default key is the client IP (resolved via the same trusted-proxy helpers asClientIp). To rate-limit by API key instead, set
ThrottlerOptions.key_generator:
ApiKeyHeaderKeyGenerator::default() reads x-api-key;
ApiKeyHeaderKeyGenerator::new(header) overrides the name. Missing
headers fall back to the IP so unkeyed traffic is still throttled.
PrincipalKeyGenerator reads the PrincipalId request extension
installed by authn / oauth2.
ThrottleKeyGenerator is a trait over ThrottlerRequest<'_> —
handler, ip, and borrowed parts (headers, URI, extensions). There
is no req.path field; use req.parts.uri.path().
Skip on demand
Internal services, health-check endpoints, and authenticated admins typically shouldn’t count against the limit. ImplementThrottleSkipper:
ThrottlerOptions { skipper: Some(Arc::new(SkipAdminRoutes)), .. }.
NeverSkip is the default. Skipped requests neither consume counters
nor generate blocks.
Distributed limits
Enablethrottler-redis on the umbrella (maps to
nestrs-throttle/cache-redis):
RedisThrottler uses an atomic INCR + EXPIRE pattern per spec key, so
concurrent processes see the same counter. Connection failure at init
falls back to in-memory (throttling is an optimization, not an
availability gate). Sliding-window and token-bucket are not built in.
Debug redacts user:pass@ from the Redis URL.
When to use what
#[throttle(n, "per")]on a route — the default. Limit and window are explicit and visible.#[skip_throttle]— probes, admin, and internal paths. Raise limits on “important” routes; don’t skip them.ThrottlerService::checkfrom inside a handler — when the limit depends on handler-side state (per-tenant, per-resource).ThrottleSkipper— bypass for a class of requests the decorator can’t see (internal IPs, a header).- Custom
ThrottleKeyGenerator— when the IP-derived key is wrong for your traffic shape (NAT’d mobile clients, single egress proxy, per-tenant SaaS).
See also
- Guides: authorization — for combining auth principal with throttling key generation.
- Guides: security — global
use_rate_limitvs per-routeuse_throttler. - Concepts: middleware-pipeline —
the global
throttler_middlewareruns before handlers. @nestjs/throttler— upstream documentation for comparison.