How to design a ⚙️ rate limiter?

By Prajwal Haniya

Techletter #88 | August 31, 2024

Before designing a rate limiter, we need to understand what a rate limiter is. Why is it required?

So what is a rate limiter?

A rate limiter is a component or tool designed to control the rate at which requests are processed or executed in a system.

rate limiter

Rate limiters function by setting the maximum number of requests that a user or client can make within a specified time frame. If this limit is exceeded, subsequent requests may be denied or delayed until the limit resets.

Client-side and server-side rate limiting are two approaches to controlling the number of requests made to an API or server, each with distinct characteristics and applications.

Client-side rate limiting is implemented within the client application. This means that the client is responsible for tracking and enforcing the rate limits on its own requests to the server. Some of the advantages are immediate feedback and reduced server load. There are cons too, security risks, and inconsistent enforcement.

Server-side rate limiting is implemented on the server, where it monitors and controls the number of requests from clients. This method is generally more robust and secure.

How to create a rate limiter? A summary

// Token Bucket Algorithm

class TokenBucket {
  constructor(capacity) {
    this.capacity = capacity;
    this.tokens = capacity;
    this.lastRefill = Date.now();

    setInterval(() => {
        // the interval at which the bucket will be filled can be dynamic
        this.refill();
    }, 10 * 1000);
  }

  consume(tokens = 1) {
    if (this.tokens >= tokens) {
        this.tokens -= tokens;
        console.log('Consumed', tokens);
        console.log('Remaining tokens:', this.tokens);
        return true;
    } else {
        console.log('No Tokens available to consume. Please try again later');
        return false;
    }
  }

  refill() {
    const now = Date.now();
    console.log('Refilling tokens');
    const timePassed = now - this.lastRefill;
    const tokensToAdd = this.capacity - this.tokens;
    console.log(tokensToAdd);
    this.tokens = Math.min(this.capacity, this.tokens + tokensToAdd);
    this.lastRefill = now;
    console.log('Total Available tokens after refill: ', this.tokens)
  }
}

const bucket = new TokenBucket(20, 10);
setInterval(() => {
    bucket.consume(5);
}, 3000);