From 6bc0c9543c4e7b744cf5c23585189a7b4523dd43 Mon Sep 17 00:00:00 2001 From: Rotimi Best Date: Thu, 12 Mar 2026 09:39:17 +0100 Subject: [PATCH] feat: enhance rate limiter options to support dynamic maxRequests and conditional skipping --- apps/api/src/middlewares/rate-limiter.ts | 23 ++++++++++++++++------- 1 file changed, 16 insertions(+), 7 deletions(-) diff --git a/apps/api/src/middlewares/rate-limiter.ts b/apps/api/src/middlewares/rate-limiter.ts index 406c995c..bfc50866 100644 --- a/apps/api/src/middlewares/rate-limiter.ts +++ b/apps/api/src/middlewares/rate-limiter.ts @@ -13,8 +13,9 @@ import { redis } from '@api/utils/redis/redis'; export interface RateLimiterOptions { windowMs?: number; - maxRequests?: number; + maxRequests?: number | ((c: any) => number); keyGenerator?: (c: any) => string; + skip?: (c: any) => boolean; skipSuccessfulRequests?: boolean; skipFailedRequests?: boolean; message?: string; @@ -24,20 +25,23 @@ export interface RateLimiterOptions { const defaultOptions: Required = { ...DEFAULT_RATE_LIMITER_OPTIONS, - keyGenerator: userKeyGenerator + keyGenerator: userKeyGenerator, + skip: () => false }; export const createRateLimiter = (options: RateLimiterOptions = {}): MiddlewareHandler => { const opts = { ...defaultOptions, ...options }; - const limiter = new RedisRateLimiter(redis, opts.windowMs, opts.maxRequests); return async (c, next) => { // Skip rate limiting if not in production - if (env.NODE_ENV !== 'production') { + if (env.NODE_ENV !== 'production' || opts.skip(c)) { return await next(); } try { + const maxRequests = typeof opts.maxRequests === 'function' ? opts.maxRequests(c) : opts.maxRequests; + const limiter = new RedisRateLimiter(redis, opts.windowMs, maxRequests); + // Generate rate limit key const key = opts.keyGenerator(c); @@ -46,13 +50,13 @@ export const createRateLimiter = (options: RateLimiterOptions = {}): MiddlewareH // Set rate limit headers if (opts.standardHeaders) { - c.header(RATE_LIMIT_HEADERS.LIMIT, opts.maxRequests.toString()); + c.header(RATE_LIMIT_HEADERS.LIMIT, maxRequests.toString()); c.header(RATE_LIMIT_HEADERS.REMAINING, result.remaining.toString()); c.header(RATE_LIMIT_HEADERS.RESET, new Date(result.resetTime).toISOString()); } if (opts.legacyHeaders) { - c.header(RATE_LIMIT_HEADERS.LIMIT, opts.maxRequests.toString()); + c.header(RATE_LIMIT_HEADERS.LIMIT, maxRequests.toString()); c.header(RATE_LIMIT_HEADERS.REMAINING, result.remaining.toString()); c.header(RATE_LIMIT_HEADERS.RESET, Math.ceil(result.resetTime / 1000).toString()); } @@ -84,4 +88,9 @@ export const createRateLimiter = (options: RateLimiterOptions = {}): MiddlewareH }; // Default rate limiter middleware with standard configuration -export default createRateLimiter(); +export default createRateLimiter({ + maxRequests: (c) => + c.req.path === '/api/auth/get-session' + ? DEFAULT_RATE_LIMITER_OPTIONS.maxRequests * 3 + : DEFAULT_RATE_LIMITER_OPTIONS.maxRequests +});