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:

  1. Usar interceptores do Axios para tratar as respostas.
  2. Aguardar quando atingir o limite de requisições (com base no cabeçalho Retry-After ou status 429).
  3. 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:

  1. 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.
  2. 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.
  3. 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.
  4. Função makeRequest:

    • Faz a requisição usando o RateLimiter para garantir que os limites sejam respeitados entre as requisições.

  • 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.