Skip to content
/practice·rate-limiter

Rate Limiter

intermediaterate-limitingmiddlewareredishttp
requires:ExpressRedis
/concept
Rate Limiting
Protecting services from excessive traffic
Read full guide

Problem

Build an HTTP API with a rate limiter that restricts clients to a fixed number of requests per time window.

Background

Rate limiting protects services from abuse and ensures fair resource allocation. Every production API has some form of rate limiting — whether it's per-user, per-IP, or per-API-key.

A well-implemented rate limiter should:

  • Accurately count requests within a time window
  • Return proper HTTP status codes and headers
  • Handle concurrent requests correctly
  • Use an external store (Redis) so it works across multiple server instances

Requirements

  • Implement GET /api/resource — a simple endpoint that returns a JSON response
  • Apply a rate limit of 10 requests per 60-second window per client IP
  • When a client is within the limit, return 200 with the response and rate limit headers
  • When a client exceeds the limit, return 429 Too Many Requests with rate limit headers
  • Use Redis to store rate limit counters (available via REDIS_URL env var)

Your Server

  • Start an HTTP server on the port specified by the PORT environment variable (default: 3000)
  • A Redis instance is available at the REDIS_URL environment variable

API Contract

`GET /health`

Health check endpoint. Return 200 when your server is ready.

  • Response: 200 { "status": "ok" }

`GET /api/resource`

A rate-limited endpoint that returns a simple resource.

Response (within limit):

  • Status: 200
  • Body: { "message": "ok" }
  • Headers:

- X-RateLimit-Limit: 10 — max requests per window

- X-RateLimit-Remaining: — requests remaining (limit minus requests made this window, minimum 0)

- X-RateLimit-Reset: — Unix timestamp (seconds) when the window resets

Response (rate limited):

  • Status: 429
  • Body: { "error": "Rate limit exceeded" }
  • Headers:

- X-RateLimit-Limit: 10

- X-RateLimit-Remaining: 0

- X-RateLimit-Reset:

- Retry-After: — seconds until the client can retry

Useful APIs

These are the key APIs you'll need. How you combine them is the challenge.

code
class=class="text-pass">"text-fg-subtle">// Redis: atomically increment a key (returns new value)
const count = await redis.incr(key)

class=class="text-pass">"text-fg-subtle">// Redis: set key expiration in seconds
await redis.expire(key, seconds)

class=class="text-pass">"text-fg-subtle">// Redis: get remaining TTL on a key
const ttl = await redis.ttl(key)

class=class="text-pass">"text-fg-subtle">// Redis: execute a Lua script atomically (prevents race conditions)
const result = await redis.eval(luaScript, numKeys, key, ...args)

class=class="text-pass">"text-fg-subtle">// Express: get client IP address
const ip = req.ip || class="text-pass">'127.0.0.1'

class=class="text-pass">"text-fg-subtle">// Calculate current window ID (60-second windows)
const windowId = Math.floor(Date.now() / (60 * 1000))
Scenarios·4 visible + hidden

Your solution will be tested against these scenarios plus a hidden set.

  1. 01GET /api/resource returns 200 with rate limit headers
  2. 02Response includes X-RateLimit-Limit, X-RateLimit-Remaining, and X-RateLimit-Reset headers
  3. 03X-RateLimit-Remaining decreases with each request
  4. 04After 10 requests, the 11th returns 429 Too Many Requests

Hints

Click each hint to reveal it. Take your time — try before you peek.