Tech

How to Rate Limit Express Applications



Rate limiting is a strategy you can use to control traffic on a network. It limits the number of requests that a user can make within a specific time frame.

Various rate limiting algorithms exist, each with its own trade-offs. One simple and popular method is to track requests’ IP addresses and check how much time elapses between requests. The system can then deny a request if its IP address exceeds the number of requests the limit allows.

This approach to rate limiting is easy to build in a NodeJS-Express app, with just a few steps.

Step 1: Setting Up a Development Environment

First, you’ll need to create and initialize an Express application.

Start by creating a project directory by running:

mkdir express-app

Then enter that directory by running:

cd express-app

Next, initialize npm, the node package manager, and create a package.json file in your application by running:

npm init -y

The -y flag will create your package.json file with all the default settings.

Next, you will need to install some dependencies. The dependencies required for this tutorial are:

  • ExpressJS: ExpressJS is a NodeJS framework that provides a robust set of features for web and mobile applications. It simplifies the process of building backend applications with NodeJS.
  • Express Rate Limit: Express rate limit is a rate-limiting middleware for ExpressJS. It limits repeated requests to public APIs and/or endpoints, such as password resets, user logins, etc.

Install the required dependencies by running:

npm install express express-rate-limit

Step 2: Creating an Express Application

You’ll need to create a basic Express server that listens to requests being made to your application.

First, create an index.js file in your project’s root directory. This will be the entry file for your application.

Next, add the following code to your index.js file:


const express = require("express");
const app = express();
const port = process.env.PORT || 3000

app.listen(port, () => {
console.log(`App running on port ${port}`);
});

This code imports express and creates an Express application by calling express() and storing its return value in the app variable. It then listens for traffic on port 3000 by calling the listen method on the app object.

Step 3: Creating Route Handlers

Next, create some route handlers you can implement the rate-limiting solution on.

First, create a folder, routes, in your project’s root directory by running:

mkdir routes

Create a file, routes.js, inside your routes folder and add the following code:

const express = require("express");
const router = express.Router();

router.get("/", (req, res) => {
res.send({ message: "Hello, this is a GET request" });
});

router.post("/add-demo", (req, res) => {
res.status(201).send({ message: "Resource created successfully" });
});

router.put("/update-demo", (req, res) => {
res.status(201).send({ message: "Resource updated sucessfully" });
});

module.exports = router;

This code imports express, calls the Router method on express, and stores the value in a variable, router. The Router method lets you create modular, mountable route handlers. You can create route handlers for a GET request to “/”, a POST request to “/add-demo”, and a PUT request to “/update-demo”. Finally, export the router variable.

Next, import the router variable in your index.js file:


const routes = require("./routes/routes");

Then, use it as a middleware in your index.js file:


app.use(routes);

Be sure to place the code block above before the app.listen call.

Step 4: Implementing Rate Limiting

First, create a “middleware” folder in your project’s root directory by running:

mkdir middleware

Then create a file called “rate-limiter.js” inside the middleware directory. Add the following code to this file:


const rateLimiter = require("express-rate-limit");

const limiter = rateLimiter({
max: 5,
windowMS: 10000,
message: "You can't make any more requests at the moment. Try again later",
});

module.exports = limiter

The rateLimiter function takes a configuration object with the conditions to limit the number of requests.

The properties in the configuration object above are:

  • max: This property must always be a number or a function that returns a number. It represents the max number of requests a user can make within a specified timeframe. If this property is not set in the configuration object, it defaults to 5.
  • windowsMS: This property should always be a number. It represents the timeframe in which several requests are allowed in milliseconds. If this property is not set in the configuration object, it defaults to 60000 milliseconds (one minute).
  • message: This property can be a string, a JSON object, or any other value supported by Express’s response.send method. If this property is not set in the configuration object, it defaults to “Too many requests. Please try again later.”


The function will then check for repeated requests to your application. If a user exceeds the limit (max, 5) within the time frame (windowMS, 10s), it will block the request. It will also throw a “Too Many Requests” error with a status code of 429.

Finally, import the limiter function in your index.js file and apply it as a global middleware in your application. Do this by placing app.use(limiter) above the routes middleware. This applies the rate-limiting solution to all of your application’s routes.

app.use(limiter);

Rate Limiting Specific Routes

You can also apply rate limiting to specific routes. You can configure them separately to reject requests made in a different timeframe, display a different message, etc.

For example, assume you’re implementing a user sign-in route in your application. You might need to add a rate-limiting configuration for the sign-in route that differs from the configuration used by the other routes.

First, you’ll have to remove limiter as an application-level middleware and apply it because there is no built-in middleware filter system in ExpressJS. So even if you add a specific rate-limiting solution to a route, the global middleware would still run on that route.

Next, create a new rate-limiting configuration in your rate-limiter.js file and export it.

const signInLimiter = rateLimiter({
max: 3,
windowMS: 10000,
message: "Too many sign-in attempts. Try again later."
})

module.exports = {
limiter,
signInLimiter
}

The signInLimiter configuration object has a different number of max requests and a different error message from the general rate-limiter.

Finally, update your router.js file with the code block below:


const express = require("express");
const router = express.Router();
const {limiter, signInLimiter} = require("../middleware/rate-limiter")

router.get("/sign-in", signInLimiter, (req, res, next) => {
res.send({ message: "Hello, this is a GET request" });
});

router.use(limiter)

router.post("/post", (req, res) => {
res.status(201).send({ message: "Resource created successfully" });
});

router.put("/put", (req, res) => {
res.status(201).send({ message: "Resource updated sucessfully" });
});

module.exports = router;

In the code block above, you imported limiter and signInLimiter. Then you applied signInLimiter as a specific rate-limiter to the “/sign-in” route.

Finally, by placing router.use(limiter) above the rest of the routes, you applied limiter as the rate-limiter for the rest of the routes.

The Importance of Rate Limiting

Rate limiting reduces the strain on your web server by avoiding having to process too many requests at once. It lowers bot activity, protects you from Denial of Service (DoS) attacks, and prevents brute-force attacks.

Related Articles

Leave a Reply

Your email address will not be published. Required fields are marked *

Back to top button