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 thereq.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 format123.123.123.12
.We then use the
incr()
function for incrementing the value associated with a Redis key. The key is formed by concatenating theipAddress
and anapiMessage
to differentiate the number of API calls made to a particular API. Theincr()
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 of0
, increments the value, and returns1
.We then check to see if the value returned by the
incr()
function is1
; 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 thettl()
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 thenext()
middleware function.
Let’s now move to the code.
Get hands-on with 1400+ tech skills courses.