@devhuset-oss/ratelimit
v1.0.0-beta.3
Published
A flexible rate limiting library with support for fixed and sliding windows using Redis
Downloads
515
Maintainers
Readme
@devhuset-oss/ratelimit
A flexible Redis-based rate limiting library supporting both fixed and sliding window algorithms. Perfect for API rate limiting, request throttling, and protecting your services from abuse.
Features
- 🚦 Fixed window rate limiting
- 📊 Sliding window rate limiting
- 🔄 Redis-backed for distributed systems
- 🎯 TypeScript support
- ⚡️ High performance
- 🛡️ Protection against race conditions
- 💪 Zero dependencies (except Redis)
Installation
npm install @devhuset-oss/ratelimit redis
# or
yarn add @devhuset-oss/ratelimit redis
# or
pnpm add @devhuset-oss/ratelimit redis
# or
bun add @devhuset-oss/ratelimit redis
Quick Start
import { createClient } from 'redis';
import { Ratelimit } from '@devhuset-oss/ratelimit';
// Create Redis client
const redis = createClient({
url: 'redis://localhost:6379',
});
await redis.connect();
// Create rate limiter (10 requests per 60 seconds)
const limiter = new Ratelimit(
redis,
Ratelimit.slidingWindow({
limit: 10, // requests
window: 60, // seconds
prefix: 'my-api', // optional
}),
);
// Check rate limit
const result = await limiter.limit('user-123');
if (result.success) {
// Process request
console.log(`${result.remaining} requests remaining`);
} else {
// Rate limit exceeded
console.log(`Try again in ${result.retry_after}ms`);
}
Rate Limiting Algorithms
Fixed Window
Fixed window rate limiting divides time into fixed intervals (e.g., 60-second windows) and tracks requests within each window.
const limiter = new Ratelimit(
redis,
Ratelimit.fixedWindow({
limit: 100,
window: 60,
prefix: 'api', // optional
}),
);
Sliding Window
Sliding window rate limiting provides smoother rate limiting by considering both the current and previous windows with weighted rates.
const limiter = new Ratelimit(
redis,
Ratelimit.slidingWindow({
limit: 100,
window: 60,
prefix: 'api', // optional
}),
);
API Reference
Configuration
interface RatelimitOptionsWithoutType {
/** Maximum requests per window */
limit: number;
/** Window duration in seconds */
window: number;
/** Optional Redis key prefix */
prefix?: string;
}
// Create with static methods
Ratelimit.fixedWindow(options: RatelimitOptionsWithoutType)
Ratelimit.slidingWindow(options: RatelimitOptionsWithoutType)
Response
interface RatelimitResponse {
/** Whether the request is allowed */
success: boolean;
/** Maximum number of requests allowed in the window */
limit: number;
/** Number of remaining requests in current window */
remaining: number;
/** Time in milliseconds until the next request will be allowed. 0 if under limit */
retry_after: number;
/** Time in milliseconds when the current window expires completely */
reset: number;
}
Framework Integration Examples
Next.js Route Handler
import { NextResponse } from 'next/server';
import { createClient } from 'redis';
import { Ratelimit } from '@devhuset-oss/ratelimit';
import { headers } from 'next/headers';
const redis = createClient({
url: process.env.REDIS_URL,
});
await redis.connect();
const ratelimit = new Ratelimit(
redis,
Ratelimit.slidingWindow({
limit: 10,
window: 60,
prefix: 'api',
}),
);
export async function GET() {
const headersList = headers();
const ip = headersList.get('x-forwarded-for') || '127.0.0.1';
const { success, remaining, reset, retry_after } =
await ratelimit.limit(ip);
if (!success) {
return NextResponse.json(
{ error: 'Too many requests' },
{
status: 429,
headers: {
'X-RateLimit-Limit': '10',
'X-RateLimit-Remaining': remaining.toString(),
'X-RateLimit-Reset': reset.toString(),
'Retry-After': Math.ceil(retry_after / 1000).toString(),
},
},
);
}
// Process request
}
Express Middleware
import { createClient } from 'redis';
import { Ratelimit } from '@devhuset-oss/ratelimit';
import express from 'express';
const app = express();
const redis = createClient({
url: process.env.REDIS_URL,
});
await redis.connect();
const ratelimit = new Ratelimit(
redis,
Ratelimit.slidingWindow({
limit: 10,
window: 60,
prefix: 'api',
}),
);
app.use(async (req, res, next) => {
const ip = req.ip;
const { success, remaining, reset, retry_after } =
await ratelimit.limit(ip);
res.setHeader('X-RateLimit-Limit', '10');
res.setHeader('X-RateLimit-Remaining', remaining.toString());
res.setHeader('X-RateLimit-Reset', reset.toString());
if (!success) {
res.setHeader('Retry-After', Math.ceil(retry_after / 1000).toString());
return res.status(429).json({ error: 'Too many requests' });
}
next();
});
Contributing
Pull requests are welcome. For major changes, please open an issue first to discuss what you would like to change.
Please make sure to update tests as appropriate.