JAVASCRIPT

Simple Rate Limiter for Outgoing API Requests

Implement a basic rate limiter in Node.js to control the frequency of outgoing API calls, preventing your application from exceeding external service rate limits.

// A simple in-memory queue and rate limiter
class ApiRateLimiter {
  constructor(requestsPerInterval, intervalMs) {
    this.requestsPerInterval = requestsPerInterval;
    this.intervalMs = intervalMs;
    this.queue = [];
    this.timestamps = []; // Stores timestamps of recent requests
    this.isProcessing = false;

    console.log(`Rate Limiter initialized: ${requestsPerInterval} requests/${intervalMs / 1000}s`);
  }

  // Add a request function to the queue
  addRequest(requestFn) {
    return new Promise((resolve, reject) => {
      this.queue.push({ requestFn, resolve, reject });
      this.processQueue();
    });
  }

  async processQueue() {
    if (this.isProcessing) {
      return;
    }
    this.isProcessing = true;

    while (this.queue.length > 0) {
      // Clean up old timestamps
      const now = Date.now();
      this.timestamps = this.timestamps.filter(ts => ts > now - this.intervalMs);

      if (this.timestamps.length < this.requestsPerInterval) {
        const { requestFn, resolve, reject } = this.queue.shift(); // Get next request
        this.timestamps.push(now); // Mark this request as sent

        try {
          const result = await requestFn(); // Execute the actual API call
          resolve(result);
        } catch (error) {
          reject(error);
        }
      } else {
        // Rate limit hit, wait until next slot is available
        const timeToWait = this.intervalMs - (now - this.timestamps[0]);
        console.log(`Rate limit hit. Waiting for ${timeToWait}ms...`);
        await new Promise(res => setTimeout(res, timeToWait + 50)); // Add a small buffer
      }
    }
    this.isProcessing = false;
  }
}

// Example Usage:
const myRateLimiter = new ApiRateLimiter(5, 1000); // 5 requests per second

async function makeApiCall(id) {
  return myRateLimiter.addRequest(async () => {
    console.log(`[${new Date().toLocaleTimeString()}] Making API call ${id}...`);
    // Simulate an API call
    return new Promise(resolve => setTimeout(() => {
      console.log(`[${new Date().toLocaleTimeString()}] API call ${id} complete.`);
      resolve(`Response for call ${id}`);
    }, Math.random() * 500)); // Simulate variable API response time
  });
}

// Make some requests quickly to test the limiter
// for (let i = 1; i <= 15; i++) {
//   makeApiCall(i).then(result => console.log(result)).catch(console.error);
// }

// After a delay, make more requests
// setTimeout(() => {
//   for (let i = 16; i <= 20; i++) {
//     makeApiCall(i).then(result => console.log(result)).catch(console.error);
//   }
// }, 3000);
How it works: This Node.js snippet provides a basic in-memory rate limiter that controls the frequency of outgoing API requests. It maintains a queue of pending requests and a list of timestamps for recently made requests. Before executing a new request, it checks if the configured `requestsPerInterval` limit has been reached within the `intervalMs`. If the limit is hit, it pauses execution for the calculated duration until a slot becomes available, preventing the application from overwhelming external APIs and hitting their rate limits.

Need help integrating this into your project?

Our team of expert developers can help you build your custom application from scratch.

Hire DigitalCodeLabs