Skip to content

Getting Started

This guide will help you get started with @shinijs/rate-limit.

Quick Start

The fastest way to get started with @shinijs/rate-limit:

1. Install the package

bash
pnpm add @shinijs/rate-limit
# Optional: for distributed rate limiting
pnpm add ioredis

2. Add to your AppModule

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

@Module({
  imports: [
    ConfigModule.forRoot({
      isGlobal: true,
    }),
    RateLimitModule.forRoot(), // That's it! Rate limiting is now available
  ],
})
export class AppModule {}

3. Use the decorator on your routes

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

@Controller('api')
@UseGuards(RateLimitGuard)
export class ApiController {
  @Get('users')
  @RateLimit({ requests: 10, window: '1m' }) // 10 requests per minute
  getUsers() {
    return { users: [] };
  }
}

That's it! Rate limiting is now active. The library will automatically use Redis if configured, or fall back to memory-based limiting.

Optional: Configure Redis (for production)

Add to your .env file:

env
REDIS_URL=redis://localhost:6379

Optional: Integrate Custom Logger

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

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

// Token for RateLimit logger provider
export const RATE_LIMIT_LOGGER_TOKEN = Symbol('RATE_LIMIT_LOGGER');

// Create a global module to provide the logger token
@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, // Import logger module first
    RateLimitLoggerModule, // Provide logger for rate limit
    RateLimitModule.forRoot({
      loggerToken: RATE_LIMIT_LOGGER_TOKEN, // Inject custom logger
    }),
  ],
})
export class AppModule {}

Now rate limit logs will use your custom logger with the "RateLimit" context:

INFO [20:17:12]: RateLimit Connected to Redis for rate limiting
DEBUG [20:17:17]: RateLimitGuard Rate limit check passed
WARN [20:17:23]: RateLimitGuard Rate limit exceeded

Installation

Install the package using your preferred package manager:

bash
# Using pnpm (recommended)
pnpm add @shinijs/rate-limit

# Using npm
npm install @shinijs/rate-limit

# Using yarn
yarn add @shinijs/rate-limit

Peer Dependencies

Make sure you have the required peer dependencies installed:

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

# Optional (but recommended for production)
pnpm add ioredis@^5.0.0

Basic Setup

1. Import the Module

Import RateLimitModule in your root module:

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

@Module({
  imports: [
    ConfigModule.forRoot(), // Required for environment variables
    RateLimitModule,
  ],
})
export class AppModule {}

2. Configure Redis (Optional)

Create a .env file in your project root:

env
REDIS_URL=redis://localhost:6379

TIP

If you don't configure Redis, the library will automatically fall back to memory-based rate limiting. This is fine for development but not recommended for production in distributed environments.

Usage Patterns

Pattern 1: Using the Decorator with Guard

The most common pattern - use the @RateLimit decorator with RateLimitGuard:

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

@Controller('api')
@UseGuards(RateLimitGuard)
export class ApiController {
  @Get('users')
  @RateLimit({ requests: 100, window: '1m' })
  getUsers() {
    return { users: [] };
  }
}

Pattern 2: Using the Interceptor

Use the RateLimitInterceptor to automatically add rate limit headers to responses:

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

@Controller('api')
@UseInterceptors(RateLimitInterceptor)
export class ApiController {
  @Get('data')
  @RateLimit({ requests: 50, window: '5m' })
  getData() {
    return { data: 'some data' };
  }
}

Pattern 3: Using the Service Directly

For more control, inject the RateLimitService directly:

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

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

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

    if (!result.allowed) {
      throw new HttpException(
        `Rate limit exceeded. Try again in ${Math.ceil((result.resetTime - Date.now()) / 1000)} seconds.`,
        HttpStatus.TOO_MANY_REQUESTS
      );
    }

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

Time Window Format

The window parameter accepts the following formats:

FormatDescriptionExample
sSeconds30s = 30 seconds
mMinutes5m = 5 minutes
hHours1h = 1 hour
dDays1d = 1 day

Health Check

Monitor Redis connectivity:

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

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

  async checkRedis() {
    const isHealthy = await this.rateLimitService.healthCheck();
    return {
      redis: isHealthy ? 'connected' : 'disconnected',
    };
  }
}

Next Steps