How to add API rate limits to your Express API

How to add API rate limits to your Express API

I had an idea of building a public API, sort of similar to the Marvel API, where you get to fetch all Marvel characters.

Screenshot at 2019-11-23 01-09-57.png

When I was brainstorming on how I would build the API, I thought about three key things:

  • It should be public and everyone should access (take note)
  • An admin account to allow me to update the API's content.

The problem with making it public as it would be quite risky especially when someone runs a bot that results in DDoS attack happens and well the API will be free and I will be paying for the server costs. Luckily there is a way to prevent these issues by setting a fixed number of API calls per IP. This means each user is tied to a limit and if they exceed, they have to wait a specified period for their rate limit to be renewed.

After tinkering around with API rate limits, I thought of writing this article, hoping it would help someone in my shoes or maybe future me. (Also it's been a while since I've written)

Introduction

We are going to create a simple express API project and enforce an API rate limit.

Let's get started

Prequisites

  • Basic knowledge of Javascript ES5
  • Basic concepts of express

Project Setup

The usual, create a new project

$ mkdir express_rate_limit_api && cd $_

Create a package.json file to track our dependencies.

$ yarn init -y # Creates a default package.json file

I prefer Yarn, I sort of feel its npm but on steroids

Next, we need to install express to have an API to work with.

$ yarn install express

To enforce an API rate limit we need a library unless you want to reinvent the wheel, go for it but this blog does not serve you. I found express-rate-limit which is much easier and better(my opinion).

yarn add express-rate-limit

Let's Code

We will first have to create a simple express server to enforce the rate limit.

const express = require("express");

const app = express();

const port = process.env.PORT || 2300;

app.get("/api/hello-world", (req, res) => {
  res.status(200).json({
    message: "Hello world! climate change is real"
  });
});

app.listen(port, err => {
  if (err) {
    console.log(`Server error due to ${err.message}`);
  }
  console.log(`Server running on port ${port}`);
});

Next, we will enforce the API rate limit

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

const app = express();

const port = process.env.PORT || 2300;

// API rate limit
const apiLimiter = rateLimit({
  windowMs: 15 * 60 * 1000, // 15 minutes,
  max: 10, // Maximum number of API calls to be made by an IP,
  message: "Limit reached, try again after a couple of minutes",
  statusCode: 429 // Status to be returned Too many requests
});

// Enforse the rate limit middleware
app.use(apiLimiter);

app.get("/api/hello-world", (req, res) => {
  res.status(200).json({
    message: "Hello world! climate change is real"
  });
});

app.listen(port, err => {
  if (err) {
    console.log(`Server error due to ${err.message}`);
  }
  console.log(`Server running on port ${port}`);
});

If you try to access the endpoint 10 times you should receive this error. Screenshot at 2019-11-23 02-07-09.png

If you want the response to be of JSON format. You will have to add a new line.

// ...
const apiLimiter = rateLimit({
  windowMs: 15 * 60 * 1000, 
  max: 10,
  message: {
    message:
      "Exceeded your allocated API calls, try again after a couple of minutes",
    api_calls_limit: 10,
    api_calls_renewal_time: "15 minutes"
  },
  statusCode: 429
});
// ...

API rate limit can also help in limiting a number of accounts created by a single IP.

Note: By default, the rate limits are stored in memory, this means if you stop your server from running, everything is gone. Not to worry, I am planning on writing the second part of this tutorial where I will outline in depth so many ways you can store the rate limits, this includes:

  • Store on a MongoDB
  • Store on MemoryCache
  • Store on Redis

Outro

Repo link: link