Stop 22 · Build Backend Portfolio ProjectsProjects · 5/14

Rate Limiter Service

Part of the Backend Developer Roadmap

In short

Rate limiting protects APIs from abuse, runaway scripts and surprise bills. Building it as a real service, shared across many servers, is a system design answer you can actually demo.

A standalone rate-limiting service other applications can call — practice for implementing a real system-design interview question as working, deployed code.

The purple blob gatekeeper lets one visitor through a turnstile and holds up a palm to the crowd.

Think of it like this

The gatekeeper at a college fest

The hall holds only so many people per hour. The gatekeeper counts entries and, once the limit is hit, says "wait 10 minutes". If there are three gates, they share one count over walkie-talkies, or the limit means nothing.

Watch it happen

Request number 101 in a minute

Client
API
Limiter
Redis

GET /search

1/6A client sends its 101st request within the same minute.

The big ideas

What you need to know

Pick an algorithm

Fixed window is simplest. Sliding window is fairer at the edges. Token bucket allows short bursts.

Shared, atomic counters

Keep counts in Redis with INCR or a Lua script, so every server instance sees the same number.

Clear responses

Return 429 with Retry-After, plus headers telling clients their limit and how much is left.

Decide on failure mode

If Redis is down, do you allow everything (fail open) or block everything (fail closed)? Choose on purpose.

See it in code

Walk through the code

ts
1import Redis from "ioredis";2const redis = new Redis(process.env.REDIS_URL!);3 4export async function allow(key: string, limit = 100, windowSec = 60) {5  const window = Math.floor(Date.now() / 1000 / windowSec);6  const redisKey = `rl:${key}:${window}`;7 8  const [[, count]] = (await redis9    .multi()10    .incr(redisKey)11    .expire(redisKey, windowSec)12    .exec()) as [Error | null, number][];13 14  return { allowed: count <= limit, remaining: Math.max(0, limit - count) };15}

1/3Work out which minute we're in and build a key like rl:user:7:29481730.

A fixed-window limiter. Each minute gets its own key that deletes itself, so Redis memory stays small.

Save yourself the pain

Common mistakes

Oops

Keeping counters in each server's memory.

Do this instead

With 3 servers behind a load balancer, users get 3x the limit. Use a shared store like Redis.

Oops

Limiting only by IP address.

Do this instead

Many users share one IP on college Wi-Fi or mobile networks. Prefer user ID or API key, with IP as a fallback.

Your mission

Milestone 1: a shared limiter

~40 min

Quick check

Your API runs on 4 servers, each with its own in-memory limit of 100 per minute. What's the real limit for a user?

Go deeper

  1. 1Implement at least two algorithms (token bucket and sliding window) and let callers choose.
  2. 2Make it usable as a shared service across multiple client applications via Redis.
  3. 3Add clear response headers (remaining requests, reset time) like real APIs do.

More projects at this stop

Stuck on this topic? Ask an Insider

Get 1:1 guidance from people who've walked this exact path — on the InsideEdge app.

Download
InsideEdge