Rate limiting and CSRF are infrastructure, not optional
Rate limiting and CSRF ship as shared infrastructure — wired once, not bolted on per route later.
nestjs-security-rate-limit-csrf-infrastructure
Why it matters
| Stake | If ignored |
|---|---|
| Abuse vulnerable |
|
| Agent gets lost |
|
How to fix
@nestjs/throttler on every endpoint. CSRF token on every state-changing operation from a cookie-based client. Baseline headers via helmet by default. All three ship as global infrastructure — wired once in AppModule / main.ts, not bolted on per route later.
Examples
ts
// apps/api/src/main.ts — no helmet, no CSRF
async function bootstrap() {
const app = await NestFactory.create(AppModule);
await app.listen(3000);
}
// apps/api/src/auth/auth.controller.ts — no throttling
@Post('login')
async login(@Body() dto: LoginDto) {
return this.auth.login(dto);
}ts
// apps/api/src/main.ts
import helmet from 'helmet';
import { doubleCsrf } from 'csrf-csrf';
async function bootstrap() {
const app = await NestFactory.create(AppModule);
app.use(helmet());
const { doubleCsrfProtection } = doubleCsrf({
getSecret: () => config.CSRF_SECRET,
cookieName: '__Host-csrf',
cookieOptions: { sameSite: 'strict', secure: true, httpOnly: true },
});
app.use(doubleCsrfProtection);
await app.listen(3000);
}
// apps/api/src/app.module.ts
@Module({
imports: [
ThrottlerModule.forRoot([{ ttl: 60_000, limit: 100 }]),
],
providers: [
{ provide: APP_GUARD, useClass: ThrottlerGuard },
],
})
export class AppModule {}
// apps/api/src/auth/auth.controller.ts — stricter per route
@Throttle({ default: { ttl: 60_000, limit: 5 } })
@Post('login')
async login(@Body() dto: LoginDto) {
return this.auth.login(dto);
}