Erro 429 em APIs Node.js? Como implementar Rate Limiting
Aprenda a proteger rotas no Express usando Redis e Node.js para controlar o tráfego por IP, evitando sobrecargas e ataques de negação de serviço.
Resumo
- O código HTTP 429 indica que o cliente excedeu o limite de requisições permitidas em uma janela de tempo.
- O Redis atua como um banco de dados em memória ultrarrápido ideal para contadores atômicos e expiração automática.
- Identificar o usuário pelo endereço IP ou por tokens de autenticação garante um controle granular por cliente.
- A estratégia de janela deslizante oferece um equilíbrio perfeito entre precisão e baixo consumo de recursos.
- Bloquear tráfego excessivo protege a infraestrutura contra raspagem de dados e tentativas automatizadas de invasão.
Por que o seu servidor Node.js precisa de controle de tráfego
Imagine que você abre uma cafeteria popular e uma única pessoa decide entrar na loja a cada segundo para pedir um copo d'água grátis. Em poucos minutos, a fila impede que clientes reais comprem café e o caixa entra em colapso. No universo das APIs, essa situação é comum quando robôs, scripts mal escritos ou ataques de força bruta inundam uma aplicação com milhares de acessos simultâneos.
Quando o servidor não consegue processar tanta demanda, ele desaba, derrubando o serviço para todos os usuários legítimos. É exatamente para evitar esse caos que utilizamos o Rate Limiting, um mecanismo de controle que limita o número de vezes que um endereço pode chamar uma rota em um intervalo específico. Quando o limite é estourado, a aplicação responde com o famigerado erro 429 Too Many Requests.
Muitas equipes tentam resolver isso guardando contadores na própria memória RAM do processo Node.js. Embora funcione para testes simples, essa abordagem falha miseravelmente em ambientes modernos onde escalamos a aplicação em múltiplos contêineres ou servidores. Se a requisição A cai no servidor um e a requisição B cai no servidor dois, eles perdem a conta conjunta, abrindo brechas para abusos.
Como o Redis resolve o controle de taxa em larga escala
Para manter o controle de requisições sincronizado entre diferentes instâncias da sua API, precisamos de uma fonte de verdade centralizada, rápida e volátil. É aqui que entra o Redis, um armazenamento de dados em memória conhecido por sua velocidade extrema e capacidade de expirar chaves automaticamente após alguns segundos.
Na prática, cada vez que um usuário faz uma requisição, a API incrementa um contador atômico no Redis associado ao endereço IP daquele visitante. Se o contador ultrapassa o teto estipulado, a aplicação barra a execução imediatamente. O Redis lida com essas operações em frações de milissegundo, garantindo que a verificação de segurança não adicione lentidão perceptível ao sistema.
Antes de implementar regras complexas de tráfego, vale a pena garantir que outros aspectos de infraestrutura da sua API estejam redondos, como resolver problemas comuns de configuração de CORS no Express para evitar bloqueios indesejados no navegador. Com a fundação limpa, podemos estruturar a lógica do limitador de taxa sem interferir nas requisições legítimas que vêm de origens confiáveis.
Implementando Rate Limiting com Express e Redis na prática
Para colocar a mão na massa, vamos construir um middleware em Node.js utilizando o framework Express junto com a biblioteca ioredis. O objetivo é interceptar as chamadas recebidas, consultar o Redis para verificar quantas vezes aquele IP acessou o endpoint nos últimos sessenta segundos e decidir se a porta permanece aberta ou fechada.
Primeiro, instalamos os pacotes necessários no terminal do projeto com o comando de gerenciamento de dependências. Em seguida, configuramos a conexão com o servidor Redis e escrevemos a lógica de contagem incremental com tempo de expiração automático configurado por chave de IP.
const express = require('express');const Redis = require('ioredis');const app = express();const redis = new Redis();const WINDOW_SIZE_IN_SECONDS = 60;const MAX_REQUESTS = 10;app.use(async (req, res, next) => { const ip = req.ip; const key = `rate-limit:${ip}`; try { const requests = await redis.incr(key); if (requests === 1) { await redis.expire(key, WINDOW_SIZE_IN_SECONDS); } if (requests > MAX_REQUESTS) { return res.status(429).json({ error: 'Muitas requisições. Tente novamente mais tarde.' }); } next(); } catch (error) { console.error('Erro no rate limiter:', error); next(); }});app.get('/api/dados', (req, res) => { res.json({ mensagem: 'Acesso liberado com sucesso!' });});app.listen(3000, () => { console.log('Servidor rodando na porta 3000');});Esse código simples mas robusto garante que qualquer IP que ultrapasse dez requisições dentro de uma janela de um minuto receba uma resposta 429 instantânea. O segredo está no comando incr do Redis, que executa a leitura e a soma de forma atômica, evitando condições de corrida mesmo sob alto volume de acessos paralelos.
Armadilhas comuns e boas práticas na configuração
Embora a lógica básica funcione bem, cenários do mundo real exigem cuidados adicionais. Se a sua aplicação estiver atrás de um proxy reverso, balanceador de carga ou serviços de nuvem como Cloudflare, o endereço IP real do cliente pode vir mascarado nos cabeçalhos da requisição, como o x-forwarded-for, em vez de aparecer diretamente no objeto de conexão do Express.
Para contornar isso, é fundamental habilitar a propriedade trust proxy no Express para que a aplicação saiba ler corretamente o IP original do usuário. Outro ponto crítico é definir mensagens de erro claras e informativas no corpo da resposta HTTP, acompanhadas opcionalmente pelo cabeçalho Retry-After para orientar clientes legítimos sobre quando podem tentar novamente.
Em cenários avançados onde múltiplos serviços compartilham recursos sensíveis de banco de dados, o controle de taxa por IP pode ser complementado com estratégias de isolamento de conexões ou uso de pool de conexões eficientes para evitar gargalos. Proteger a camada de transporte e a camada de dados em conjunto garante resiliência total à arquitetura. Para aprofundar esses conceitos na prática em produção, confira também nossa análise técnica sobre Como testar display LCD com multímetro na bancada.
Considerações finais sobre resiliência de APIs
Implementar controle de taxa com Node.js e Redis é uma das defesas mais eficazes e baratas que você pode adicionar a uma arquitetura de microsserviços ou monolitos modernos. Além de bloquear tentativas maliciosas de varredura e raspagem de dados, essa estratégia blinda seus servidores contra falhas em cascata geradas por clientes com bugs em loops infinitos.
Ao adotar essa prática desde o início do desenvolvimento, você garante previsibilidade operacional, protege seus bancos de dados contra picos anômalos de leitura e melhora significativamente a experiência dos usuários que dependem de um sistema estável e sempre disponível.