Implement Rate Limiting Middleware Function

Learn to implement an API rate-limiting middleware function using Redis.

Introduction

In today's interconnected world, APIs play a crucial role in enabling communication and integration between different systems. However, ensuring fair and efficient usage of APIs is essential to maintain system stability, prevent abuse, and protect valuable resources. This is where API rate limiting comes into play. By leveraging Redis, a fast in-memory data store, and the power of Node.js, we can effectively control and regulate the rate at which clients can make requests to our API.

How Redis will be integrated within our project

For example, in our Twitter project, we have three APIs and assume one of the APIs accepts at most five requests within 10 seconds from an IP address. If more requests occur within the 10-second window, the API will return an error message. The limit gets reset once the window is over.

We’ll use Redis to store the IP address along with the API endpoint as the key and the number of requests made from the IP address to the API endpoint as the value. For example, if an IP address of 123.123.123.12 makes a request to the API endpoint https://your-domain-name.com/generate-tweet for the first time, we’ll store 123.123.123.12/generate-tweet as the key and 1 as the value. 

Since our rate-limiting function can be used by multiple APIs in our app, we’ll create it as middleware. We’ll keep our middleware function to be configurable in terms of allowed API hits and the windows in seconds for different APIs. So, we’ll create a function named rateLimiter() that accepts three parameters: the time in seconds denoting our window, the number of allowed hits from an IP address in that window, and a simple message to differentiate all the different API endpoint calls.

Now, to make this function a middleware, we need to accept three parameters:

  • The request object

  • The response object

  • The next() function

Therefore, we’ll return a function from our rateLimiter() function. There’s an important concept in JavaScript named closures. Closures are a powerful concept. They allow functions to retain access to variables from their parent scope even after the parent function has finished executing. A closure is created when an inner function references variables from its outer function, forming a “closure” around those variables. In our case, the inner function creates a closure with our rateLimiter() function, which means that the inner function will have access to all the three parameters that are passed to the rateLimiter() function.

Implement the rate limiter function

We’ll follow the steps mentioned below to implement our rate limiter middleware function:

  • We get the IP address of the client making the API request. We check whether the request headers contain the x-forwarded-for field, which could contain the IP address if the request went through a proxy. Otherwise, we fall back on using the remote IP address of the client using the req.connection object.

  • It’s possible that the IP address has an IPv4-mapped IPv6 address format, indicated by the ::ffff: prefix before the actual IP. If this prefix exists, we need to remove it to extract the actual IPv4 address, which is represented in the format 123.123.123.12.

  • We then use the incr() function for incrementing the value associated with a Redis key. The key is formed by concatenating the ipAddress and an apiMessage to differentiate the number of API calls made to a particular API. The incr() method increases the value by one and returns the new value. If the key doesn’t exist, it creates the key-value pair with a value of 0, increments the value, and returns 1.

  • We then check to see if the value returned by the incr() function is 1; if so, it means this is the first request made to an API by an IP address. So, we set the expiration time for the Redis key.

  • If the value isn’t 1, we use the ttl() function that returns the time left for the key to expire in Redis.

  • Finally, we check if the value returned by the incr() function exceeds the maximum number of allowed API calls per IP address. If it does, we return a failure response. Otherwise, we call the next() middleware function.

Let’s now move to the code.

Get hands-on with 1200+ tech skills courses.