Skip to the solution
MediumSystem Design13 min read

What is an API gateway?

818 views

The problem

Question presented to candidate: "We are splitting our backend into a dozen services, and the mobile and web apps are starting to call all of them directly. What is an API gateway, what would you put in it, what would you keep out of it, and how does it differ from a plain load balancer?"

What a strong answer should cover:

  • Definition: a reverse proxy that is the single entry point for external API clients, applying cross-cutting concerns once instead of in every service
  • The pipeline: routing (path, host, version), authentication (validating tokens or keys), coarse authorisation, rate limiting / quotas, request and response transformation, TLS termination, logging and metrics
  • Aggregation and the Backend for Frontend (BFF) pattern: one call from a slow mobile network instead of many, with worked latency numbers
  • How it differs from a load balancer (L7 routing and policy vs spreading traffic) and from a service mesh (north-south vs east-west traffic)
  • Risks: single point of failure and extra hop, a "smart gateway" that accumulates business logic, and gateway timeouts and limits (for example, Amazon API Gateway defaults)

Clarifying questions expected:

  • Who are the clients β€” first-party apps, third-party developers, or both?
  • How many services and teams, and who will own the gateway configuration?

Code / implementation expected: Optional β€” describing the pipeline is enough; sketching a route table or a token-bucket check is a plus.

  • api-gateway

Try it yourself

Sketch your approach before you read the solution. That is what the interview tests.

Stuck? Get a hint.

A short nudge toward the approach. It will not give the answer away.

Solution

The solution is hidden so you can try first

13 min read Β· 12 sections

Read the code

An API gateway pipeline: routing, signed tokens, roles, per-caller rate limit, aggregation
// An API gateway as a pipeline: route -> authenticate -> authorise -> rate limit
// -> forward. Backends are in-process functions and the clock is simulated.
// The token is a simplified signed token (payload + HMAC), standing in for a JWT.
const crypto = require("crypto");

const SECRET = "gateway-demo-secret";
function sign(payload) {
  return crypto.createHmac("sha256", SECRET).update(payload).digest("hex").slice(0, 16);
}
function makeToken(sub, role, exp) {
  const payload = `sub=${sub};role=${role};exp=${exp}`;
  return `${payload}.${sign(payload)}`;
}

const ROUTES = [ // longest matching prefix wins
  { prefix: "/api/orders/admin", service: "orders-svc", role: "admin" },
  { prefix: "/api/orders", service: "orders-svc", role: "user" },
  { prefix: "/api/users", service: "users-svc", role: "user" },
  { prefix: "/public", service: "content-svc", role: null },
];

const buckets = new Map(); // token bucket per caller: 3 requests burst, 1 per second refill
function allow(key, nowMs) {
  const cap = 3, perMs = 1 / 1000;
  const b = buckets.get(key) ?? { tokens: cap, at: nowMs };
  b.tokens = Math.min(cap, b.tokens + (nowMs - b.at) * perMs);
  b.at = nowMs;
  buckets.set(key, b);
  if (b.tokens >= 1) { b.tokens -= 1; return { ok: true }; }
  return { ok: false, retryAfterMs: Math.ceil((1 - b.tokens) / perMs) };
}

function handle(nowMs, path, token) {
  const route = ROUTES.filter((r) => path.startsWith(r.prefix)).sort((a, b) => b.prefix.length - a.prefix.length)[0];
  if (!route) return "404 no route";
  let caller = "anonymous";
  if (route.role) {
    if (!token) return "401 missing token";
    const dot = token.lastIndexOf(".");
    const payload = token.slice(0, dot), sig = token.slice(dot + 1);
    const good = sign(payload);
    if (sig.length !== good.length || !crypto.timingSafeEqual(Buffer.from(sig), Buffer.from(good))) return "401 bad signature";
    const claims = Object.fromEntries(payload.split(";").map((kv) => kv.split("=")));
    if (Number(claims.exp) * 1000 <= nowMs) return "401 token expired";
    if (route.role === "admin" && claims.role !== "admin") return "403 role user may not call admin route";
    caller = claims.sub;
  }
  const limit = allow(caller, nowMs);
  if (!limit.ok) return `429 rate limited, Retry-After ${Math.ceil(limit.retryAfterMs / 1000)}s`;
  return caller === "anonymous" ? `200 -> ${route.service} (public route, no identity)` : `200 -> ${route.service} (X-User-Id: ${caller} added)`;
}

const alice = makeToken("alice", "user", 1800);
const root = makeToken("root", "admin", 1800);
const forged = alice.replace("role=user", "role=admin");
const oldTok = makeToken("bob", "user", 60);

console.log("Part 1: one gateway, many checks");
const calls = [
  [0, "/api/orders/42", alice], [100, "/api/orders/43", alice], [200, "/api/users/me", alice],
  [300, "/api/orders/44", alice], [1400, "/api/orders/45", alice],
  [1500, "/api/orders/admin/refund", alice], [1600, "/api/orders/admin/refund", forged],
  [1700, "/api/orders/admin/refund", root], [1800, "/api/users/me", oldTok.slice(0, -1) + "0"],
  [61000, "/api/users/me", oldTok], [61100, "/api/users/me", null], [61200, "/public/home", null],
  [61300, "/internal/metrics", null],
];
for (const [t, path, tok] of calls) {
  console.log(`  t=${String(t).padStart(5)}ms  ${path.padEnd(26)} ${handle(t, path, tok)}`);
}

console.log("\nPart 2: one screen needs 3 backend calls (40, 70, 55 ms); mobile RTT 150 ms");
const backend = [40, 70, 55], rtt = 150, hop = 2; // hop = gateway to service RTT in the data centre
const direct = backend.reduce((s, b) => s + rtt + b, 0);
const viaGatewaySeq = rtt + backend.reduce((s, b) => s + hop + b, 0);
const viaGatewayPar = rtt + hop + Math.max(...backend);
console.log(`  client calls each service in turn:   ${direct} ms`);
console.log(`  gateway calls them in turn:          ${viaGatewaySeq} ms`);
console.log(`  gateway calls them in parallel:      ${viaGatewayPar} ms`);

Discussion

Other approaches, follow-ups and how it went in real interviews.

Sign in to join the discussion.

No comments yet. Share how you would answer it.

Question 20 of 106 in System design

Keep your momentum going.

Mark this question solved to track your progress, then take the next one while it is fresh.

Up nextMedium

What is a load balancer and what strategies does it use?

Continue