Rate Limiter
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
200with the response and rate limit headers - When a client exceeds the limit, return
429 Too Many Requestswith rate limit headers - Use Redis to store rate limit counters (available via
REDIS_URLenv var)
Your Server
- Start an HTTP server on the port specified by the
PORTenvironment variable (default:3000) - A Redis instance is available at the
REDIS_URLenvironment 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.
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))Your solution will be tested against these scenarios plus a hidden set.
- 01GET /api/resource returns 200 with rate limit headers
- 02Response includes X-RateLimit-Limit, X-RateLimit-Remaining, and X-RateLimit-Reset headers
- 03X-RateLimit-Remaining decreases with each request
- 04After 10 requests, the 11th returns 429 Too Many Requests
Hints
Click each hint to reveal it. Take your time — try before you peek.