Module 04 · Web · All tracks
APIs & Web Fundamentals
APIs are the contracts that let systems collaborate. Interviewers probe whether you understand the protocol underneath — not just that you can call an endpoint.
By the end you'll be able to explain, with conviction:
- HTTP verbs, status codes, and what makes an API genuinely RESTful.
- When to choose REST, GraphQL, or gRPC — and why.
- Idempotency, rate limiting, gateways, and the CORS rule everyone fumbles.
1HTTP methods & status codes
HTTP is the shared grammar of the web. Speak it precisely and everything else follows.
The methods express intent: GET reads, POST creates, PUT replaces, PATCH partially updates, DELETE removes. They carry semantic promises — GET should never change state (it's safe), and several are idempotent (§3). Honouring those promises is what lets caches, proxies, and retries work correctly.
Status codes are grouped by their leading digit, and knowing the families matters more than memorising every code:
- 2xx success — 200 OK, 201 Created, 204 No Content.
- 3xx redirection — 301 moved permanently, 304 not modified (cache hit).
- 4xx client error — 400 bad request, 401 unauthenticated, 403 forbidden, 404 not found, 429 too many requests.
- 5xx server error — 500 internal error, 502 bad gateway, 503 unavailable.
Common trap
401 vs 403 trips people up: 401 means "I don't know who you are" (authenticate), 403 means "I know who you are and you're not allowed" (authorisation). Mixing them up signals shaky fundamentals.
2What REST actually means
REST is an architectural style, not a protocol — a set of constraints Roy Fielding described for using HTTP the way it was designed. The core ideas: resources identified by URLs (nouns, not verbs — /orders/42, not /getOrder?id=42), manipulated with the standard HTTP methods, exchanging representations (usually JSON).
Two constraints carry most of the weight. Statelessness: each request contains everything needed to process it; the server keeps no client session between calls. This is what lets you scale horizontally — any server can handle any request. And a uniform interface: predictable, resource-oriented URLs and verbs, so a new consumer can guess how the API works.
Interview angle
"REST is a style: resources as URLs, standard HTTP verbs, and — the part people skip — statelessness. Because the server holds no session between requests, any instance can serve any request, which is what makes it scale horizontally."
3Idempotency & versioning
Idempotency means making the same request multiple times has the same effect as making it once. GET, PUT, and DELETE are idempotent; POST typically isn't (two POSTs create two orders). This matters enormously in the real world because networks retry — if a response is lost, the client resends, and a non-idempotent operation can double-charge a customer. The fix is an idempotency key the server uses to deduplicate retries.
Versioning protects existing consumers when an API must change incompatibly. Common approaches: URL path (/v2/orders), a header, or a query param. The principle matters more than the mechanism: never break existing clients — add a new version and migrate, rather than changing the old contract underneath people.
Interview angle
"Idempotency means a repeated request has the same effect as one — critical because clients retry on network failures. For payments I'd use an idempotency key so a retried POST can't double-charge."
4The request/response lifecycle
Knowing the journey of a request lets you reason about latency and failure at every hop.
auth · rate limit] GW --> S[Service] S --> DB[(Database)] DB --> S --> GW --> LB --> C
The takeaway for interviews: a "slow API" is rarely one thing. It could be DNS, TLS handshakes, a cold load balancer, gateway auth, the service logic, or a slow query (Module 03). Naming the stages shows you can diagnose systematically instead of guessing.
5REST vs GraphQL vs gRPC
Three ways to shape an API, each solving a different pain.
| REST | GraphQL | gRPC | |
|---|---|---|---|
| Shape | Resources + verbs | Single endpoint, query language | RPC over HTTP/2 + Protobuf |
| Solves | Simplicity, caching | Over/under-fetching | Speed, typed contracts |
| Cost | Many round-trips | Complexity, caching is hard | Not browser-native, binary |
| Sweet spot | Public, cacheable APIs | Rich frontends, varied clients | Internal microservices |
GraphQL lets the client ask for exactly the fields it needs in one request — killing the REST problems of over-fetching (too much data) and under-fetching (N calls to assemble a screen). gRPC uses binary Protobuf over HTTP/2 for low-latency, strongly-typed service-to-service calls. The honest summary: REST for public/cacheable APIs, GraphQL for complex frontends with diverse data needs, gRPC for fast internal microservice communication.
Interview angle
"REST is simple and cacheable; GraphQL fixes over- and under-fetching for rich clients but makes caching harder; gRPC is fast, typed, and binary — great between internal services but not browser-native. I choose by client and performance needs."
6Real-time: WebSockets & SSE
Plain HTTP is request-response: the client asks, the server answers, done. Real-time features need the server to push. Two main tools:
- WebSockets — a persistent, full-duplex connection: both sides send messages any time. Ideal for chat, multiplayer, collaborative editing — anything bidirectional.
- Server-Sent Events (SSE) — a one-way stream from server to client over plain HTTP. Simpler than WebSockets and great for notifications, live feeds, or streaming LLM tokens, where the client only needs to listen.
Before either, the old hack was polling (ask repeatedly) or long-polling. Mentioning that you'd reach for SSE over WebSockets when traffic is one-directional shows you pick the simplest tool that fits — a senior instinct.
Go deeper
Streaming LLM responses token-by-token is a textbook SSE use case — one-way, incremental, over standard HTTP. If the role is GenAI-flavoured, that's a sharp, relevant example to drop.
7Pagination, filtering, rate limiting
These three keep an API usable at scale. Pagination stops a list endpoint returning a million rows: offset-based (?page=3) is simple but drifts when data changes mid-scroll; cursor-based (a pointer to the last item seen) is stable and performs better on large datasets — the senior choice for infinite scroll.
Filtering, sorting, and field selection via query params let clients fetch just what they need. Rate limiting protects the service from abuse and runaway clients — a common scheme is a token bucket per API key, returning 429 Too Many Requests with a Retry-After header when exceeded. It's both a stability and a fairness mechanism.
Interview angle
"For large lists I prefer cursor-based pagination over offset — it's stable when rows are inserted mid-scroll and faster at depth. And I'd rate-limit per key with a token bucket, returning 429 with Retry-After so clients can back off gracefully."
8API gateways
In a microservices world, an API gateway is the single front door. Instead of clients talking to dozens of services directly, they hit the gateway, which routes each request to the right service and handles the cross-cutting concerns in one place: authentication, rate limiting, TLS termination, request logging, caching, and sometimes response aggregation.
The value is DRY infrastructure — you implement auth and rate limiting once at the edge rather than in every service, and clients get one stable endpoint while the services behind it can change freely. It's the network-level expression of the same "centralise the cross-cutting concern" instinct you saw with middleware and encapsulation.
9CORS — the one everyone gets wrong
CORS (Cross-Origin Resource Sharing) is misunderstood constantly, so nail it. The browser enforces a same-origin policy: by default, JavaScript on site-a.com can't read responses from api-b.com. CORS is the mechanism by which a server opts in to allowing specific other origins, via response headers like Access-Control-Allow-Origin.
The two things people miss: CORS is enforced by the browser, not the server (a server-to-server call or curl ignores it entirely), and it's a relaxation of security, not a restriction — the server decides who's allowed. For non-simple requests the browser first sends a preflight OPTIONS request to ask permission before the real one.
Common trap
"I got a CORS error so the API is down" — no. A CORS error means the browser blocked the response because the server didn't grant your origin permission. The fix is server-side headers, and it only ever affects browser-based callers.
Interview angle
"CORS is a browser-enforced policy where the server opts in to which origins may read its responses. It's not a server-side block and it doesn't apply to server-to-server calls — and non-simple requests get a preflight OPTIONS first."
Recap — what you can now teach
- HTTP verbs carry semantic promises; status-code families matter more than memorising codes (401 ≠ 403).
- REST = resources, verbs, and statelessness — which is what enables horizontal scale.
- Idempotency + idempotency keys make retries safe; version to never break existing clients.
- REST for cacheable APIs, GraphQL for over/under-fetching, gRPC for fast internal services.
- WebSockets for bidirectional, SSE for one-way streams; cursor pagination + rate limiting at scale.
- Gateways centralise cross-cutting concerns; CORS is a browser-enforced server opt-in.
Self-check
Say each answer out loud before revealing it.
What's the difference between 401 and 403?
401 = not authenticated ("I don't know who you are"); 403 = authenticated but not authorised ("I know you, you're not allowed").
Why does statelessness let REST scale horizontally?
Each request is self-contained and the server holds no session, so any instance can handle any request — you can add servers freely behind a load balancer.
When would you pick GraphQL over REST?
When diverse clients need different field sets and REST causes over- or under-fetching — GraphQL lets each client request exactly what it needs in one round-trip.
Who enforces CORS, and what does it actually do?
The browser enforces it. It's the server opting in (via headers) to let specific other origins read its responses; it doesn't affect server-to-server calls.
How do you make a payment POST safe to retry?
Use an idempotency key the server records, so a retried request with the same key is recognised and not processed twice.