Smart Rate Limiter with Sliding Window

    An advanced rate limiter implementation. Protects your API endpoints from spam and brute-force attacks using a sliding window algorithm. Features a memory-efficient design, IP-based monitoring, and automatic cleanup.

    Language: typescript

    Author: Umut Bayraktar (@umut)

    2 stars · 16 views

    Smart Rate Limiter with Sliding Window

    Public
    • Updated Jan 11, 2026
    2
    smart-rate-limiter-with-sliding-window.ts
    /**
     * Smart Rate Limiter with Sliding Window Algorithm
     * 
     * Features:
     * - Sliding window for accurate rate limiting
     * - Memory-efficient with automatic cleanup
     * - Configurable per-route limits
     * - TypeScript support
     */
    
    interface RateLimitConfig {
      windowMs: number;      // Time window in milliseconds
      maxRequests: number;   // Max requests per window
      message?: string;      // Error message
    }
    
    interface RequestRecord {
      count: number;
      timestamps: number[];
    }
    
    class SlidingWindowRateLimiter {
      private requests: Map<string, RequestRecord> = new Map();
      private config: RateLimitConfig;
      private cleanupInterval: NodeJS.Timeout;
    
      constructor(config: RateLimitConfig) {
        this.config = {
          message: "Too many requests, please try again later.",
          ...config
        };
        
        // Auto-cleanup every minute
        this.cleanupInterval = setInterval(() => this.cleanup(), 60000);
      }
    
      /**
       * Check if request is allowed
       * @returns { allowed: boolean, remaining: number, resetIn: number }
       */
      check(identifier: string): { allowed: boolean; remaining: number; resetIn: number } {
        const now = Date.now();
        const windowStart = now - this.config.windowMs;
        
        let record = this.requests.get(identifier);
        
        if (!record) {
          record = { count: 0, timestamps: [] };
          this.requests.set(identifier, record);
        }
        
        // Remove timestamps outside the window
        record.timestamps = record.timestamps.filter(ts => ts > windowStart);
        record.count = record.timestamps.length;
        
        const remaining = Math.max(0, this.config.maxRequests - record.count);
        const oldestTimestamp = record.timestamps[0] || now;
        const resetIn = Math.max(0, oldestTimestamp + this.config.windowMs - now);
        
        if (record.count >= this.config.maxRequests) {
          return { allowed: false, remaining: 0, resetIn };
        }
        
        // Allow request and record timestamp
        record.timestamps.push(now);
        record.count++;
        
        return { 
          allowed: true, 
          remaining: remaining - 1, 
          resetIn: this.config.windowMs 
        };
      }
    
      /**
       * Express middleware factory
       */
      middleware() {
        return (req: any, res: any, next: any) => {
          const identifier = req.ip || req.connection.remoteAddress || "unknown";
          const result = this.check(identifier);
          
          // Set rate limit headers
          res.setHeader("X-RateLimit-Limit", this.config.maxRequests);
          res.setHeader("X-RateLimit-Remaining", result.remaining);
          res.setHeader("X-RateLimit-Reset", Math.ceil(result.resetIn / 1000));
          
          if (!result.allowed) {
            res.status(429).json({ 
              error: this.config.message,
              retryAfter: Math.ceil(result.resetIn / 1000)
            });
            return;
          }
          
          next();
        };
      }
    
      /**
       * Cleanup expired entries
       */
      private cleanup(): void {
        const now = Date.now();
        const windowStart = now - this.config.windowMs;
        
        for (const [key, record] of this.requests.entries()) {
          record.timestamps = record.timestamps.filter(ts => ts > windowStart);
          if (record.timestamps.length === 0) {
            this.requests.delete(key);
          }
        }
      }
    
      /**
       * Stop the cleanup interval
       */
      destroy(): void {
        clearInterval(this.cleanupInterval);
        this.requests.clear();
      }
    }
    
    // Usage Example:
    const apiLimiter = new SlidingWindowRateLimiter({
      windowMs: 60 * 1000,  // 1 minute
      maxRequests: 100,     // 100 requests per minute
      message: "Rate limit exceeded. Please wait before making more requests."
    });
    
    // Express usage:
    // app.use("/api", apiLimiter.middleware());
    
    // Manual check:
    // const result = apiLimiter.check("user-123");
    // console.log(result); // { allowed: true, remaining: 99, resetIn: 60000 }
    
    export { SlidingWindowRateLimiter, RateLimitConfig };

    About

    An advanced rate limiter implementation. Protects your API endpoints from spam and brute-force attacks using a sliding window algorithm. Features a memory-efficient design, IP-based monitoring, and automatic cleanup.

    2stars
    16views
    Created Jan 11, 2026