Rate Limit
Contract
@modularityjs/rate-limit defines the abstract RateLimiterService:
interface RateLimitResult {
readonly allowed: boolean;
readonly remaining: number;
readonly total: number;
readonly resetAt: number; // Unix timestamp ms
}
abstract class RateLimiterService {
abstract consume(key: string, points?: number): Promise<RateLimitResult>;
abstract get(key: string): Promise<RateLimitResult>;
abstract reset(key: string): Promise<void>;
}consume(key, points)— attempt to consume points (default 1). Returnsallowed: falseif limit exceeded.get(key)— check current state without consuming.reset(key)— clear the counter for a key.
Drivers
Memory (@modularityjs/rate-limit-memory)
In-memory fixed-window rate limiter. For development and single-instance deployments.
import { RateLimitModule } from '@modularityjs/rate-limit';
import { RateLimitMemoryModule } from '@modularityjs/rate-limit-memory';
const modules = [
RateLimitModule,
RateLimitMemoryModule.forRoot({ limit: 100, windowMs: 60_000 }),
];| Option | Default | Description |
|---|---|---|
limit | 100 | Maximum points per window |
windowMs | 60000 | Window duration in milliseconds |
maxEntries | 10000 | Cap on tracked keys; once reached, consume returns allowed: false for new keys until older entries expire or evict |
evictionIntervalMs | — | Optional periodic sweep that removes expired entries; unset means entries are only purged lazily on access |
Redis (@modularityjs/rate-limit-redis)
Redis-backed rate limiter. consume runs a Lua script combining INCRBY + PEXPIRE + PTTL (PEXPIRE only on the first hit of a window; PTTL computes resetAt); get runs a separate Lua script that combines GET + PTTL. For distributed systems where multiple instances share limits.
import { RateLimitModule } from '@modularityjs/rate-limit';
import { RedisModule } from '@modularityjs/redis';
import { RateLimitRedisModule } from '@modularityjs/rate-limit-redis';
const modules = [
RedisModule.forRoot({ host: 'localhost', port: 6379 }),
RateLimitModule,
RateLimitRedisModule.forRoot({ limit: 100, windowMs: 60_000 }),
];| Option | Default | Description |
|---|---|---|
limit | 100 | Maximum points per window |
windowMs | 60000 | Window duration in milliseconds |
keyNamespace | 'rl:' | Prefix for Redis keys |
HTTP Bridge (@modularityjs/http-rate-limit)
Automatically rate-limits all HTTP requests via HttpServer.onRequest() hook. Throws RateLimitExceededException (HTTP 429) when the limit is exceeded.
The extension is fail-open: if the underlying RateLimiterService.consume() throws (e.g. Redis is unreachable), the error is reported via process.emitWarning and the request proceeds without being rate-limited.
import { HttpRateLimitModule } from '@modularityjs/http-rate-limit';
const modules = [
RateLimitModule,
RateLimitMemoryModule.forRoot({ limit: 100, windowMs: 60_000 }),
HttpRateLimitModule,
];By default, the rate limit key is extracted from the x-forwarded-for header (falling back to 'unknown'). Customize with forRoot():
import { getRequestAuth } from '@modularityjs/http-auth';
HttpRateLimitModule.forRoot({
keyExtractor: (request) => {
const identity = getRequestAuth(request);
return identity
? `user:${identity.id}`
: ((request.headers['x-forwarded-for'] as string) ?? 'anon');
},
});Programmatic Usage
Inject RateLimiterService directly for non-HTTP rate limiting:
@Injectable()
class LoginService {
constructor(
@Inject(RateLimiterService) private readonly limiter: RateLimiterService,
) {}
async login(username: string, password: string) {
const result = await this.limiter.consume(`login:${username}`);
if (!result.allowed) {
throw new RateLimitExceededException(result.resetAt);
}
// proceed with authentication
}
}Use cases beyond HTTP:
- Login attempt throttling (per username)
- Queue message processing rate control
- External API call budgeting
- Scheduled job frequency limiting