OSS Ratelimit

Getting Started

Install oss-ratelimit, configure it with storage (like Redis), and implement your first basic rate limiter in a Node.js or Next.js application.

Getting Started

Installation

Install the library and its peer dependency redis:

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

You might also need types for Redis if not automatically inferred:

Install Redis Types (Optional)
npm install -D @types/redis
# or
yarn add -D @types/redis
# or
pnpm add -D @types/redis

Basic Setup

The quickest way to get started is by creating a single Ratelimit instance. Ensure your Redis server is running or accessible.

import { Ratelimit, fixedWindow, getRedisClient } from 'oss-ratelimit';
import { createClient } from 'redis'; // Import redis client
 
// 1. Prepare Redis Client (using environment variables is recommended)
// Option A: Use the library's helper (handles connection logic)
const redisClientPromise = getRedisClient({
  url: process.env.RATELIMIT_REDIS_URL || 'redis://localhost:6379',
});
 
// Option B: Provide your own connected client instance
// const myRedisClient = createClient({ url: process.env.RATELIMIT_REDIS_URL });
// await myRedisClient.connect();
 
 
// 2. Configure the Limiter Algorithm
const limiterAlgorithm = fixedWindow(
  10,     // Allow 10 requests...
  '15 s'  // ...per 15 seconds
);
 
// 3. Create the Ratelimit Instance
// We await the client promise here before creating the Ratelimit instance
let ratelimit: Ratelimit;
 
redisClientPromise.then(redisClient => {
  ratelimit = new Ratelimit({
    redis: redisClient,           // The connected Redis client
    limiter: limiterAlgorithm,    // The chosen algorithm configuration
    prefix: 'myapp_basic',        // Optional: Prefix for Redis keys
  });
  console.log("Rate limiter initialized successfully!");
}).catch(err => {
  console.error("Failed to initialize rate limiter:", err);
  // Handle initialization failure (e.g., exit process, disable features)
});
 
 
// Export the instance (or a function to get it) for use elsewhere
// Note: Accessing 'ratelimit' before the promise resolves will result in an error.
// Consider using the registry pattern for easier management (see below).
export const getBasicRatelimiter = async (): Promise<Ratelimit> => {
    await redisClientPromise; // Ensure connection before returning
    if (!ratelimit) throw new Error("Ratelimiter not initialized");
    return ratelimit;
}

For managing multiple limiters or simplifying client handling, we strongly recommend using the Client & Registry Management features described below.

Usage

Apply the rate limit using a unique identifier (like an IP address or user ID).

import type { NextApiRequest, NextApiResponse } from 'next';
import { getBasicRatelimiter } from '@/lib/ratelimit'; // Adjust path
// You'll need an IP detection function (see Next.js Integration section)
import { getIpFromRequest } from '@/utils/getIpFromRequest';
 
export default async function handler(req: NextApiRequest, res: NextApiResponse) {
  const ip = getIpFromRequest(req); // Get client IP
 
  if (!ip) {
    return res.status(400).json({ error: 'Cannot determine IP address.' });
  }
 
  try {
    const ratelimit = await getBasicRatelimiter(); // Get the initialized instance
    const { success, limit, remaining, reset, retryAfter } = await ratelimit.limit(ip);
 
    // Set standard rate limit headers
    res.setHeader('X-RateLimit-Limit', limit);
    res.setHeader('X-RateLimit-Remaining', remaining);
    res.setHeader('X-RateLimit-Reset', Math.ceil(reset / 1000)); // Unix timestamp seconds
 
    if (!success) {
      if (retryAfter) {
         res.setHeader('Retry-After', retryAfter); // Seconds until retry is allowed
      }
      return res.status(429).json({
        error: `Rate limit exceeded for ${ip}. Try again later.`,
        retryAfter: retryAfter ?? 'unknown',
      });
    }
 
    // --- Rate limit passed, proceed with your logic ---
    res.status(200).json({ message: `Hello from ${ip}! Remaining: ${remaining}` });
    // --- End logic ---
 
  } catch (error: any) {
     console.error("Ratelimit error:", error);
     // Handle specific errors like Redis connection issues if needed
     if (error.name === 'RedisConnectionError') {
        // Specific handling if Redis is down and failOpen is false
        return res.status(503).json({ error: 'Service temporarily unavailable.' });
     }
     return res.status(500).json({ error: 'Internal Server Error' });
  }
}

On this page