homeresume
 
   

Redis as custom storage for NestJS rate limiter

September 14, 2021

Rate 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.ts
ThrottlerModule.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.

 

© 2021