Limiting Node.js API calls with express-rate-limit
Tags: node, javascript, webdev, api
Published: 2021-07-06 08:49:43

For many reasons you may need to limit your API calls: It can be to avoid extra cost in cloud, to create a free tier to use your API, to fulfill a third-party API requirement, to prevent performance issues... I believe if you are reading this post this purpose is very clear in your mind and you are just trying to figure out how to make it work. So let's focus on this.

The [express-rate-limit](https://www.npmjs.com/package/express-rate-limit) is a simple and straight forward library that solves this problem for us. It's not the most complete one, but is a lightweight and fast way to achieve this goal. For most refined options the express-rate-limit itself already recommends other libraries such as [rate-limiter-flexible](https://www.npmjs.com/package/rate-limiter-flexible) [express-brute](https://www.npmjs.com/package/express-brute) and [express-limiter](https://www.npmjs.com/package/express-limiter).

## Now starting

### Installing packages
 
First of all you need a **node.js** project. So I created a directory and executed the command `npm init -y` to create a default **package.json** file.

![image](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/lujwvfwjd02zhg81cbfk.png)

Then I will install the necessary packages for this application: the [express](https://www.npmjs.com/package/express) and the [express-rate-limit](https://www.npmjs.com/package/express-rate-limit)

```sh
npm install express express-rate-limit
```

### Setup express

To make it easier to execute I will update my **package.json** file and add a start script. It will let us to execute the project with the `npm start` command

```json
...
  "scripts": {
    "start": "node index.js"
  },
...
```

Then I will create an **index.js** file in the root directory to be our entry point.
So I'll place the most generic **express** start code.

```javascript
// express import
const express = require('express')
// express initialization
const app = express()

const PORT = 3000
 
// generic GET route that we will use for the tests
app.get('/', function (req, res) {
  return res.send('Hello World')
})

// server initialization 
app.listen(PORT, () => {
    console.log(`server started on port ${PORT}`)
})
```

So when we run the `npm start` (or `node index.js` if you jumped the package.json step) it should display that message indicating that the application is working:

![image](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/8p553iq9ax9e9plg4dho.png)

And when accessing the `localhost:3000` in the "/" route it will display the `Hello World` that we configured.

![image](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/6leat963w1u6pby8xnop.png)

### Adding the rate limit

Since we already added the [express-rate-limit](https://www.npmjs.com/package/express-rate-limit) in the first step we just have to start using it.

The **express-rate-limit** work as a middleware, it means that we can use it in a single route, the entire app or a group of sub-routes.

For that we just need to understand how the **express** handles middlewares, but being very straight to the point we will use the `use` function from express to add a middleware or request handler in the root of our api to wrap it entirely.

If you want to understand more about the middleware usage by express you can check the [express documentation](https://expressjs.com/en/guide/using-middleware.html).

Now let's use it:

First we need to import the express-rate-limit in our code just under the express import

```javascript
// /index.js
const express = require('express')
const rateLimit = require('express-rate-limit')
// ...
```

Then we can configure the time box (windowMs) in milliseconds and the maximum number of requests per IP address (max)

```javascript
// /index.js
const express = require('express')
const rateLimit = require('express-rate-limit')
const app = express()

const PORT = 3000

// Create the rate limit rule
const apiRequestLimiter = rateLimit({
    windowMs: 1 * 60 * 1000, // 1 minute
    max: 2 // limit each IP to 2 requests per windowMs
})

// Use the limit rule as an application middleware
app.use(apiRequestLimiter)

app.get('/', function (req, res) {
  return res.send('Hello World')
})

app.listen(PORT, () => {
    console.log(`server started on port ${PORT}`)
})
``` 

So that will be the default response, that we had before:
![image](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ywdyv8b1lnk6nczf5nx9.png)

And when the limit is exceeded it will display a default message `Too many requests, please try again later.`:
![image](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/27bmhdtvuehui3fe44l2.png) 

**And it's already working!**

To change this message we have two options:

1. Add a "message" property inside the object passed as param for the `rateLimit` function
    ```javascript
    const apiRequestLimiter = rateLimit({
        windowMs: 1 * 60 * 1000, // 1 minute
        max: 2, // limit each IP to 2 requests per windowMs
        message: "Your limit exceeded"
    })
    ```
  
    ![image](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/zfuzwmx6nafe4h3qa37u.png) 

2. Add a handler function to process the failure case:

    ```javascript
    const apiRequestLimiter = rateLimit({
        windowMs: 1 * 60 * 1000, // 1 minute
        max: 2, // limit each IP to 2 requests per windowMs
        handler: function (req, res, /*next*/) {
            return res.status(429).json({
              error: 'You sent too many requests. Please wait a while then try again'
            })
        }
    })
    ```
    
    ![image](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/t90pko8hwsgzonvwhumb.png)

### BONUS

You can also use a function handler to start billing the consumer for the exceeded requests

```javascript
const apiRequestLimiter = rateLimit({
    windowMs: 1 * 60 * 1000, // 1 minute
    max: 2, // limit each IP to 2 requests per windowMs
    handler: function (req, res, next) {
      applyFeesForConsumer()
      next()
    }
})
``` 

That way you will not block the request, just apply fees before continue.

And that's it!

## Thank you

Thank you for reading this.

There are more available features and configurations in the [express-rate-limit documentation](https://www.npmjs.com/package/express-rate-limit) it was just a simple example how we can use this library to control the API requests.

The examples and step-by-step are available in [this repository](https://github.com/brunohgv/nodejs-rate-limit-example) branches.

I hope it helps you!