How To Add Rate Limiting To Your Express.js App in Node.js?

It’s a regular weekend, and you are working on a new side project, which you definitely won’t abandon after the weekend, using all the trending technologies available to man. You get an alert that a previous personal you made and managed to finish is getting a lot of new users. Before you can rejoice, you realize that it is not users, but a single user who is abusing your APIs and not only slowing it down for other users but also increasing your costs because increased API usage equates to an increase in the expenses like exceeding free tier of your database provider and 3rd party API charges like that of the OTP service and more.

What do you do? Turning off the service is not an option because of that 1 loyal user you have. What do you do? Other than panicking, you can add a rate limiting to your API to ensure abuse like this doesn’t overwhelm your server and gives you time to track and report the abuser.

In this article, we’ll use express-rate-limit as a middleware to rate-limit our simple API. The code is available here if you want to glance at it once. Otherwise, you can follow this article where I’ll be explaining the entire process line by line.

Initializing the project

The first step would be to do what we do in every NodeJS project, we init everything by running:

npm init -y

-y because I am lazy. You can and should fill out all the details.

Next up, we will install express, and the npm package of the day, express-rate-limit:

npm i express express-rate-limit

This will help you install express and express-rate-limit. Ideally, I’d be using pnpm, which is really cool, but for simplicity’s sake, let’s use npm.

Let’s start coding

Create a file called index.js and import all the necessary dependencies there:

const express = require('express');
const rateLimit = require('express-rate-limit');;Code language: JavaScript (javascript)

Since I am using codedamn playground, I’ll get the PORT number on which my express app will run on using this:

const PORT = process.env.PUBLIC_PORT;Code language: JavaScript (javascript)

If you are working on your local machine, you can use any free PORT.

Next up, I’ll write create an instance of express and start the server:

const app = express();

app.get('/', (req, res)=> {
    res.send("The name's bond. James Bond.");
});

app.listen(PORT, ()=> {
    console.log("I am up, I am up.");
});Code language: JavaScript (javascript)

As you can see, I have created a get route instead of a POST because I can run this simply on my browser and I need not spin up Postman or any other tool.

Implementing the rate limiter

Remember rateLimit which we imported from express-rate-limit? It is going to come in very handy now. Copy and paste this code just after importing the modules:

const limiter = rateLimit({
	windowMs: 1 * 60 * 1000, 
	max: 2,
    message: "Hold on their, maybe get a life instead of spamming my api.",
	standardHeaders: true,
	legacyHeaders: true, 
    skipFailedRequests: true
});

app.use(limiter);Code language: JavaScript (javascript)

Now, before we understand what the different options mean, let’s try hitting the endpoint twice.
Ideally, this is what you should get:

Screenshot of the successful response

Now we’ll hit the same endpoint again and the result changes, we get something like this:

Error thrown by the rate limiter

As evident from the two pictures, we can see that the API has thrown an error if we used it more than twice. So, now we know that the rate limiter works. But how? express-rate-limit uses web standards, HTTP headers, and user IP to keep track of requests made by a client.

Configuring the rate limiter


It’s time to configure the rate limiter for ourselves:

  1. windowMs: This specifies the window of rate limiting. This means the quota (the number of requests a client can make) is spread over this time period. It is in ms, so 1 * 60 * 1000 equates to 1 minute.
  2. max: Max is the quota which we referred to earlier. It is the maximum number of requests a user can make in a particular time period. I have set it to 2, which means a client can make 2 requests in a minute.
  3. skipFailedRequests: This ensures that only successful responses are rate limited and all unsuccessful requests are not counted in the quota allotted to a client. You can change it based on your needs.
  4. legacyHeaders: With this, you can turn the legacy HTTP headers on or off. These headers start with x and look something like x-ratelimit-limit and are used to track the total rate limit and quota left for the client.
  5. standardHeaders: These should be preferred over legacyHeaders to send the rate-limit related information. They look something like ratelimit-limit and ratelimit-remaining.

Conclusion

Other than these, there are many more options available. You can customize the HTTP status code sent in case of an error(it defaults to 429), and you can write custom logic to alter the rate limit for specific IP addresses or any custom logic. One use might be giving free clients 5 API calls per minute, whereas paid customers get 20!

While this is a good way to rate-limit your APIs, it might not be the best for certain use cases. What if there are multiple instances of the sample application running? In that case, you might have to use an external store (something like Redis). What if you are migrating from Express to Fastify? In those cases, it might be a good idea to look into different packages or create your own rate limiter!

Sharing is caring

Did you like what Shashank Kumar wrote? Thank them for their work by sharing it on social media.

0/10000

No comments so far