Fala Dev, tudo em riba?
Este é o segundo artigo da série sobre Rate Limiting, onde iremos explorar 4 estratégias essenciais para proteger suas APIs desenvolvidas com Node.js. No primeiro artigo, apresentamos os conceitos fundamentais do rate limiting, entendemos por que ele é indispensável para APIs modernas e conhecemos os principais algoritmos que sustentam essa técnica de segurança.
Agora chegou o momento de colocar a mão no código e implementar nossa primeira estratégia: o Rate Limiting Global. Esta é a abordagem mais simples e direta para adicionar uma camada de proteção à sua API, aplicando o mesmo limite de requisições para todos os endpoints de forma uniforme.
Se você não leu o primeiro artigo da série, recomendo fortemente sua leitura antes de prosseguir. Lá explicamos conceitos fundamentais como Fixed Window, Sliding Window, Token Bucket e Leaky Bucket, além de discutirmos por que o rate limiting é crucial para prevenir ataques DoS, controlar custos em ambientes cloud e garantir uma distribuição justa de recursos.
Primeira estratégia: Rate Limiting Global?
O rate limiting global funciona como um porteiro na entrada de uma festa exclusiva: ele controla quantas pessoas (requisições) podem entrar em um determinado período de tempo, independentemente de qual sala (endpoint) elas pretendem visitar.
Quando implementamos essa estratégia, estabelecemos um limite único que se aplica a todos os endpoints da API. Por exemplo, se configurarmos um limite de 10 requisições por minuto, um cliente poderá fazer no máximo 10 chamadas em 60 segundos, não importa se todas forem para o mesmo endpoint ou distribuídas entre diferentes endpoints.
Quando usar rate limiting global?
Esta estratégia é ideal para cenários onde você precisa de uma proteção rápida e uniforme, especialmente útil em aplicações de pequeno a médio porte. Algumas situações perfeitas para aplicá-la incluem APIs públicas que precisam de proteção básica contra abusos, microsserviços internos com comportamento de tráfego previsível e prototipagem rápida onde você precisa adicionar segurança sem complexidade.
Implementando rate limite global com Node.js e Express
Vamos implementar nossa primeira estratégia utilizando a biblioteca express-rate-limit, que oferece uma solução robusta e amplamente testada pela comunidade. O código completo desta implementação está disponível no repositório oficial da série no GitHub.
Instalando as Dependências
Primeiro, precisamos instalar as bibliotecas necessárias:
npm install express express-rate-limit
npm install --save-dev @types/express @types/node
O pacote express-rate-limit é uma solução leve e eficiente que resolve o problema de limitação de taxa sem necessidade de infraestrutura adicional para aplicações de pequeno e médio porte.
Estrutura básica da aplicação
Vamos criar o arquivo rate-limit-global.ts com a estrutura básica do nosso servidor Express:
import express from ‘express’;
import { rateLimit } from ‘express-rate-limit’;
const app = express();
const PORT = 3333;
Neste trecho inicial, importamos o Express para criar nosso servidor e a função rateLimit da biblioteca express-rate-limit, que será responsável por toda a lógica de limitação. Definimos também a porta onde nosso servidor ficará escutando as requisições.
Configurando o rate limiter
Agora vamos criar a configuração central do nosso rate limiter:
const globalLimiter = rateLimit({
windowMs: 60 * 1000,
limit: 10,
message: ‘Muitas requisições deste IP. Tente novamente após 1 minuto.’,
standardHeaders: true,
legacyHeaders: false
});
Vamos analisar cada parâmetro desta configuração em detalhes:
- windowMs: define a janela de tempo em milissegundos durante a qual as requisições serão contabilizadas. No nosso exemplo,
60 * 1000equivale a 60.000 milissegundos ou 1 minuto. Este é o intervalo que será usado como base para aplicar o limite; - limit: estabelece o número máximo de requisições permitidas dentro da janela de tempo definida. Com o valor 10, cada cliente poderá fazer no máximo 10 requisições por minuto. Quando esse limite for atingido, as requisições subsequentes serão bloqueadas até que a janela se renove;
- message: personaliza a mensagem de erro retornada ao cliente quando o limite é excedido. É importante que esta mensagem seja clara e informe ao usuário quando ele poderá tentar novamente;
- standardHeaders: quando configurado como
true, habilita o suporte aos cabeçalhos RateLimit recomendados pela IETF (Internet Engineering Task Force). Estes são os headers modernos do padrão HTTP que não requerem o prefixoX-, incluindoRateLimit-Limit,RateLimit-RemainingeRateLimit-Reset; - legacyHeaders: define se os headers legados com prefixo
X-devem ser enviados nas respostas. Quando configurado comofalse, desabilita headers comoX-RateLimit-LimiteX-RateLimit-Remaining. Antes dos padrões atuais, todo header customizado utilizava o prefixoX-, mas isso não é mais necessário desde que o header não entre em conflito com headers já existentes na especificação HTTP.
Aplicando o rate limit global via middleware
Para que nosso rate limiter funcione em todos os endpoints, precisamos registrá-lo como um middleware global:
app.use(globalLimiter);
Esta linha é crucial: ao usar app.use() sem especificar um caminho, estamos instruindo o Express a executar o middleware globalLimiter em todas as requisições, independentemente da rota solicitada. O middleware será executado antes de qualquer lógica de rota, interceptando e verificando se o limite foi atingido.
Criando os endpoints
Agora vamos criar dois endpoints simples para testar nossa implementação:
app.get(’/’, (req, res) => {
res.json({
message: ‘API protegida por rate limiting’,
timestamp: new Date().toISOString()
});
});
app.get(’/api/users’, (req, res) => {
const users = [
{ id: 1, name: ‘João Silva’ },
{ id: 2, name: ‘Maria Santos’ },
{ id: 3, name: ‘Pedro Oliveira’ }
];
res.json({
message: ‘Lista de usuários’,
data: users
});
});
O primeiro endpoint responde na rota raiz (/) retornando uma mensagem confirmando que a API está protegida, junto com o timestamp da requisição. O segundo endpoint (/api/users) simula uma listagem de usuários, retornando um array com dados fictícios.
Iniciando o servidor
Por fim, colocamos nosso servidor para escutar na porta definida:
app.listen(PORT, () => {
console.log(`🚀 Servidor rodando na porta ${PORT}`);
console.log(`📊 Rate Limit: 10 requisições por minuto`);
});
Esta função inicia o servidor e exibe mensagens informativas no console, facilitando o acompanhamento do estado da aplicação durante o desenvolvimento.^1
Testando a implementação
Para testar nossa implementação, podemos usar ferramentas como cURL, Postman, Insomnia ou criar um arquivo .http no VS Code. Vamos criar requisições para observar o comportamento do rate limiter:
### Requisição para rota principal
GET http://localhost:3333/
### Requisição para listagem de usuários
GET http://localhost:3333/api/users
Ao executar estas requisições repetidamente, você observará o seguinte comportamento:
- Primeiras requisições: Os headers da resposta incluirão
RateLimit-Limit: 10,RateLimit-Remaining: 9(depois 8, 7, e assim por diante), indicando quantas requisições ainda restam na janela atual; - Após 10 requisições: A API retornará o status HTTP
429 Too Many Requestscom a mensagem personalizada que configuramos. Os headers mostrarãoRateLimit-Remaining: 0eRateLimit-Resetcom o timestamp de quando a janela será renovada; - Alternando entre endpoints: Note que o contador é compartilhado entre todos os endpoints. Se você fizer 5 requisições para
/e 5 para/api/users, o limite será atingido, pois o rate limiting é global.
Por debaixo dos panos
A biblioteca express-rate-limit implementa o algoritmo de janela fixa (Fixed Window) por padrão. Isso significa que o tempo é dividido em intervalos fixos (no nosso caso, de 1 minuto) e permite um número X de requisições nessa janela.
O controle é feito em memória por padrão, utilizando a memória RAM da própria aplicação para armazenar o contador de requisições de cada cliente. A identificação do cliente é feita através do endereço IP (req.ip), extraído automaticamente do objeto de requisição do Express.
Importante saber: existem limitações
Esta abordagem funciona perfeitamente para aplicações que rodam em uma única instância. Porém, se sua aplicação escala horizontalmente (múltiplas instâncias/máquinas), cada instância manterá seu próprio contador em memória.
Por exemplo, com 2 instâncias e um load balancer distribuindo as requisições, um cliente poderia teoricamente fazer 20 requisições (10 em cada instância) antes de ser bloqueado. Para ambientes com múltiplas instâncias, a quarta estratégia desta série apresentará uma solução usando Redis para centralizar o controle.
Headers Informativos: Guiando os Clientes
Os headers de rate limiting são essenciais para uma boa experiência do desenvolvedor que consome sua API. Vejamos o que cada um significa:
- RateLimit-Limit: informa o número total de requisições permitidas na janela de tempo atual. No nosso exemplo, sempre retornará
10; - RateLimit-Remaining: mostra quantas requisições ainda restam antes de atingir o limite. Este valor decresce a cada requisição: 9, 8, 7… até 0.
- RateLimit-Reset: indica quando o limite será renovado, geralmente em formato de timestamp Unix (epoch) ou tempo relativo em segundos.
Estes headers permitem que aplicações cliente implementem lógica inteligente, como aguardar automaticamente antes de fazer novas requisições ou alertar usuários sobre a proximidade do limite.
Vantagens e desvantagens da estratégia global
Vantagens
- A implementação é extremamente simples e rápida, exigindo apenas poucas linhas de código para proteger toda a API;
- Funciona imediatamente sem necessidade de infraestrutura adicional como bancos de dados ou caches;
- É ideal para proteção básica contra abusos e ataques de negação de serviço em aplicações de pequeno a médio porte.
Desvantagens
- Trata todos os endpoints da mesma forma, sem considerar que alguns podem ser mais críticos ou consumir mais recursos que outros;
- Endpoints simples de leitura são limitados da mesma forma que operações complexas de escrita ou processamento pesado;
- Também não diferencia entre tipos de usuários – usuários premium e gratuitos recebem o mesmo tratamento;
Boas práticas de implementação
- Sempre inclua headers informativos nas respostas para que os clientes possam se adaptar aos limites;
- Configure limites realistas baseados na capacidade real da sua infraestrutura e no comportamento esperado dos usuários;
- Monitore as métricas de rate limiting para ajustar os valores ao longo do tempo – limites muito restritivos frustram usuários legítimos, enquanto limites muito permissivos não protegem adequadamente;
- Documente claramente os limites na documentação da sua API. Desta forma quem utilizá-la não será surpreendido pela limitação nas requisições;
- Para aplicações em produção com alta demanda, considere migrar para soluções mais robustas (como veremos na quarta estratégia com Redis) ou usar serviços gerenciados de API Gateway oferecidos pelos cloud proveiders;
Próximos passos
O rate limiting global é uma excelente primeira linha de defesa e serve como base para entendermos as estratégias mais avançadas. No próximo artigo, exploraremos o Rate Limiting por Endpoint, onde aprenderemos a aplicar limites diferenciados para cada endpoint, permitindo maior flexibilidade e controle granular sobre o tráfego da API.
Enquanto isso, experimente modificar os valores de windowMs e limit no código, teste diferentes configurações e observe o comportamento. O código completo desta implementação está disponível no repositório GitHub da série.
Gostou do conteúdo? Deixe seu feedback nos comentários e compartilhe com outros desenvolvedores que possam se beneficiar dessas técnicas de proteção de APIs. Nos vemos no próximo artigo da série!

Deixe um comentário