Skip to content

abdelrahmen1313/express-rate-limiting

Repository files navigation

Express Rate Limiter

A production-ready, type-safe rate limiter middleware for Express.js applications built with TypeScript.

Features

  • Type - Safe

-Full TypeScript support with comprehensive types

  • Lightweight

    -Minimal dependencies, only requires Express

  • Flexible

    -Configurable time windows and request limits

  • Memory Efficient

    -Automatic cleanup of expired entries

  • Proxy Support

    -Handles X-Forwarded-For headers and custom IP extraction

  • Standards Compliant

    -Returns standard rate limit headers(X-RateLimit-*)

  • Per - Route Control

    -Apply to specific routes or globally

  • Observable

    -Access rate limit status in downstream handlers

Installation

npm install @edah/express-rate-limiting

Quick Start

import express from 'express';
import { createRateLimiterMiddleware } from '@edah/express-rate-limiting';

const app = express();

const rateLimiter = createRateLimiterMiddleware({
  maxRequests: 100,
  windowInMinutes: 15,
});

// Apply globally
app.use(rateLimiter);

// Or apply to specific routes
app.use('/api', rateLimiter);

app.listen(3000);

Configuration

Basic Options

  maxRequests: number;             # Max requests per window (required)
  windowInMinutes?: number;        # Time window in minutes (default: 1)
  enableCleanup?: boolean;         # Enable automatic cleanup (default: true)
  cleanupIntervalMinutes?: number  # Cleanup interval in minutes (default: 5)
  showInformativeHeaders?: boolean #  Control if the api should send X-RATE-LIMIT HEADERS (default : true)
  maxClients?: number              # Maximum clients allowed per route  (default : 9999) 

Examples

Basic Usage

const rateLimiter = createRateLimiterMiddleware({
  maxRequests: 100,
  windowInMinutes: 15,
});

app.use(rateLimiter);

Advanced Options

const rateLimiter = createRateLimiterMiddleware({
    maxRequests: 50, windowInMinutes: 5, errorMessage: "API rate limit exceeded. Please wait before making more requests.",

    // Custom IP extractor for cloud environments
    getClientIp: (req : Request) => {
        return ((req.headers["cf-connecting-ip"]as string) || // Cloudflare
                (req.headers["x-forwarded-for"]as string)
            ?.split(",")[0]
                ?.trim() || req.socket
                    ?.remoteAddress || req.ip)
    },

    // Skip rate limiting for health checks and internal endpoints
    skip: (req : Request) => {
        return req.path === "/health" || req.path === "/metrics"
    },

    // Cleanup expired records every 10 minutes -- default is 5
    cleanupIntervalMinutes: 10
})

Per-Route Rate Limits

const apiLimiter = createRateLimiterMiddleware({maxRequests: 50, windowInMinutes: 5});

const authLimiter = createRateLimiterMiddleware({maxRequests: 5, windowInMinutes: 15});

const pastaServiceLimiter = createRateLimiterMiddleware({maxRequests: 5, windowInMinutes: 10, maxClients : 100});

app.use('/api', apiLimiter);
app.post('/auth/login', authLimiter, (req, res) => {
    // Handle login
});

Response Behavior

Allowed Request

Response includes rate limit headers:

HTTP / 1.1 200 OK
X - RateLimit - Limit : 100
X - RateLimit - Remaining : 99
X - RateLimit - Reset : 1702123456

Request object includes rate limit info:

req.rateLimit = {
    remaining: 99,
    resetAt: 1702123456000
};

req.rateLimitInfos = {
    
    "clientsCount": 1
}

Rate Limited Request

HTTP / 1.1 429 Too Many Requests
X - RateLimit - Limit : 100
X - RateLimit - Remaining : 0
X - RateLimit - Reset : 1702123456

{
"error" : "Too many requests, please try again later",
"retryAfter" : 300
}

How It Works

  1. Request arrives - Middleware extracts client IP
  2. Check storage - Looks up client's current snapshot
  3. Validate window - If window expired, creates new snapshot; otherwise increments hits
  4. Check limit - Compares hits against maxRequests
  5. Response - Returns 429 if limited, otherwise adds headers and continues

Memory Management

The middleware automatically cleans up expired entries:

  • Cleanup runs every 5 minutes by default (configurable)
  • Only expired snapshots are removed
  • Can be disabled with enableCleanup : false if managing memory differently

Monitoring

Get active clients number :

app
.get("/", (req, res) => {

res.json({message: "Hello!", rateLimit: req.rateLimit, infos: req.rateLimitInfos})
})

Performance Considerations

  • O(1) lookups - Client snapshots use Map for constant-time access
  • Minimal overhead - Lightweight class instances, no external I/O
  • Automatic cleanup - Prevents unbounded memory growth
  • No external dependencies - Only requires Express
  • Overrideable Methods - You can use the skip() decorator to skip rate limiting internal routes
  • Monitor active clients for spikes or anomalies

Contributing

Contributions are welcome! If you'd like to improve this utility, fix bugs (if found), or suggest a new feature :

  1. Fork the repository
  2. Create a feature branch (git checkout -b feature/my-feature-name)
  3. Commit your changes (git commit -m "feat: add my feature")
  4. Push to your branch (git push origin feature/my-feature-name)
  5. Open a pull request

Please follow Conventional Commits

Feedback and suggestions help shape the roadmap.

License

MIT

About

Rate limiter middleware for Express.js

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published

Contributors 2

  •  
  •