Skip to main content
Glama
rate-limit.ts4.04 kB
/** * Rate Limiting Middleware * * Implements HTTP rate limiting for the REST API using the existing RateLimiter. * Adds rate limit headers to responses and returns 429 when limit exceeded. * * Requirements: 18.2 */ import type { NextFunction, Request, Response } from "express"; import { RateLimiter } from "../../security/rate-limiter.js"; /** Rate limit middleware configuration */ export interface RateLimitMiddlewareConfig { /** Time window in milliseconds */ windowMs: number; /** Maximum requests per window */ maxRequests: number; /** Custom key generator function */ keyGenerator?: (req: Request) => string; /** Skip rate limiting for certain requests */ skip?: (req: Request) => boolean; /** Custom handler for rate limit exceeded */ handler?: (req: Request, res: Response, next: NextFunction) => void; /** Include rate limit headers in response */ headers?: boolean; /** Skip failed requests from counting */ skipFailedRequests?: boolean; /** Skip successful requests from counting */ skipSuccessfulRequests?: boolean; } /** Default rate limit configuration */ const DEFAULT_CONFIG: Required<RateLimitMiddlewareConfig> = { windowMs: 60000, // 1 minute maxRequests: 100, // 100 requests per minute keyGenerator: (req) => { // Use X-Forwarded-For header if behind proxy, otherwise use IP const forwarded = req.headers["x-forwarded-for"]; if (typeof forwarded === "string") { return forwarded.split(",")[0].trim(); } return req.ip ?? req.socket.remoteAddress ?? "unknown"; }, skip: () => false, handler: (_req, res) => { res.status(429).json({ success: false, error: "Rate limit exceeded", code: "RATE_LIMITED", suggestion: "Please wait before making more requests", }); }, headers: true, skipFailedRequests: false, skipSuccessfulRequests: false, }; /** * Create rate limiting middleware */ export function createRateLimitMiddleware(config: Partial<RateLimitMiddlewareConfig> = {}) { const rateLimitConfig: Required<RateLimitMiddlewareConfig> = { ...DEFAULT_CONFIG, ...config, }; const limiter = new RateLimiter({ windowMs: rateLimitConfig.windowMs, maxRequests: rateLimitConfig.maxRequests, keyGenerator: (ctx) => ctx.ip ?? "unknown", skipFailedRequests: rateLimitConfig.skipFailedRequests, skipSuccessfulRequests: rateLimitConfig.skipSuccessfulRequests, }); return (req: Request, res: Response, next: NextFunction): void => { // Check if request should be skipped if (rateLimitConfig.skip(req)) { next(); return; } const key = rateLimitConfig.keyGenerator(req); const result = limiter.check({ ip: key }); // Add rate limit headers if enabled if (rateLimitConfig.headers) { res.setHeader("X-RateLimit-Limit", rateLimitConfig.maxRequests.toString()); res.setHeader("X-RateLimit-Remaining", result.remaining.toString()); res.setHeader("X-RateLimit-Reset", result.resetTime.toISOString()); } // Check if rate limit exceeded if (!result.allowed) { if (rateLimitConfig.headers && result.retryAfter !== undefined) { res.setHeader("Retry-After", result.retryAfter.toString()); } rateLimitConfig.handler(req, res, next); return; } // Record the request after response is sent res.on("finish", () => { const success = res.statusCode < 400; limiter.record({ ip: key }, success); }); next(); }; } /** * Create a rate limiter instance for manual control * Useful for testing or custom rate limiting scenarios */ export function createRateLimiterInstance( config: Partial<RateLimitMiddlewareConfig> = {} ): RateLimiter { const rateLimitConfig = { ...DEFAULT_CONFIG, ...config }; return new RateLimiter({ windowMs: rateLimitConfig.windowMs, maxRequests: rateLimitConfig.maxRequests, skipFailedRequests: rateLimitConfig.skipFailedRequests, skipSuccessfulRequests: rateLimitConfig.skipSuccessfulRequests, }); }

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/keyurgolani/ThoughtMcp'

If you have feedback or need assistance with the MCP directory API, please join our Discord server