@devhuset-oss/ratelimit v1.0.0
@devhuset-oss/ratelimit
A flexible Valkey-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
- 🔄 Valkey-backed for distributed systems
- 🎯 TypeScript support
- ⚡️ High performance
- 🛡️ Protection against race conditions
- 💪 Zero dependencies (except Valkey)
Installation
npm install @devhuset-oss/ratelimit
# or
yarn add @devhuset-oss/ratelimit
# or
pnpm add @devhuset-oss/ratelimit
# or
bun add @devhuset-oss/ratelimitQuick Start
import { Ratelimit, Valkey } from '@devhuset-oss/ratelimit';
// Create Valkey client
const valkey = new Valkey('redis://localhost:6379');
// Create rate limiter (10 requests per 60 seconds)
const limiter = new Ratelimit(
valkey,
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(
valkey,
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(
valkey,
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 Valkey 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 { Ratelimit, Valkey } from '@devhuset-oss/ratelimit';
import { headers } from 'next/headers';
const valkey = new Valkey(process.env.VALKEY_URL);
const ratelimit = new Ratelimit(
valkey,
Ratelimit.slidingWindow({
limit: 10,
window: 60,
prefix: 'api',
}),
);
export async function GET() {
const headersList = await 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 new Response(JSON.stringify({ 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 { Ratelimit, Valkey } from '@devhuset-oss/ratelimit';
import express from 'express';
const app = express();
const valkey = new Valkey(process.env.VALKEY_URL);
const ratelimit = new Ratelimit(
valkey,
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.
License
9 months ago
9 months ago
9 months ago
9 months ago
9 months ago
9 months ago
9 months ago
9 months ago
9 months ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago