From 27cd12553287385b8bdc70e5e86f0afbdb981522 Mon Sep 17 00:00:00 2001 From: gauthier-th Date: Wed, 26 Jun 2024 14:55:47 +0200 Subject: [PATCH] refactor: add rate limit to fetch api --- server/utils/rateLimit.ts | 61 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 61 insertions(+) create mode 100644 server/utils/rateLimit.ts diff --git a/server/utils/rateLimit.ts b/server/utils/rateLimit.ts new file mode 100644 index 00000000..6049037e --- /dev/null +++ b/server/utils/rateLimit.ts @@ -0,0 +1,61 @@ +export type RateLimitOptions = { + maxRequests?: number; + perMilliseconds?: number; + maxRPS?: number; +}; + +export default function rateLimit< + T extends (...args: Parameters) => Promise, + U +>(fn: T, options: RateLimitOptions): (...args: Parameters) => Promise { + const maxRequests = options.maxRPS ?? options.maxRequests ?? 1; + const perMilliseconds = options.maxRPS + ? 1000 + : options.perMilliseconds ?? 1000; + + const queue: { + args: Parameters; + resolve: (value: U) => void; + }[] = []; + let activeRequests = 0; + let timer: NodeJS.Timeout | null = null; + + const processQueue = () => { + if (queue.length === 0) { + if (timer) { + clearInterval(timer); + timer = null; + } + return; + } + + while (activeRequests < maxRequests) { + activeRequests++; + const item = queue.shift(); + if (!item) break; + const { args, resolve } = item; + fn(...args) + .then(resolve) + .finally(() => { + activeRequests--; + if (queue.length > 0) { + if (!timer) { + timer = setInterval(processQueue, perMilliseconds); + } + } else { + if (timer) { + clearInterval(timer); + timer = null; + } + } + }); + } + }; + + return (...args: Parameters): Promise => { + return new Promise((resolve) => { + queue.push({ args, resolve }); + processQueue(); + }); + }; +}