Rate-Limiter Middleware Function

Implement the middleware function to limit the API calls made from an IP address in a defined window.

We'll cover the following

We’ve learned about middleware functions and some of their use cases. Since our rate-limiting function can be used by multiple APIs in our app, we’ll create it as middleware. As discussed earlier, 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, and the next() function. Theref, 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 return a success response.

Let’s now move to the code.

Get hands-on with 1200+ tech skills courses.