Building a Lightweight API Rate Limiter in Node.js Without External Libraries

Tobechi DuruTobechi Duru
2 min read

Why Rate Limiting Matters

If your API is open to the public, it is vulnerable to abuse. Without any control, malicious users can overwhelm your server, causing downtime or inflated server costs. Rate limiting ensures each client only makes a certain number of requests in a given timeframe.

While most developers reach for packages like express-rate-limit, there are scenarios where you might want a lightweight, dependency-free solution. This could be for performance, security, or simply to understand what happens under the hood.


Building It From Scratch

Here’s how to implement a simple in-memory rate limiter using only built-in JavaScript objects.

javascriptCopyEdit// rateLimiter.js
const rateLimitWindow = 60000; // 1 minute in ms
const maxRequests = 5; // allowed requests per window
const clients = {}; // store client request timestamps

function rateLimiter(req, res, next) {
  const clientIP = req.ip;
  const currentTime = Date.now();

  if (!clients[clientIP]) {
    clients[clientIP] = [];
  }

  // Filter out timestamps older than the window
  clients[clientIP] = clients[clientIP].filter(
    timestamp => currentTime - timestamp < rateLimitWindow
  );

  if (clients[clientIP].length >= maxRequests) {
    return res.status(429).json({ message: "Too many requests, try again later." });
  }

  clients[clientIP].push(currentTime);
  next();
}

module.exports = rateLimiter;

Using It in Your Express App

javascriptCopyEditconst express = require("express");
const rateLimiter = require("./rateLimiter");

const app = express();
app.use(rateLimiter);

app.get("/", (req, res) => {
  res.send("Hello World!");
});

app.listen(3000, () => console.log("Server running on port 3000"));

Why This Works

  • In-memory tracking: The clients object stores timestamps for each IP, resetting naturally as old timestamps fall outside the time window.

  • No dependencies: Less overhead and reduced attack surface.

  • Customizable: Change rateLimitWindow or maxRequests to match your use case.

For production, you might store this in a distributed cache like Redis to handle multiple server instances, but this code is a great starting point for local or small-scale projects.

0
Subscribe to my newsletter

Read articles from Tobechi Duru directly inside your inbox. Subscribe to the newsletter, and don't miss out.

Written by

Tobechi Duru
Tobechi Duru

Software Engineer, MERN-Stack Developer