Rate limiting adalah salah satu fitur yang terasa opsional sampai hari seseorang menemukan endpoint Anda dan mengirim 10.000 request dalam 60 detik, menjatuhkan database Anda. Modul @nestjs/throttler NestJS membuat implementasi rate limiting mudah, tapi penyimpanan in-memory default rusak ketika Anda melakukan scaling ke beberapa instance. Rate limiting berbasis Redis adalah pendekatan yang benar untuk produksi.
Default @nestjs/throttler menggunakan penyimpanan in-memory — Map yang melacak jumlah request per IP klien per jendela TTL. Ini berfungsi sempurna untuk deployment satu instance. Saat Anda menambahkan instance kedua di balik load balancer, instance A tidak tahu tentang request yang menekan instance B. Redis menyelesaikan ini dengan menyediakan state bersama di semua instance.
Instal paket yang diperlukan: @nestjs/throttler dan @nest-lab/throttler-storage-redis. Paket throttler-storage-redis mengimplementasikan antarmuka ThrottlerStorage menggunakan Redis sebagai backend. Konfigurasikan ThrottlerModule di AppModule Anda dengan penyedia penyimpanan Redis. Daftarkan ThrottlerGuard secara global menggunakan token APP_GUARD di array providers Anda sehingga setiap route dilindungi secara default.
In-Memory Throttler (single instance — breaks at scale)
────────────────────────────────────────────────────────
Client ──► Instance A (Map: {clientIP: requestCount})
• Works fine for 1 instance
• Client sends to Instance B → counter resets → bypass!
Redis Throttler (multi-instance — production correct)
────────────────────────────────────────────────────────
Client ──► Instance A ──► Redis INCR("throttle:clientIP:ttl")
Client ──► Instance B ──► Redis INCR("throttle:clientIP:ttl") ← same key!
Client ──► Instance C ──► Redis INCR("throttle:clientIP:ttl") ← same key!
All instances share one counter → correct rate limiting at any scale
Request flow:
Client Request
│
▼
ThrottlerGuard.canActivate()
│
├── Get tracker (IP or userId)
├── Redis INCR(key) + set TTL if new
├── If count > limit → 429 Too Many Requests + Retry-After header
└── If count <= limit → continue to route handler
Three-tier config (what I use in production):
/auth/* → 5 req / 60s (brute-force protection)
/api/* → 100 req / 60s (standard API)
/export/* → 2 req / 300s (heavy operation)Dari mengimplementasikan rate limiting pada API NestJS ERP: gunakan beberapa konfigurasi throttle untuk tipe endpoint yang berbeda. Endpoint autentikasi publik (login, reset kata sandi) mendapatkan batas agresif: 5 request per menit. Endpoint API umum mendapatkan batas yang lebih longgar: 100 request per menit. Endpoint ekspor massal mendapatkan batas yang sangat ketat: 2 request per 5 menit. Gunakan decorator @Throttle() pada controller atau route tertentu untuk menimpa konfigurasi global.
Konfigurasi ThrottlerModule di AppModule: impor ThrottlerModule dengan array konfigurasi throttle (masing-masing memiliki nama, ttl dalam milidetik, dan limit). Opsi storage menunjuk ke ThrottlerStorageRedisService Anda. Daftarkan APP_GUARD dengan ThrottlerGuard di array providers. Setiap route dalam aplikasi Anda sekarang menegakkan rate limit. Endpoint yang harus melewati rate limiting (pemeriksaan kesehatan, pemantauan internal) menggunakan decorator @SkipThrottle().
# Install packages
npm install --save @nestjs/throttler @nest-lab/throttler-storage-redis ioredis
# app.module.ts
import { ThrottlerModule, ThrottlerGuard } from '@nestjs/throttler'
import { ThrottlerStorageRedisService } from '@nest-lab/throttler-storage-redis'
import { APP_GUARD } from '@nestjs/core'
@Module({
imports: [
ThrottlerModule.forRoot({
throttlers: [
{ name: 'short', ttl: 60000, limit: 5 }, // 5 req/min (auth routes)
{ name: 'medium', ttl: 60000, limit: 100 }, // 100 req/min (default)
{ name: 'long', ttl: 300000, limit: 2 }, // 2 req/5min (heavy ops)
],
storage: new ThrottlerStorageRedisService({
host: process.env.REDIS_HOST,
port: parseInt(process.env.REDIS_PORT ?? '6379'),
password: process.env.REDIS_PASSWORD,
}),
}),
],
providers: [
{ provide: APP_GUARD, useClass: ThrottlerGuard }, // global guard
],
})
export class AppModule {}
# Override per-controller: use specific throttle tier
@Controller('auth')
@Throttle({ short: { limit: 5, ttl: 60000 } }) // strict for auth
export class AuthController {
@Post('login')
login(@Body() dto: LoginDto) { ... }
}
# Skip for health checks:
@Get('health')
@SkipThrottle()
health() { return { status: 'ok' } }
# Custom tracker — use userId instead of IP for authenticated routes
@Injectable()
export class UserThrottlerGuard extends ThrottlerGuard {
protected async getTracker(req: Record<string, unknown>): Promise<string> {
const userId = (req as any).user?.sub
return userId ?? (req as any).ip // fallback to IP if not authenticated
}
}
# Custom exception filter — add Retry-After header
@Catch(ThrottlerException)
export class ThrottlerExceptionFilter implements ExceptionFilter {
catch(exception: ThrottlerException, host: ArgumentsHost) {
const ctx = host.switchToHttp()
const response = ctx.getResponse<Response>()
response
.status(429)
.setHeader('Retry-After', '60')
.json({
statusCode: 429,
message: 'Too Many Requests',
retryAfter: 60,
})
}
}Throttler default mengelompokan request berdasarkan alamat IP. Untuk API terautentikasi, Anda ingin mengelompokan berdasarkan ID pengguna — satu IP bisa mewakili seluruh tim perusahaan. Timpa metode getTracker() ThrottlerGuard untuk mengembalikan ID pengguna dari payload JWT jika terautentikasi, kembali ke IP untuk request yang tidak terautentikasi.
Saat request ditolak dengan 429 Too Many Requests, selalu sertakan header Retry-After — ini memberi tahu klien kapan mereka bisa mencoba lagi. Tanpanya, klien biasanya mengimplementasikan exponential backoff dari awal atau mencoba lagi segera (memperburuk masalah). Throttler NestJS menambahkan header X-RateLimit-Limit, X-RateLimit-Remaining, dan X-RateLimit-Reset ke respons yang berhasil secara otomatis. Untuk request yang ditolak, tambahkan header Retry-After di ThrottlerExceptionFilter Anda.
@nest-lab/throttler-storage-redis menyimpan state throttle di kunci Redis dengan format throttle:<tracker>:<ttl>. Setiap kunci memiliki TTL otomatis yang cocok dengan jendela throttle Anda. Saat TTL kedaluwarsa, kunci dihapus dan hitungan direset. Operasi INCR atomik Redis memastikan request bersamaan dari klien yang sama tidak melampaui batas secara bersamaan.
Untuk API NestJS apa pun yang melayani lebih dari satu instance, throttling berbasis Redis dengan @nestjs/throttler + @nest-lab/throttler-storage-redis adalah standar saya. Pengaturan membutuhkan kurang dari satu jam dan memberikan perlindungan yang berarti terhadap penyalahgunaan. Saya mengonfigurasi tiga tingkat throttle: ketat untuk endpoint auth, standar untuk API biasa, dan longgar untuk endpoint publik read-only.
Rate limiting adalah satu lapisan. Perlindungan API produksi membutuhkan banyak lapisan: (1) Rate limiting (NestJS Throttler + Redis) — mencegah banjir request. (2) Validasi input (class-validator + class-transformer pipes) — mencegah input yang cacat. (3) Guard autentikasi (Passport JWT) — mencegah akses yang tidak terautentikasi. (4) Guard otorisasi — mencegah mengakses sumber daya yang bukan milik Anda. (5) Pencegahan injeksi SQL (query berparameter Prisma). (6) Perlindungan DDoS tingkat jaringan (Cloudflare atau setara).