Redis as custom storage for NestJS rate limiter
September 14, 2021Rate limiter is a common technique to protect against brute-force attacks. NestJS provides a module for it, the default storage is in-memory. Custom storage should be injected inside ThrottlerModule configuration.
// src/modules/app.module.tsThrottlerModule.forRootAsync({imports: [ThrottlerStorageModule],useFactory: (throttlerStorage: ThrottlerStorageService) => ({ttl,limit,storage: throttlerStorage,}),inject: [ThrottlerStorageService],}),
Custom storage needs to implement the ThrottlerStorage
interface.
// src/modules/throttler-storage/throttler-storage.service.ts@Injectable()export class ThrottlerStorageService implements ThrottlerStorage {constructor(@InjectRedis() private readonly throttlerStorageService: Redis) {}async getRecord(key: string): Promise<number[]> {// ...}async addRecord(key: string, ttl: number): Promise<void> {// ...}}
Redis client should be configured inside RedisModule configuration.
// src/modules/throttler-storage/throttler-storage.module.ts@Module({imports: [RedisModule.forRootAsync({useFactory: (configService: ConfigService) => {const redisUrl = configService.get('REDIS_URL');return {config: {url: redisUrl}};},inject: [ConfigService]})],providers: [ThrottlerStorageService],exports: [ThrottlerStorageService]})export class ThrottlerStorageModule {}
Redis connection should be closed during the graceful shutdown.
// src/app/app.module.ts@Injectable()export class AppModule implements OnApplicationShutdown {constructor(// ...@InjectRedis() private readonly redisConnection: Redis) {}async closeRedisConnection(): Promise<void> {await this.redisConnection.quit();this.logger.log('Redis connection is closed');}async onApplicationShutdown(signal: string): Promise<void> {// ...await Promise.all([// ...this.closeRedisConnection()]).catch((error) => this.logger.error(error.message));}}
@liaoliaots/nestjs-redis library is used in the examples.