Rate Limit Storage
Keep rate limit counters in memory or in a shared store such as Redis
By default, counters live in the memory of the server process. That's fast and needs no setup, but each server has its own counters and they reset when the process restarts. If you run several copies of your server behind a load balancer, a caller could get the full limit from each copy. Store the counters somewhere shared instead.
- No setup
- Counters reset on restart
- Each server counts separately
- All servers share one count
- Survives restarts
- Needs a store you run
The storage interface
Any object with these two methods can store counters. increment adds one to a key's count and returns the new total, starting a new window when the old one has ended. reset clears a key.
interface RateLimitStorage {
increment(key: string, windowMs: number): Promise<number> | number;
reset(key: string): Promise<void> | void;
}Redis
This implementation uses the redis package. The first call in a window sets the key to expire when the window ends, so Redis clears old counters for you.
import { createClient } from 'redis';
import type { RateLimitStorage } from '@nitrostack/core';
const client = createClient({ url: process.env.REDIS_URL });
await client.connect();
export const redisRateLimitStorage: RateLimitStorage = {
async increment(key, windowMs) {
const count = await client.incr(`ratelimit:${key}`);
if (count === 1) {
await client.pExpire(`ratelimit:${key}`, windowMs);
}
return count;
},
async reset(key) {
await client.del(`ratelimit:${key}`);
},
};Pass it to each limit that should be shared:
@RateLimit({ requests: 100, window: '1h', storage: redisRateLimitStorage })