DEV Community

Cover image for Limiting Node.js API calls with express-rate-limit
Bruno Henrique Gusmão Vasconcelos
Bruno Henrique Gusmão Vasconcelos

Posted on

Limiting Node.js API calls with express-rate-limit

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 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 express-brute and 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

Then I will install the necessary packages for this application: the express and the express-rate-limit

npm install express express-rate-limit
Enter fullscreen mode Exit fullscreen mode

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

...
  "scripts": {
    "start": "node index.js"
  },
...
Enter fullscreen mode Exit fullscreen mode

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.

// 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}`)
})
Enter fullscreen mode Exit fullscreen mode

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

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

image

Adding the rate limit

Since we already added the 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.

Now let's use it:

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

// /index.js
const express = require('express')
const rateLimit = require('express-rate-limit')
// ...
Enter fullscreen mode Exit fullscreen mode

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

// /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}`)
})
Enter fullscreen mode Exit fullscreen mode

So that will be the default response, that we had before:
image

And when the limit is exceeded it will display a default message Too many requests, please try again later.:
image

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

    const apiRequestLimiter = rateLimit({
        windowMs: 1 * 60 * 1000, // 1 minute
        max: 2, // limit each IP to 2 requests per windowMs
        message: "Your limit exceeded"
    })
    

    image

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

    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

BONUS

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

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()
    }
})
Enter fullscreen mode Exit fullscreen mode

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 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 branches.

I hope it helps you!

Top comments (5)

Collapse
 
skona27 profile image
Jakub Skoneczny

So simple with that billing for extra API calls! 😃

Collapse
 
paras594 profile image
Paras 🧙‍♂️

Nice article !
It would be nice if you can suggest an average rate limit. :)

Collapse
 
arthurdenner profile image
Arthur Denner

Great tip in the bonus section! Thank you for the article.

Collapse
 
abhidadhaniya23 profile image
Abhi Dadhaniya

Really great article... Thank you so much.. 🙏🏻

Collapse
 
nicocardo profile image
Nicocardo

Great tutorial thanks!
But I can't find a way to retrieve the message anywhere (to put it in an alert() for example) without reloading the entire page, a bit like a 'fetch'.