Что такое Throttler и как реализовать rate limiting в NestJS?
Что такое rate limiting
Rate limiting (ограничение частоты запросов) — техника защиты API от избыточной нагрузки: брутфорса паролей, спама, парсинга данных ботами, случайных или намеренных всплесков трафика. Сервер отслеживает количество запросов от клиента (по IP, токену, пользователю) за интервал времени и отклоняет запросы сверх лимита с кодом 429 Too Many Requests.
Throttler в NestJS
В NestJS для этого есть официальный пакет @nestjs/throttler. Он реализует rate limiting как Guard, использующий алгоритм "фиксированного окна" (fixed window): для каждого трекера (обычно IP) хранится счётчик запросов и время сброса.
Подключение
import { ThrottlerModule, ThrottlerGuard } from '@nestjs/throttler';
import { APP_GUARD } from '@nestjs/core';
@Module({
imports: [
ThrottlerModule.forRoot([
{
ttl: 60000, // окно в миллисекундах
limit: 10, // максимум запросов за окно
},
]),
],
providers: [
{ provide: APP_GUARD, useClass: ThrottlerGuard },
],
})
export class AppModule {}
После этого ThrottlerGuard применяется глобально ко всем маршрутам.
Точечная настройка
Декоратор @Throttle() переопределяет лимиты для конкретного контроллера или метода, @SkipThrottle() отключает проверку. Можно задавать несколько именованных лимитов (например, "short" и "long") для разных сценариев одновременно.
@Throttle({ default: { limit: 3, ttl: 60000 } })
@Post('login')
login() { /* строгий лимит на попытки входа */ }
@SkipThrottle()
@Get('health')
health() { /* без ограничений */ }
Хранилище счётчиков
По умолчанию счётчики хранятся в памяти процесса — это не подходит для нескольких инстансов приложения за балансировщиком, так как каждый инстанс считает отдельно. Для распределённого окружения используют ThrottlerStorageRedisService из @nestjs/throttler-storage-redis, чтобы все инстансы делили общий счётчик.
Кастомизация трекера
По умолчанию клиент идентифицируется по IP (req.ip). Если приложение стоит за прокси (nginx, Cloudflare), нужно включить app.set('trust proxy', 1) и/или переопределить метод getTracker() в наследнике ThrottlerGuard, чтобы учитывать реальный IP из заголовков или идентифицировать по userId для авторизованных пользователей.
@Injectable()
export class UserThrottlerGuard extends ThrottlerGuard {
protected async getTracker(req: Record<string, any>): Promise<string> {
return req.user?.id ?? req.ip;
}
}
Зачем это важно с точки зрения security
Rate limiting — базовый уровень защиты: снижает эффективность брутфорса credential и OTP, защищает от исчерпания ресурсов (CPU, БД, платные сторонние API), ограничивает злоупотребление публичными эндпоинтами. Важно комбинировать его с другими мерами (капча, блокировка аккаунта, WAF), так как throttling по IP легко обойти через прокси или ботнет.
Что хочет услышать интервьюер
Понимание, что rate limiting защищает от брутфорса, DoS-подобной нагрузки и злоупотребления API
Знание пакета @nestjs/throttler и того, что он реализован как Guard
Умение настроить ttl/limit глобально и точечно через @Throttle/@SkipThrottle
Понимание проблемы in-memory хранилища при нескольких инстансах и решения через Redis
Знание про кастомизацию трекера (IP за прокси, идентификация по пользователю)
Пример: Базовая настройка ThrottlerModule
import { ThrottlerModule, ThrottlerGuard } from '@nestjs/throttler';
import { APP_GUARD } from '@nestjs/core';
@Module({
imports: [
ThrottlerModule.forRoot([
{
ttl: 60000,
limit: 10,
},
]),
],
providers: [
{ provide: APP_GUARD, useClass: ThrottlerGuard },
],
})
export class AppModule {}
Пример: Кастомный трекер по userId
@Injectable()
export class UserThrottlerGuard extends ThrottlerGuard {
protected async getTracker(req: Record<string, any>): Promise<string> {
return req.user?.id ?? req.ip;
}
}
Типичные ошибки
Не учитывают, что in-memory throttler считает независимо на каждом инстансе за балансировщиком
Не настраивают trust proxy, из-за чего все запросы считаются с IP самого прокси
Ставят единый общий лимит на все эндпоинты вместо более строгих ограничений для чувствительных маршрутов (login, reset password)
Считают, что throttling полностью защищает от DDoS, хотя он легко обходится через ботнет или ротацию IP
Не обрабатывают код 429 и заголовки X-RateLimit-* на стороне клиента


