Destino Dev

Blog

Limitador de requisições no NestJS com Throttler + Redis: passo a passo para iniciantes

Por John Pires · 10 de março de 2026

Contexto: quando você vai usar isso

Proteção contra abuso e picos de tráfego é uma necessidade básica em qualquer API pública. Para quem está começando, fazer rate limiting pode parecer complicado, mas a ideia central é simples: limitar quantas requisições um cliente pode fazer em um intervalo de tempo.

No ecossistema NestJS existe o pacote oficial @nestjs/throttler que resolve essa necessidade com integrações fáceis. Porém, o armazenamento padrão é em memória, o que é problemático em ambientes com múltiplas instâncias. É aí que o Redis entra: ele fornece um armazenamento centralizado, rápido e compartilhado entre instâncias.

Este artigo guia você, passo a passo, na configuração do Throttler com Redis — desde o básico em memória até a configuração com Redis e dicas práticas de produção. O foco é ser didático: analogias simples, comandos reais e exemplos funcionais para experimentar localmente.

Você deve aplicar isso quando: expor endpoints públicos, quiser limitar acesso a rotas sensíveis, proteger rotas autenticadas de abuso ou controlar bots e scraping.

O que o Throttler resolve

O Throttler faz o trabalho de contar requisições por chave (IP ou outro identificador) e retornar HTTP 429 quando o limite é ultrapassado. A biblioteca trata TTL, contagem e integração com Guards do NestJS.

Por que usar Redis

Redis é um banco de dados em memória rápido e compartilhado. Em arquiteturas com múltiplas instâncias da API (containers, máquinas), um armazenamento centralizado evita que cada instância tenha contadores diferentes, o que quebraria o controle de limite.

Explicação visual com diagramas mentais

Imagine o rate limiter como um porteiro em frente a um prédio:

  • Cada visitante (requisição) desfila para o porteiro.
  • O porteiro consulta um caderno central (Redis) que conta quantas vezes o visitante já entrou no período.
  • Se a contagem exceder o permitido, o porteiro bloqueia a entrada (HTTP 429).

Quando só existe um porteiro (uma instância), um caderno local funciona. Mas com vários porteiros (várias instâncias), o caderno precisa ser o mesmo para todos — é o papel do Redis.

Fluxo mental passo a passo

  1. Requisição chega ao NestJS.
  2. Guard de Throttler calcula a chave (por IP ou anotação do usuário).
  3. Guard pede ao armazenamento (memória ou Redis) a contagem atual.
  4. Se contagem < limite, incrementa e permite; caso contrário, retorna 429.

Componentes envolvidos

  • ThrottlerModule: configura ttl e limite padrão.
  • ThrottlerGuard: guard global ou por controlador/rota.
  • ThrottlerStorage (Redis): implementação do armazenamento.
  • Decorators: @Throttle e @SkipThrottle para rotas específicas.
Dica: em desenvolvimento, testar primeiro em memória para validar lógica; em staging/prod, passar para Redis para consistência entre instâncias.

Primeiro exemplo simples (em memória)

Vamos montar uma API mínima com NestJS e aplicar Throttler em memória. Serve para validar comportamento antes de integrar Redis.

Instalação rápida

Execute estes comandos no terminal do seu projeto NestJS:

npm install @nestjs/throttler

Contexto: o comando instala o módulo de rate limiting do NestJS. A partir daqui vamos configurar o módulo na aplicação.

Configuração básica no AppModule

Crie ou abra src/app.module.ts e adicione o ThrottlerModule. Abaixo tem um exemplo funcional que aplica limite global de 10 requisições por 60 segundos e registra o ThrottlerGuard global via provider APP_GUARD.

import { Module } from '@nestjs/common';
import { APP_GUARD } from '@nestjs/core';
import { ThrottlerModule, ThrottlerGuard } from '@nestjs/throttler';
import { AppController } from './app.controller';

@Module({
  imports: [
    ThrottlerModule.forRoot({
      ttl: 60, // tempo em segundos
      limit: 10, // número de requisições permitidas no TTL
    }),
  ],
  controllers: [AppController],
  providers: [
    {
      provide: APP_GUARD,
      useClass: ThrottlerGuard,
    },
  ],
})
export class AppModule {}

O que faz: registra o módulo para usar guard global. O que acontece: agora todas rotas da aplicação respeitam 10 requisições por 60s por chave padrão (IP).

Controller de exemplo

Crie um controller simples com uma rota que respeita o throttle padrão e outra que define limite personalizado com o decorator @Throttle.

import { Controller, Get } from '@nestjs/common';
import { Throttle, SkipThrottle } from '@nestjs/throttler';

@Controller('demo')
export class AppController {
  @Get('default')
  default() {
    return { ok: true, msg: 'rota padrão com limite global' };
  }

  @Get('tight')
  @Throttle(2, 30) // 2 requisições a cada 30 segundos
  tight() {
    return { ok: true, msg: 'rota com limite mais restrito' };
  }

  @Get('open')
  @SkipThrottle()
  open() {
    return { ok: true, msg: 'rota sem limite (use com cuidado)' };
  }
}

O que faz: demonstra uso de @Throttle e @SkipThrottle. O que acontece: você pode testar com curl ou Postman e observar 429 quando limites são atingidos.

Segundo exemplo mais realista (Throttler + Redis)

Agora que você validou o comportamento com memória, vamos integrar Redis para uso em produção ou em setups com múltiplas instâncias. Usaremos o pacote de armazenamento Redis compatível com Throttler e o cliente ioredis.

Instalação e serviços necessários

Instale dependências e garanta que o Redis esteja disponível localmente ou em um endpoint gerenciado:

npm install @nestjs/throttler throttler-storage-redis ioredis
# certifique-se de ter o redis rodando em localhost:6379

Contexto: o pacote throttler-storage-redis fornece uma implementação que o Throttler consegue usar como armazenamento. ioredis é cliente robusto para Node.js.

Configuração com ThrottlerModule.forRootAsync

Abaixo um AppModule que usa ThrottlerModule.forRootAsync para criar a instância de armazenamento Redis com variáveis de ambiente. Esse padrão facilita trocar configurações por ambiente.

import { Module } from '@nestjs/common';
import { ConfigModule, ConfigService } from '@nestjs/config';
import { ThrottlerModule } from '@nestjs/throttler';
import { ThrottlerStorageRedisService } from 'throttler-storage-redis';
import { APP_GUARD } from '@nestjs/core';
import { ThrottlerGuard } from '@nestjs/throttler';
import { AppController } from './app.controller';

@Module({
  imports: [
    ConfigModule.forRoot({ isGlobal: true }),
    ThrottlerModule.forRootAsync({
      imports: [ConfigModule],
      inject: [ConfigService],
      useFactory: async (config: ConfigService) => ({
        ttl: 60,
        limit: 20,
        storage: new ThrottlerStorageRedisService({
          host: config.get('REDIS_HOST', '127.0.0.1'),
          port: +config.get('REDIS_PORT', 6379),
        }),
      }),
    }),
  ],
  controllers: [AppController],
  providers: [
    {
      provide: APP_GUARD,
      useClass: ThrottlerGuard,
    },
  ],
})
export class AppModule {}

O que faz: registra Throttler com armazenamento Redis. O que acontece: as contagens são gravadas no Redis, permitindo consistência em instâncias múltiplas.

Testando em cluster ou com Docker

Em Docker Compose, exponha múltiplos containers da sua API apontando para o mesmo serviço Redis. Você verá que as requisições somam no mesmo contador central.

version: '3.7'
services:
  redis:
    image: redis:6
    ports:
      - '6379:6379'
  api:
    build: .
    environment:
      REDIS_HOST: redis
    depends_on:
      - redis
    ports:
      - '3000:3000'
    deploy:
      replicas: 2

Contexto: este docker-compose simples ilustra várias réplicas da mesma API conectando num único Redis. O que acontece: o Throttler passa a contar globalmente entre réplicas.

Erros comuns de iniciante e como evitar

Mesmo com exemplos prontos, iniciantes cometem erros frequentes que quebram o rate limiting ou causam comportamento inesperado. Listei os principais com soluções práticas.

1) Esquecer de registrar o APP_GUARD

Erro: instalar ThrottlerModule, mas não registrar o ThrottlerGuard global. Resultado: decorators como @Throttle não são aplicados automaticamente e suas rotas não serão protegidas.

Solução: garantir provider APP_GUARD apontando para ThrottlerGuard no array providers do módulo raiz (veja exemplos acima).

2) Usar armazenamento em memória em produção

Erro: manter configuração padrão em memória em ambiente com múltiplas instâncias. Resultado: cada instância conta separadamente e o rate limit é inefetivo.

Solução: usar Redis (ou outro storage compartilhado). Em Kubernetes ou containers, sempre prefira Redis para rate limiting.

3) Não lidar com proxy / X-Forwarded-For

Erro: o Throttler por padrão usa o IP remoto direto. Em infra com proxy reverso (NGINX, ALB) o IP pode ser do proxy, causando todos requests parecerem vir do mesmo IP.

Solução: configurar NestJS para confiar no proxy e usar cabeçalho X-Forwarded-For para extrair IP real. No NestJS, configure app.set('trust proxy', 1) no bootstrap, e verifique como o Throttler calcula a chave. Alternativamente, crie um ThrottlerGuard custom que usa o header para formar a chave.

4) TTL mal interpretado ou clocks diferentes

Erro: inconsistência de tempo entre instâncias (clock skew) pode afetar expiração de chaves no Redis.

Solução: sincronize NTP nos servidores e prefira usar TTL em segundos com Redis. Teste comportamento com clocks controlados e monitore Expire dos keys no Redis.

Dicas práticas e troubleshooting

  • Use redis-cli para inspecionar chaves criadas pelo Throttler e verificar valores e TTL.
  • Logs de 429 devem incluir informação suficiente para reproduzir: IP, rota e contador atual.
  • Em ambientes serverless, evite armazenamento em memória. Prefira Redis gerenciado.
Dica: antes de mover para produção, rode testes de carga simples (wrk, vegeta) para validar limites e observar comportamento real do Throttler+Redis.

Conclusão prática

Você aprendeu quando e por que usar Throttler no NestJS, e como integrar com Redis para produção. O caminho recomendado para iniciantes é: implementar primeiro em memória para validar a lógica, depois migrar para Redis para garantir consistência entre instâncias.

Resumo das ações práticas:

  1. Instale @nestjs/throttler e valide limites locais em memória.
  2. Adicione APP_GUARD com ThrottlerGuard para proteção global.
  3. Se escalar para múltiplas instâncias, instale throttler-storage-redis + ioredis e configure ThrottlerModule.forRootAsync com ThrottlerStorageRedisService.
  4. Trate proxy/IP real e teste com ferramentas de carga.

Para seguir em frente, teste os exemplos apresentados, observe as chaves no Redis e experimente ajustar ttl/limit para sua aplicação. Rate limiting bem aplicado reduz abuso, mantém disponibilidade e protege custos.

Se quiser, posso fornecer um repositório de exemplo completo (Docker + NestJS) pronto para clonar e rodar localmente, com scripts de teste de carga e dashboards para monitorar contadores no Redis.

Boa prática: mantenha logs claros de 429, monitore Redis (latência e uso de memória) e ajuste limites com base em métricas reais de uso.

Assuntos: NestJS, throttler, NoSQL, guards, rate-limiting

Leia também