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.

Postgres and Redis container services for e2e tests in Github actions

September 8, 2021

For e2e testing we need database connection, provisioning container service for Postgres database can be automated using Github actions. Environment variable for connection string for newly created database can be set in the step for running e2e tests. The same goes for Redis instance.

# ...
jobs:
build:
# Container must run in Linux-based operating systems
runs-on: ubuntu-latest
# Image from Docker hub
container: node:16.3.0-alpine3.13
# ...
strategy:
matrix:
# ...
database-name:
- e2e-testing-db
database-user:
- username
database-password:
- password
database-host:
- postgres
database-port:
- 5432
redis-host:
- redis
redis-port:
- 6379
services:
postgres:
image: postgres:latest
env:
POSTGRES_DB: ${{ matrix.database-name }}
POSTGRES_USER: ${{ matrix.database-user }}
POSTGRES_PASSWORD: ${{ matrix.database-password }}
ports:
- ${{ matrix.database-port }}:${{ matrix.database-port }}
# Set health checks to wait until postgres has started
options:
--health-cmd pg_isready
--health-interval 10s
--health-timeout 5s
--health-retries 5
redis:
image: redis
# Set health checks to wait until redis has started
options: >-
--health-cmd "redis-cli ping"
--health-interval 10s
--health-timeout 5s
--health-retries 5
steps:
# ...
- run: npm run test:e2e
env:
DATABASE_URL: postgres://${{ matrix.database-user }}:${{ matrix.database-password }}@${{ matrix.database-host }}:${{ matrix.database-port }}/${{ matrix.database-name }}
REDIS_URL: redis://${{ matrix.redis-host }}:${{ matrix.redis-port }}
# ...
2020

 

© 2021