Help us measure site usage?

With your permission, we use self-hosted analytics to understand page usage. It is optional and does not affect the Service. Learn more.

TypeScriptadvanceddevopsAI generated

Distributed Rate Limiter and API Gateway Middleware

Build a rate limiting middleware for Express in TypeScript that works across several copies of an API: all instances share their counters in Redis (through ioredis), so a limit such as 100 requests per minute per API key holds no matter which server handles each request. It supports two strategies, token bucket (allows short bursts) and sliding window (smooth limits), each implemented as an atomic Redis Lua script, with typed configuration per route, standard rate limit headers, and a choice of what to do when Redis is down. A load test with several instances proves the limits hold, and rejections are logged and counted. You'll learn designing typed middleware, rate limiting algorithms, atomic distributed state with Redis, graceful degradation, and load testing.

Estimate
~12.5h
Steps
7
Completed by
0
Proposed by
codeseed.app

Express · Redis · ioredis

Project roadmap

  1. 01

    Design the configuration API

    ~1.5h

    Define TypeScript types for the configuration: the strategy (tokenBucket with capacity and refill rate, or slidingWindow with limit and window), a key function (by default the API key header, or the IP), per-route overrides, a skip function, the behavior when Redis fails (allow or deny), and a custom rejection handler. Write README.md with usage examples. Done when: the types compile, and README.md shows how the middleware is used.

  2. 02

    Implement the token bucket

    ~2h

    Write a Lua script that reads a bucket (tokens and last refill time) from a Redis hash, refills based on the elapsed time using Redis's TIME, takes one token if available, stores the bucket with an expiry, and returns whether the request is allowed, the remaining tokens and the wait time. Load it with ioredis's defineCommand. Done when: tests against a real Redis show bursts up to the capacity, then steady refilling.

  3. 03

    Implement the sliding window

    ~2h

    Write a second Lua script for a sliding window counter: keep counts for the current and previous fixed windows, estimate the count as current plus previous weighted by how much of the previous window still overlaps, and allow the request if under the limit. Compare its behavior with the token bucket at window edges in a small test. Done when: tests show the sliding window prevents bursts at the edges of windows.

  4. 04

    Build the middleware and a sample API

    ~2h

    Write the Express middleware that picks the route's configuration, computes the key, runs the right script, sets RateLimit-Limit, RateLimit-Remaining, RateLimit-Reset and Retry-After headers, and returns 429 with a JSON body when over the limit. Apply a Redis timeout, and follow the configured fail-open or fail-closed behavior on errors. Build a sample API with different limits per route. Done when: the sample API returns 429 with correct headers when limits are exceeded.

  5. 05

    Load test across instances

    ~2h

    Run three instances of the sample API behind a simple proxy (or on different ports) sharing one Redis, and write a load test (with autocannon or k6, on your own machine) sending many requests with a few API keys. Check that the number of allowed requests per key never exceeds its limit across all instances, and record throughput and added latency in README.md. Done when: README.md shows the limits held under load with the measured overhead.

  6. 06

    Log and count rejections

    ~1h

    Log each rejection with the key (hashed), route, strategy and remaining wait, and keep counters of allowed and rejected requests per route, exposed on a metrics endpoint (for example in Prometheus format with prom-client). Count Redis errors and fallbacks too. Done when: the metrics endpoint shows allowed and rejected counts per route.

  7. 07

    Test it

    ~2h

    Write Vitest tests with a real Redis (Testcontainers or a local instance): both strategies, headers, per-route overrides, skip rules, many concurrent requests never exceeding the limit, and fail-open and fail-closed behavior with Redis stopped. Done when: npm test passes.

Ready to build this?

Get a GitHub repo and start building. Your AI reviewer checks each step as you go.

~12.5h · 7 steps

Tech stack

ExpressRedisioredis