Skip to content

shinijs/rate-limit

@shinijs/rate-limit

A flexible and powerful rate limiting solution for NestJS applications with Redis support

CI Documentation

📚 Full Documentation | API Reference | Examples

Features

  • NestJS Integration - Seamless integration with NestJS ecosystem via decorators, guards, and interceptors
  • Redis Support - Distributed rate limiting using Redis with automatic fallback to memory-based limiting
  • TypeScript - Full type safety and IntelliSense support
  • Flexible Configuration - Per-route rate limits with customizable time windows
  • Multiple Patterns - Use decorators, guards, or interceptors based on your needs
  • Tested - Comprehensive test coverage with 33 passing tests

Installation

pnpm add @shinijs/rate-limit

Peer Dependencies

This package requires the following peer dependencies to be installed in your project:

Package Version Required
@nestjs/common ^11.0.0 Yes
@nestjs/config ^4.0.0 Yes
reflect-metadata ^0.2.0 Yes
rxjs ^7.8.0 Yes
ioredis ^5.0.0 No (Optional)

Install all required peer dependencies:

pnpm add @nestjs/common@^11.0.0 @nestjs/config@^4.0.0 reflect-metadata@^0.2.0 rxjs@^7.8.0

For Redis support (recommended for production):

pnpm add ioredis@^5.0.0

Quick Start

1. Import the Module

import { Module } from '@nestjs/common';
import { ConfigModule } from '@nestjs/config';
import { RateLimitModule } from '@shinijs/rate-limit';

@Module({
  imports: [
    ConfigModule.forRoot({
      isGlobal: true,
    }),
    RateLimitModule.forRoot(), // Use forRoot() for configuration options
  ],
})
export class AppModule {}

2. Configure Redis (Optional but Recommended)

Create a .env file:

REDIS_URL=redis://localhost:6379

If Redis is not configured, the library will automatically fall back to memory-based rate limiting that properly tracks hits in memory.

3. (Optional) Integrate Custom Logger

To use @shinijs/logger with rate limiting for consistent logging:

import { Module, Global } from '@nestjs/common';
import { ConfigModule } from '@nestjs/config';
import { LoggerModule, LoggerFactory } from '@shinijs/logger';
import { RateLimitModule } from '@shinijs/rate-limit';

export const RATE_LIMIT_LOGGER_TOKEN = Symbol('RATE_LIMIT_LOGGER');

@Global()
@Module({
  providers: [
    {
      provide: RATE_LIMIT_LOGGER_TOKEN,
      useFactory: (loggerFactory: LoggerFactory) => {
        return loggerFactory.createLogger('RateLimit');
      },
      inject: [LoggerFactory],
    },
  ],
  exports: [RATE_LIMIT_LOGGER_TOKEN],
})
class RateLimitLoggerModule {}

@Module({
  imports: [
    ConfigModule.forRoot({ isGlobal: true }),
    LoggerModule,
    RateLimitLoggerModule,
    RateLimitModule.forRoot({
      loggerToken: RATE_LIMIT_LOGGER_TOKEN, // Inject custom logger
    }),
  ],
})
export class AppModule {}

4. Use the Decorator

import { Controller, Get } from '@nestjs/common';
import { RateLimit } from '@shinijs/rate-limit';

@Controller('api')
export class ApiController {
  @Get('endpoint')
  @RateLimit({ requests: 10, window: '1m' }) // 10 requests per minute
  async getData() {
    return { message: 'Success' };
  }
}

5. Or Use the Guard

import { Controller, Get, UseGuards } from '@nestjs/common';
import { RateLimit, RateLimitGuard } from '@shinijs/rate-limit';

@Controller('api')
@UseGuards(RateLimitGuard)
export class ApiController {
  @Get('endpoint')
  @RateLimit({ requests: 100, window: '1h' }) // 100 requests per hour
  async getData() {
    return { message: 'Success' };
  }
}

6. Or Use the Service Directly

import { Injectable } from '@nestjs/common';
import { RateLimitService } from '@shinijs/rate-limit';

@Injectable()
export class YourService {
  constructor(private readonly rateLimitService: RateLimitService) {}

  async processRequest(userId: string) {
    const result = await this.rateLimitService.checkRateLimit(
      `user:${userId}`,
      { requests: 50, window: '5m' }
    );

    if (!result.allowed) {
      throw new Error('Rate limit exceeded');
    }

    // Process the request
    return { remaining: result.remaining };
  }
}

Configuration

Time Window Formats

The window parameter accepts the following formats:

  • s - seconds (e.g., 30s = 30 seconds)
  • m - minutes (e.g., 5m = 5 minutes)
  • h - hours (e.g., 1h = 1 hour)
  • d - days (e.g., 1d = 1 day)

Environment Variables

Variable Description Default
REDIS_URL Redis connection URL undefined (falls back to memory-based limiting)

Example:

REDIS_URL=redis://localhost:6379
# Or with authentication
REDIS_URL=redis://:password@localhost:6379
# Or for Redis cluster
REDIS_URL=redis://localhost:6379/0

API Reference

RateLimitModule

RateLimitModule.forRoot(options?: RateLimitModuleOptions)

Configure the rate limit module with optional logger injection.

Options:

interface RateLimitModuleOptions {
  loggerToken?: string | symbol; // Token to inject logger from DI container
  logger?: LoggerService;        // Logger instance to use directly
}

Example:

RateLimitModule.forRoot({
  loggerToken: YOUR_LOGGER_TOKEN, // Inject custom logger via DI token
})

RateLimitService

checkRateLimit(key: string, options: RateLimitOptions): Promise<RateLimitResult>

Checks if a request is allowed based on the rate limit configuration.

Parameters:

  • key - Unique identifier for the rate limit (e.g., user ID, IP address)
  • options - Rate limit configuration
    • requests - Maximum number of requests allowed
    • window - Time window (e.g., '1m', '1h')

Returns:

{
  allowed: boolean;      // Whether the request is allowed
  remaining: number;     // Remaining requests in current window
  resetTime: number;     // Timestamp when the limit resets
  totalHits: number;     // Total requests in current window
}

decrementRateLimit(key: string): Promise<void>

Decrements the rate limit counter (useful for rolling back failed requests).

healthCheck(): Promise<boolean>

Checks if Redis connection is healthy. Returns false if Redis is not configured or unhealthy.

Decorators

@RateLimit(options: RateLimitOptions)

Applies rate limiting to a route handler.

@RateLimit({ requests: 10, window: '1m' })

Guards

RateLimitGuard

A NestJS guard that enforces rate limits based on the @RateLimit decorator.

Interceptors

RateLimitInterceptor

A NestJS interceptor that enforces rate limits and adds rate limit headers to responses.

Redis vs Memory-Based Mode

With Redis (Recommended for Production)

  • ✅ Distributed rate limiting across multiple instances
  • ✅ Persistent rate limit data
  • ✅ Accurate counting
  • ✅ Suitable for horizontal scaling

Memory-Based Fallback

  • ⚠️ Only works for single-instance applications
  • ⚠️ Rate limits reset on application restart
  • ⚠️ Not suitable for distributed systems
  • ✅ Properly tracks hits in memory (fixed in latest version)
  • ℹ️ Automatically used when Redis is not available

Testing

# Unit tests
pnpm test

# Watch mode
pnpm test:watch

# Coverage
pnpm test:cov

Documentation

📚 Full Documentation is available with:

  • Complete API reference
  • Configuration guide
  • Usage examples
  • Best practices

Contributing

Contributions are welcome! Please read our Contributing Guide and Code of Conduct before submitting pull requests.

License

MIT © Shironex

About

A flexible and powerful rate limiting solution for NestJS applications with Redis support

Resources

License

Code of conduct

Contributing

Stars

Watchers

Forks

Packages

No packages published

Contributors 3

  •  
  •  
  •