Well I failed a amazon OA because of a rate limiting question, So I have built this rate limiter from first principles and haven't followed any tutorial. Through this blog, I want to answer questions like: Why even use Redis to make a rate limiter? Why not just use a Node.js process? What are the issues with using Node.js? Node.js race conditions: If we use an in-memory map, async execution causes race conditions. If we use another DB to store the rate-limiting data, it can still cause race conditions because most DBs do not block queries from reading data even if another query hasn't finished writing the new state. Millisecond collisions: Two requests hitting at the same millisecond could read and write into the same timestamps, causing a rate-limit bypass. Data structure choices: Why use a ZSET instead of a simple map with keys and an array? Library flaws: How packages like express-rate-limit handle some of these flaws, but still suffer from a "boundary burst" problem because they use a fixed-window approach. Also I didnt want to use a npm package and wanted to build this from scratch. Node.js Event-Loop Race Conditions Node.js is single-threaded, but it is asynchronous. When execution hits an await (like fetching user context, parsing a JWT, or logging), Node pauses that function execution and yields control back to the Event Loop. If two requests from the same IP hit your server within the same millisecond: Request A reads timestamps (length = 4). Request A hits an await boundary. The Event Loop pauses Request A and starts processing Request B. Request B reads the exact same timestamps array in memory (still length = 4!). Both requests pass the length >= 5 check, both write their timestamps, and the rate limit is bypassed. If Node Memory Fails, Why Not Use a Standard Database (MongoDB/SQL)? If in-memory maps fail, the logical next step is persisting timestamps in a database. However, this shifts the concurrency bug from Node.js to the Database engine. Most traditional databases do not queue incoming queries into a single-file line—doing so would tank read throughput. Instead, they use Multi-Version Concurrency Control (MVCC). Standard databases allow stale reads while writes are still in flight. Unless you apply explicit, heavy row locks (SELECT FOR UPDATE), Request B reads the uncommitted state before Request A's write finishes processing. The database isn't broken—it's performing as designed, but its isolation model fails read-check-write validation without strict locks. The express-rate-limit Fixed Window Flaw (Boundary Burst) Packages like express-rate-limit fix basic in-memory issues when backed by a store, but by default, they rely on a fixed-window algorithm. This leads to the Boundary Burst problem: if your limit is 100 requests per minute resetting at 12:00, an attacker can send 100 requests at 12:00:59 and another 100 requests at 12:01:01. The counter resets at 12:01:00, allowing 200 requests within a 2-second window while technically staying under the limit. Why Redis Uses a ZSET (Map + Skip List) Instead of a Map + Array A sorted array lets us search for the window cutoff in O(logN) time using binary search. However, rate limiters are write-heavy engines: Insertion & Pruning in Arrays: Inserting or removing timestamps forces the CPU to shift surrounding elements in RAM—an O(N) memory-copy operation (memmove). Redis solves this by combining two data structures into a ZSET: Hash Map (dict): Maps Value ──> Score. Gives O(1) time complexity to look up or check if a member exists. Skip List (zskiplist): Maintains elements ordered by Score. By trading a tiny bit of extra memory (≈0.33N extra pointers) to build hierarchical Express Lanes over a standard linked list, Skip Lists achieve O(logN) search speeds while retaining O(1) pointer-swap insertions and deletions. The structure is like this Check out the code :- https://gist.github.com/TejaswaHinduja/48ecaf4a63d29306bd255456588fb5c4 If you liked the blog do leave a like and connect with me on socials: LinkedIn:https://www.linkedin.com/in/tejaswahinduja/ Twitter:https://x.com/Tej_Codes Github:https://github.com/TejaswaHinduja