Lidando com ratelimites ao consumir APIs
Ao consumir APIs é sempre importante saber lidar e respeitar com os limites estabelecidos pela API isso ajuda o serviço se manter disponivel e não sofrer instabilidades, no entanto não é tão intuitivo lidar com isso usando JS (eu pelo menos penei bastante a primeira vez que fui fazer isso)
Para lidar com rate limits ao consumir uma API usando Axios, você pode criar um sistema que respeite os limites de requisições, espere se necessário, e reenvie automaticamente as requisições após o tempo de espera indicado pela API. Vou te mostrar como implementar isso de forma eficiente.
Estrutura Básica Usando Axios
Aqui estão as etapas principais para lidar com rate limits com Axios:
- Usar interceptores do Axios para tratar as respostas.
- Aguardar quando atingir o limite de requisições (com base no cabeçalho
Retry-After
ou status 429). - Gerenciar a fila de requisições para controlar a taxa de envio de novos requests.
Exemplo de Implementação
Este exemplo configura um interceptor de resposta para lidar com o erro 429 (Rate Limit Excedido) e um sistema de espera baseado no cabeçalho Retry-After
retornado pela API.
1. Instalação do Axios
Se você ainda não tem o Axios instalado, pode instalar com o seguinte comando:
npm install axios
2. Código de Rate Limit com Axios
const axios = require('axios');
class RateLimiter {
constructor(requestsPerInterval, intervalMs) {
this.requestsPerInterval = requestsPerInterval; // Número de requisições permitidas por intervalo
this.intervalMs = intervalMs; // Intervalo de tempo (milissegundos)
this.queue = []; // Fila de requisições
this.isProcessing = false;
}
// Adiciona a requisição à fila
enqueue(requestFn) {
return new Promise((resolve, reject) => {
this.queue.push({ requestFn, resolve, reject });
this.processQueue();
});
}
// Processa a fila de requisições
async processQueue() {
if (this.isProcessing) return;
this.isProcessing = true;
while (this.queue.length > 0) {
const { requestFn, resolve, reject } = this.queue.shift();
try {
// Aguardar até que seja possível fazer a requisição
await this.waitIfNeeded();
const result = await requestFn();
resolve(result);
} catch (error) {
reject(error);
}
}
this.isProcessing = false;
}
// Aguarda o tempo necessário caso o limite de requisições tenha sido atingido
async waitIfNeeded() {
const { remaining, resetAt } = this.getRateLimitInfo();
if (remaining <= 0) {
const waitTime = resetAt - Date.now();
console.log(`Aguardando ${waitTime} ms até o reset do limite...`);
await new Promise((resolve) => setTimeout(resolve, waitTime));
}
}
// Retorna as informações do rate limit (simuladas neste caso)
getRateLimitInfo() {
// Simule as informações de rate limit, como um exemplo
// Idealmente, essas informações viriam do cabeçalho de resposta da API.
return {
remaining: 2, // Exemplo: 2 requisições restantes
resetAt: Date.now() + 1000 * 60, // Exemplo: Resetar em 1 minuto
};
}
}
// Configuração do Axios
const api = axios.create({
baseURL: 'https://api.example.com/', // URL da API
timeout: 10000, // Timeout global para as requisições
});
// Interceptor para lidar com o código de erro 429 (Rate Limit Excedido)
api.interceptors.response.use(
(response) => response, // Se a resposta for ok, só retornar
async (error) => {
if (error.response && error.response.status === 429) {
const retryAfter = error.response.headers['retry-after'];
const waitTime = retryAfter ? parseInt(retryAfter) * 1000 : 1000;
console.warn(`Rate limit excedido. Aguardando ${waitTime} ms...`);
await new Promise((resolve) => setTimeout(resolve, waitTime)); // Espera
return api(error.config); // Tenta a requisição novamente
}
return Promise.reject(error);
}
);
async function fetchData(url) {
return api.get(url)
.then((response) => response.data)
.catch((error) => console.error('Erro ao fazer a requisição:', error));
}
const limiter = new RateLimiter(5, 2000); // 5 requisições a cada 2 segundos
async function makeRequest(url) {
return limiter.enqueue(() => fetchData(url));
}
// Exemplo de uso com múltiplas requisições
const urls = ["data1", "data2", "data3"];
(async () => {
for (const url of urls) {
try {
const data = await makeRequest(url);
console.log(data);
} catch (error) {
console.error('Erro na requisição:', error);
}
}
})();
Explicação do Código:
-
Classe
RateLimiter
:- Controla o número de requisições que podem ser feitas por intervalo e gerencia a fila de requisições.
- Utiliza o método
waitIfNeeded()
para aguardar até que seja seguro fazer mais requisições, com base no limite e na velocidade de reposição de tokens.
-
Interceptor Axios:
- Se a resposta da API retornar um código de status 429 (rate limit excedido), o Axios irá esperar automaticamente o tempo especificado no cabeçalho
Retry-After
e tentará a requisição novamente.
- Se a resposta da API retornar um código de status 429 (rate limit excedido), o Axios irá esperar automaticamente o tempo especificado no cabeçalho
-
Fila de Requisições:
- Ao invés de fazer as requisições de forma imediata, elas são colocadas em uma fila e processadas uma a uma, aguardando os limites do rate limit.
-
Função
makeRequest
:- Faz a requisição usando o
RateLimiter
para garantir que os limites sejam respeitados entre as requisições.
- Faz a requisição usando o
- Axios interceptors são úteis para capturar e tratar automaticamente erros de rate limit (código 429).
- A classe RateLimiter gerencia a taxa de requisições, respeitando o rate limit e garantindo que as requisições sejam feitas no tempo adequado.
- A fila de requisições ajuda a controlar o fluxo de chamadas simultâneas e a respeitar os limites da API.
Este método proporciona um controle eficiente de requisições com Axios e garante que você não ultrapasse os limites impostos pela API.