npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2025 – Pkg Stats / Ryan Hefner

omni-rate-limiter

v1.0.1

Published

A comprehensive, high-performance rate limiting package for Node.js applications with multiple algorithms and storage backends

Readme

Omni Rate Limiter

The rate limiter that doesn't get in your way. Powerful enough for production, simple enough to set up in 60 seconds.

npm version License: MIT Node.js Version

Stop API abuse without the headache. Works with Express, Fastify, Next.js, and any Node.js app. Choose from 6 battle-tested algorithms. Store data in memory, Redis, PostgreSQL, or MongoDB. TypeScript ready. Zero config to get started.

Why Developers Love It

  • 🚀 3 lines to production - That's all it takes.
  • 🎯 Smart defaults - Works great out of the box, customize when you need to.
  • 🔥 Actually fast - Built for high-traffic apps.
  • 🛠️ Use what you have - Memory, Redis, Postgres, Mongo - your choice.
  • 📦 Batteries included - Express, Fastify, Next.js middleware ready to go.

Installation

npm install omni-rate-limiter

Quick Start

Here's how to add rate limiting to your app:

const { createRateLimiter } = require('omni-rate-limiter');

// Create a rate limiter: 100 requests per hour
const limiter = createRateLimiter({
  limit: 100,
  window: '1h',
  algorithm: 'sliding-window-counter'  // Optional - this is the default
});

// Use it in your code
async function handleRequest(userId) {
  const result = await limiter.check(userId);
  
  if (result.allowed) {
    // Request is allowed - process it
    console.log(`✓ Request allowed. ${result.remaining} remaining.`);
  } else {
    // Too many requests - reject it
    console.log(`✗ Rate limit hit. Try again in ${result.retryAfter}ms`);
  }
}

Note: The order of options doesn't matter - they're just object properties. The algorithm is optional and defaults to sliding-window-counter.

Using with Express.js

Add rate limiting to your Express app as middleware:

const express = require('express');
const { expressRateLimit } = require('omni-rate-limiter');

const app = express();

// Example 1: Rate limit by IP address (most common)
app.use(expressRateLimit({
  limit: 100,
  window: '15m',
  keyGenerator: (req) => req.ip,  // Each IP gets 100 requests per 15 min
  message: 'Too many requests, please slow down'
}));

// Example 2: Rate limit by API key (with IP fallback)
app.use('/api/', expressRateLimit({
  limit: 1000,
  window: '1h',
  keyGenerator: (req) => req.headers['x-api-key'] || req.ip
}));

// Example 3: Rate limit by user ID (for authenticated routes)
app.use('/dashboard/', expressRateLimit({
  limit: 500,
  window: '1h',
  keyGenerator: (req) => req.user?.id || req.ip  // User ID if logged in, else IP
}));

app.listen(3000);

Understanding keyGenerator

The keyGenerator function tells the rate limiter who to track. Think of it as "what makes users unique?"

Common patterns:

// By IP address - each IP address gets their own limit
keyGenerator: (req) => req.ip

// By user ID - each logged-in user gets their own limit
keyGenerator: (req) => req.user.id

// By API key - each API key gets their own limit
keyGenerator: (req) => req.headers['x-api-key']

// Combination - use API key if present, otherwise IP
keyGenerator: (req) => req.headers['x-api-key'] || req.ip

// Global - everyone shares the same limit (rarely used)
keyGenerator: (req) => 'global'

Default behavior: If you don't specify keyGenerator, it defaults to (req) => req.ip || 'global'

Next.js Integration

// middleware.js
import { nextjsRateLimit } from 'omni-rate-limiter';

const rateLimiter = nextjsRateLimit({
  limit: 10,
  window: '1m',
  keyGenerator: (request) => request.ip
});

export async function middleware(request) {
  const rateLimitResult = await rateLimiter(request);
  
  if (rateLimitResult) {
    return rateLimitResult; // Rate limit exceeded
  }
  
  // Continue to the next middleware or page
}

export const config = {
  matcher: '/api/:path*'
};

Fastify Integration

const fastify = require('fastify')({ logger: true });
const { fastifyRateLimit } = require('omni-rate-limiter');

// Register the rate limiting plugin
fastify.register(fastifyRateLimit, {
  limit: 100,
  window: '1h',
  keyGenerator: (request) => request.ip
});

fastify.listen({ port: 3000 });

Choose Your Algorithm

Different algorithms work better for different situations. Here's what each one does:

Token Bucket (Good for APIs with bursts)

Allows sudden bursts of traffic, then refills slowly. Like a bucket that fills with tokens over time.

const limiter = createRateLimiter({
  algorithm: 'token-bucket',
  limit: 10,
  window: '1m'
});

Use when: Your API can handle occasional bursts but needs to limit sustained traffic.

Sliding Window (Most accurate)

Tracks every request timestamp precisely. No edge cases or loopholes.

const limiter = createRateLimiter({
  algorithm: 'sliding-window',
  limit: 100,
  window: '1h'
});

Use when: You need exact rate limiting and have the memory for it.

Fixed Window (Fastest and simplest)

Resets the counter at fixed intervals. Very efficient but has edge cases.

const limiter = createRateLimiter({
  algorithm: 'fixed-window',
  limit: 1000,
  window: '1h'
});

Use when: Performance is critical and you can tolerate some edge cases.

Sliding Window Counter (Recommended for most cases)

Best balance between accuracy and performance. This is the default.

const limiter = createRateLimiter({
  algorithm: 'sliding-window-counter',
  limit: 100,
  window: '1h'
});

Use when: You want good accuracy without the memory cost of full sliding window.

Leaky Bucket (Smooth traffic)

Processes requests at a steady rate, smoothing out bursts.

const limiter = createRateLimiter({
  algorithm: 'leaky-bucket',
  limit: 10,
  window: '1m'
});

Use when: You want to enforce a steady request rate.

Adaptive (Smart adjustment)

Automatically adjusts limits based on your server's performance.

const limiter = createRateLimiter({
  algorithm: 'adaptive',
  limit: 100,
  window: '1m'
});

Use when: Your server load varies and you want automatic adjustment.

Not sure which to pick? Use sliding-window-counter (the default) - it works great for most apps.

Where to Store Rate Limit Data

By default, rate limits are stored in memory (RAM). This works great for single servers, but you can also use Redis, PostgreSQL, or MongoDB.

Memory (Default - No Setup Required)

Just use the rate limiter - it stores everything in RAM automatically.

const limiter = createRateLimiter({
  limit: 100,
  window: '1h'
  // That's it! Uses memory automatically
});

Good for: Development, testing, single server apps
Not good for: Multiple servers (data isn't shared)

Redis (Best for Production)

Fast and perfect when you have multiple servers.

Option 1: Individual parameters

const { createRateLimiter, createRedisStorage } = require('omni-rate-limiter');

const storage = createRedisStorage({
  host: 'localhost',
  port: 6379,
  password: 'yourpassword',  // Optional: if Redis has auth
  db: 0,                      // Optional: Redis database number (default: 0)
  keyPrefix: 'rl:'            // Optional: prefix for all keys (default: 'rl:')
});

const limiter = createRateLimiter({
  limit: 100,
  window: '1h',
  storage: storage
});

Option 2: Connection string (easier!)

const storage = createRedisStorage({
  url: 'redis://username:password@localhost:6379/0',
  keyPrefix: 'rl:'  // Optional
});

Connection string format: redis://[username]:[password]@[host]:[port]/[database]

Redis keys created: rl:your-key-here (e.g., rl:192.168.1.1 for IP-based limiting)
Good for: Production apps, multiple servers, high traffic

PostgreSQL

Use your existing PostgreSQL database. Tables are created automatically!

Option 1: Individual parameters

const { createRateLimiter, createPostgreSQLStorage } = require('omni-rate-limiter');

const storage = createPostgreSQLStorage({
  host: 'localhost',
  port: 5432,
  database: 'myapp',
  user: 'postgres',
  password: 'mypassword',
  tableName: 'rate_limits'    // Optional: table name (default: 'rate_limits')
});

const limiter = createRateLimiter({
  limit: 100,
  window: '1h',
  storage: storage
});

Option 2: Connection string (easier!)

const storage = createPostgreSQLStorage({
  connectionString: 'postgresql://postgres:mypassword@localhost:5432/myapp',
  tableName: 'rate_limits'  // Optional
});

Connection string format: postgresql://[user]:[password]@[host]:[port]/[database]

Table created: rate_limits with columns: key, value, expires_at, created_at, updated_at
Indexes: Automatically created on key and expires_at for performance
Good for: Apps already using PostgreSQL

MongoDB

Works great with MongoDB's automatic TTL cleanup.

Simple connection string (recommended)

const { createRateLimiter, createMongoDBStorage } = require('omni-rate-limiter');

const storage = createMongoDBStorage({
  url: 'mongodb://localhost:27017',
  database: 'myapp',              // Optional: database name (default: 'rate_limiter')
  collection: 'rate_limits'       // Optional: collection name (default: 'rate_limits')
});

const limiter = createRateLimiter({
  limit: 100,
  window: '1h',
  storage: storage
});

With authentication:

const storage = createMongoDBStorage({
  url: 'mongodb://username:password@localhost:27017',
  database: 'myapp'
});

MongoDB Atlas (cloud):

const storage = createMongoDBStorage({
  url: 'mongodb+srv://username:[email protected]',
  database: 'myapp'
});

Connection string format: mongodb://[username]:[password]@[host]:[port] or mongodb+srv://[username]:[password]@[cluster]

Collection created: rate_limits with fields: _id (key), value, expiresAt, updatedAt
TTL Index: Automatically created on expiresAt - MongoDB cleans up expired data for you!
Good for: Apps already using MongoDB

Advanced Options

Rate Limit by User Instead of IP

const limiter = createRateLimiter({
  limit: 100,
  window: '1h',
  keyGenerator: (req) => {
    // Use user ID if logged in, otherwise use IP
    return req.user?.id || req.ip;
  }
});

Don't Count Failed Requests

const limiter = createRateLimiter({
  limit: 100,
  window: '1h',
  skipFailedRequests: true  // Only count successful requests
});

Custom Error Messages

const middleware = expressRateLimit({
  limit: 100,
  window: '1h',
  message: 'Whoa there! You are making too many requests. Take a break.',
  onLimitReached: (key, result) => {
    console.log(`User ${key} hit the rate limit`);
  }
});

Metrics and Monitoring

const { createRateLimiter, createMetricsCollector } = require('omni-rate-limiter');

const metrics = createMetricsCollector();
const limiter = createRateLimiter({
  limit: 100,
  window: '1h',
  onLimitReached: (key, result) => {
    metrics.recordRequest(key, false, Date.now() - result.resetTime);
  }
});

// Get metrics
setInterval(() => {
  const stats = metrics.getMetrics();
  console.log('Rate limiter stats:', stats);
}, 60000);

Error Handling

const { createRateLimiter, createRateLimiterError } = require('omni-rate-limiter');

try {
  const result = await limiter.check('user123');
  // Handle result
} catch (error) {
  if (error.name === 'RateLimiterError') {
    switch (error.code) {
      case 'STORAGE_ERROR':
        console.error('Storage backend error:', error.originalError);
        break;
      case 'CONFIGURATION_ERROR':
        console.error('Configuration error:', error.message);
        break;
      default:
        console.error('Rate limiter error:', error.message);
    }
  }
}

Performance Tips

Algorithm choice matters:

  • Most apps: Use sliding-window-counter (default)
  • Need speed: Use fixed-window
  • Need precision: Use sliding-window

Storage matters too:

  • Single server: Use memory (default)
  • Multiple servers: Use Redis
  • Already have a database: Use PostgreSQL or MongoDB

Keep keys simple:

  • Short keys = better performance
  • Don't create millions of unique keys

Set appropriate limits:

  • Too low = frustrated users
  • Too high = no protection
  • Start conservative, adjust based on monitoring

API Reference

Main Function

createRateLimiter(options)

Creates a rate limiter.

Options:

  • algorithm - Which algorithm to use (default: 'sliding-window-counter')
  • limit - How many requests allowed (default: 100)
  • window - Time period (default: '1h'). Examples: '1m', '30s', '2h'
  • storage - Where to store data (default: memory)
  • keyGenerator - Function to generate keys from requests
  • skipFailedRequests - Don't count failed requests (default: false)
  • onLimitReached - Callback when limit is hit

Returns an object with:

  • check(key) - Check if request is allowed
  • reset(key) - Reset the limit for a key
  • getInfo(key) - Get current status

Storage Functions

  • createRedisStorage(options) - Use Redis
  • createPostgreSQLStorage(options) - Use PostgreSQL
  • createMongoDBStorage(options) - Use MongoDB
  • createCustomStorage(implementation) - Use your own storage

Middleware

  • expressRateLimit(options) - Express middleware
  • fastifyRateLimit(options) - Fastify plugin
  • nextjsRateLimit(options) - Next.js middleware
  • nextjsApiRateLimit(options) - Next.js API routes

Examples and Demos

Check out the Express demo folder:

  • Complete Express server with rate limiting
  • All algorithms demonstrated with working endpoints
  • Database setup guides (Redis, PostgreSQL, MongoDB)
  • Connection string examples with authentication
  • Ready to run - just cd node_modules/omni-rate-limiter/expressjs-demo folder and npm install && npm start

The demo is the fastest way to understand how everything works!

Contributing

Found a bug or want to add a feature? Please email at [email protected] .

License

MIT - Use it however you want!