Sliding Window Rate Counter

Enforce a per-key request rate limit with a sliding window of timestamps, smoothing the bursts a fixed window allows.

JavascriptResilience

Code

snippet
export class SlidingWindowLimiter {
  constructor(limit, windowMs) {
    if (!Number.isInteger(limit) || limit <= 0) throw new RangeError('limit must be positive');
    if (!Number.isInteger(windowMs) || windowMs <= 0) throw new RangeError('windowMs must be positive');
    this.limit = limit;
    this.windowMs = windowMs;
    this.hits = new Map();
  }

  allow(key, now = Date.now()) {
    const cutoff = now - this.windowMs;
    const times = (this.hits.get(key) ?? []).filter((t) => t > cutoff);
    if (times.length >= this.limit) {
      this.hits.set(key, times);
      return { allowed: false, retryAfterMs: times[0] + this.windowMs - now };
    }
    times.push(now);
    this.hits.set(key, times);
    return { allowed: true, remaining: this.limit - times.length };
  }
}