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 🙏

© 2026 – Pkg Stats / Ryan Hefner

oss-ratelimit

v2.6.0

Published

Flexible rate limiting library with Redis for TypeScript applications

Readme

Okay, let's revamp the README.md based on the current library features and best practices, incorporating the strengths of the old README while updating it significantly.

OSS-Ratelimit 🚀

npm version License: MIT

Contribute

A robust, production-ready, open-source rate limiting library for Node.js & Next.js, built with TypeScript. Inspired by Upstash Ratelimit but with enhanced features, flexibility, and a powerful registry system.

📚 Read the Full Documentation

Features ✨

  • Multiple Algorithms: Fixed Window, Sliding Window, and Token Bucket strategies out-of-the-box.
  • Efficient Redis Backend: Leverages Redis and Lua scripts for high-performance, distributed rate limiting.
  • Client & Registry Management: Easily manage multiple limiter configurations and efficiently reuse Redis connections.
  • Ephemeral Cache: Optional in-memory fallback during Redis outages (currently for Sliding Window) for improved resilience.
  • Fail Open/Closed Strategy: Configurable behavior during Redis connection issues.
  • Analytics: Optional tracking of request counts and throughput per identifier.
  • Blocking Support: Includes a .block() method to wait until a request is permitted.
  • TypeScript First: Strongly typed for a great developer experience.
  • Next.js Ready: Designed for easy integration with Next.js (Pages Router, App Router, Middleware) including IP detection utilities.
  • Customizable: Flexible configuration options for fine-tuning behavior.

Installation 📦

Install the library and its peer dependency redis:

npm install oss-ratelimit redis
# or
yarn add oss-ratelimit redis
# or
pnpm add oss-ratelimit redis

You might also need types for Redis:

npm install -D @types/redis
# or
yarn add -D @types/redis
# or
pnpm add -D @types/redis

Quick Start

While you can create single instances, we strongly recommend using the Registry system (see below) for managing configurations and Redis clients efficiently in most applications.

Here's a very basic example using a single instance:

import { createClient } from 'redis';
import { Ratelimit, slidingWindow, getRedisClient } from 'oss-ratelimit';

async function run() {
  // 1. Get a Redis client (using the helper is convenient)
  const redis = await getRedisClient({
    url: process.env.RATELIMIT_REDIS_URL || 'redis://localhost:6379'
  });
  // Ensure you handle connection errors from getRedisClient if needed

  // 2. Configure and create the limiter instance
  const limiter = new Ratelimit({
    redis, // The connected client
    limiter: slidingWindow(10, '10 s'), // Allow 10 requests per 10 seconds
    prefix: 'quickstart_app', // Optional prefix for keys
  });

  // 3. Apply the limit
  const identifier = 'user:123';
  const result = await limiter.limit(identifier);

  console.log(`Limit result for ${identifier}:`, result);
  // Example Output: { success: true, limit: 10, remaining: 9, reset: 1678886410000 }

  if (!result.success) {
    console.warn(`Rate limit exceeded for ${identifier}. Retry after ${result.retryAfter}s.`);
  }

  // Remember to disconnect the client when done
  await redis.quit();
}

run().catch(console.error);

Recommended Usage: Registry System 🏗️

For managing multiple rate limits (e.g., different API tiers, login vs general API) and simplifying Redis client management, use the built-in registry.

1. Initialize the Registry (initRateLimit)

Define your limiter configurations in one place.

import {
  initRateLimit,
  RateLimitBuilder,
  RegisterConfigParam,
  slidingWindow,
  fixedWindow,
} from 'oss-ratelimit';

// Define unique names for your limiters
export type LimiterName = 'apiGeneral' | 'loginAttempts' | 'expensiveOp';

// Create the registry instance (configure default Redis connection)
export const rateLimiterRegistry: RateLimitBuilder<LimiterName> = initRateLimit<LimiterName>({
  defaultRedisOptions: {
    url: process.env.RATELIMIT_REDIS_URL || 'redis://localhost:6379',
  },
});

// Define configurations for each limiter name
export const limiterConfigs: Record<LimiterName, RegisterConfigParam> = {
  apiGeneral: {
    limiter: slidingWindow(50, '30 s'), // 50 req / 30 sec
    prefix: 'rl_api_gen',
    analytics: true,
  },
  loginAttempts: {
    limiter: fixedWindow(5, '15 m'), // 5 req / 15 min
    prefix: 'rl_login',
    failOpen: false, // Be strict on login attempts
  },
  expensiveOp: {
     limiter: fixedWindow(10, '1 h'), // 10 req / hour
     prefix: 'rl_expensive',
  }
};

2. Initialize Limiters Eagerly (Recommended)

Load configurations and connect to Redis on startup.

import { rateLimiterRegistry, limiterConfigs } from './lib/ratelimit';
import { initializeLimiters } from 'oss-ratelimit';

async function startServer() {
  try {
    console.log("Initializing rate limiters...");
    // Initialize all limiters defined in the configs
    await initializeLimiters({
      registry: rateLimiterRegistry,
      configs: limiterConfigs,
      throwOnError: true, // Exit if any limiter fails to initialize
    });
    console.log("✅ Rate limiters initialized successfully.");

    // ... start your Express/Next.js/other server ...

  } catch (error) {
    console.error("💥 Failed to initialize rate limiters:", error);
    process.exit(1);
  }
}

startServer();

3. Use the Limiter

Access the specific limiter instance via the registry.

import { rateLimiterRegistry } from '@/lib/ratelimit'; // Adjust path
import { getIpFromRequest } from '@/utils/getIpFromRequest'; // See Docs for this util

// Example in an Express-like handler
async function handleRequest(req: Request, res: Response) {
  const ip = getIpFromRequest(req); // Implement IP detection
  if (!ip) return res.status(400).send('Cannot determine IP');

  try {
    // Get the specific limiter instance by name
    const limiter = rateLimiterRegistry.get('apiGeneral'); // Throws if not initialized

    const result = await limiter.limit(ip);

    // Add rate limit headers to response (recommended)
    res.setHeader('X-RateLimit-Limit', result.limit);
    res.setHeader('X-RateLimit-Remaining', result.remaining);
    // ... other headers

    if (!result.success) {
      res.setHeader('Retry-After', result.retryAfter ?? 1);
      return res.status(429).send('Too Many Requests');
    }

    // Proceed with protected logic
    res.send('Success!');

  } catch (error) {
    console.error("Rate limit check failed:", error);
    res.status(500).send('Internal Server Error');
  }
}

➡️ Learn more about the Registry System in the Docs

Available Limiters

Quick examples of configuring different algorithms:

Fixed Window

import { fixedWindow } from 'oss-ratelimit';
// 100 requests per minute
const limiterConfig = fixedWindow(100, '1 m');

Sliding Window

import { slidingWindow } from 'oss-ratelimit';
// 50 requests per 30 seconds (rolling window)
const limiterConfig = slidingWindow(50, '30 s');

Token Bucket

import { tokenBucket } from 'oss-ratelimit';
// Refill 5 tokens every 10 seconds, bucket capacity of 20
const limiterConfig = tokenBucket(5, '10 s', 20);

➡️ Read Algorithm Details in the Docs

API Highlights

const limiter = rateLimiterRegistry.get('apiGeneral');
const identifier = 'user:xyz';

// Check limit (consumes 1 request if successful)
const { success, limit, remaining, reset, retryAfter, pending, throughput } = await limiter.limit(identifier);

// Block until request is allowed (or timeout)
try {
  await limiter.block(identifier, { maxWaitMs: 3000 }); // Wait up to 3s
  // Proceed...
} catch (e) { /* Handle RateLimitExceededError */ }

// Reset the limit for an identifier
await limiter.reset(identifier);

// Check status without consuming request
const stats = await limiter.getStats(identifier); // { used, remaining, limit, reset }
const isAllowed = await limiter.check(identifier); // boolean

Configuration

Key options when creating a Ratelimit instance or registering with the registry:

  • redis: Connected RedisClientType instance or RedisOptions.
  • limiter: Algorithm configuration (fixedWindow(...), etc.).
  • prefix: String prefix for Redis keys (default: open-ratelimit).
  • analytics: boolean (default: false) - Enable extra metrics.
  • timeout: number (default: 1000) - Redis command timeout (ms).
  • ephemeralCache: boolean (default: true) - Enable in-memory fallback (Sliding Window only).
  • ephemeralCacheTTL: number (default: 60000) - TTL for cache entries (ms).
  • failOpen: boolean (default: false) - Allow requests if Redis fails?
  • silent: boolean (default: false) - Suppress warnings (e.g., cache usage).

➡️ See Full Configuration Options in the Docs

Error Handling ⚠️

The library uses specific error types:

  • RatelimitError: Base error class.
  • RedisConnectionError: Issues connecting/communicating with Redis (when failOpen: false).
  • RateLimitExceededError: Thrown by .block() on timeout.

Handle errors gracefully using try...catch:

try {
  await limiter.limit("user-123");
} catch (error) {
  if (error instanceof RedisConnectionError) {
     console.error("Redis down!", error);
     // Return 503 or handle based on failOpen config
  } else if (error instanceof RatelimitError) {
    console.error("Rate limit configuration or operational error:", error);
    // Return 500
  } else {
    console.error("Unexpected error:", error);
    // Return 500
  }
}

➡️ Learn more about Error Handling & Events in the Docs

Next.js Integration

Easily integrate with Next.js API Routes, Middleware, or App Router Handlers. A utility function for reliable IP detection is recommended.

➡️ See Next.js Integration Guide in the Docs

Contributing 🤝

Contributions are welcome! Please read the Contributing Guidelines before submitting a PR.

  1. Fork the repository.
  2. Create your feature branch (git checkout -b feature/your-feature).
  3. Commit your changes (git commit -m 'feat: Add some feature').
  4. Push to the branch (git push origin feature/your-feature).
  5. Open a Pull Request.

License

This project is licensed under the MIT License - see the LICENSE file for details.