Why API design matters, and how to choose
Every system is made of parts that talk to each other: a phone app and a server, one service and another. The API (application programming interface) is the agreement about how: what you can ask for, what you send, and what comes back. Once other people's code depends on it, it is hard to change, so it is one of the most important things to get right.
A good API and a good choice of protocol give you two things interviewers always ask about:
- A clear, stable contract: clients and servers can change independently without breaking each other.
- Efficient communication: few round trips and small payloads, so screens load fast even on slow networks.
Choose REST when:
- The API is public or used by many different clients, and the data maps to resources (users, orders).
- You want to use HTTP caching and simple tools.
Choose GraphQL when:
- Clients such as mobile apps need different shapes of data and you want them to ask for exactly what they need in one trip.
Choose gRPC when:
- Your own services call each other a lot, and you want a strict typed contract, small binary messages and streaming.
Choose something else when:
- You need the server to push updates (chat, live scores): use WebSockets or server-sent events.
- A single simple endpoint is all you need: a plain REST endpoint is enough. Do not add GraphQL or gRPC for the sake of it.
Try it
Start with the API styles tab: pick a style and a network, and watch how long one profile screen takes to load. Then open the TCP vs UDP tab.
Loading one profile screen
Step 1 of 5 · Press Space to play, arrow keys to step- No requests yet.
- User: not yet
- Posts: not yet
- Followers: not yet
Start: a profile screen needs the user's name and photo, their latest three post titles, and a follower count, about 1,100 bytes in all. The round trip to the server is 150 ms. Press play to see the requests.
- round trips:
- 0
- bytes received:
- 0
- bytes the screen needs:
- 1,100
- page ready (ms):
- waiting
- parts failed:
- 0
Teaching numbers, not measurements: each request costs one round trip plus 5 ms of server time, and the link moves about 1 KB per ms. The point is the shape: round trips dominate on a slow network.
Things to try:
- REST (one call per resource) on the poor mobile network: three round trips and 8,300 bytes of data for a screen that needs about 1,100. The page is ready after 474 ms (teaching numbers).
- Switch to GraphQL, or REST with a /profile endpoint: one round trip, so the page is ready after 157 ms. Now try the good network: the gap shrinks to 84 ms against 27 ms. Round trips hurt most when the network is slow.
- Tick The posts service is failing. With plain REST and GraphQL, the user and follower count still show (2 of 3 parts). With a single /profile call or gRPC call that fails as a whole, the screen gets nothing. One big call is faster, and also all-or-nothing unless you design for partial results.
- On the TCP vs UDP tab, lose packets 3 and 7. TCP resends them, delivers all 10 in order, and takes 300 ms instead of 100. UDP takes 100 ms and delivers 8 of 10, with gaps.
API styles
| Style | How it works | Good when | Weakness |
|---|---|---|---|
| REST over HTTP | Resources at URLs (/users/42), standard methods (GET, POST, PUT, DELETE) and status codes, usually JSON | Public APIs, simple CRUD, cacheable reads | Several calls for one screen; over-fetching and under-fetching |
| GraphQL | One endpoint; the client sends a query naming the fields it wants | Many client types, nested data, mobile apps | More server complexity; caching and query cost are harder; N+1 risk |
| gRPC | Typed methods defined in a .proto file, binary messages over HTTP/2, with streaming | Service-to-service calls, low latency, strict contracts | Not directly usable from a plain browser; harder to read and debug |
| WebSockets or server-sent events | A long-lived connection the server can send messages down | Chat, live updates, notifications | Connections to keep alive and scale; more stateful |
TCP vs UDP
- TCP gives you a reliable, ordered stream. It sets up a connection, numbers the data, and resends anything lost. Web pages, APIs, files and databases use it. The cost: a setup step (the handshake) and waiting when a packet is lost, which delays everything behind it (head-of-line blocking).
- UDP sends independent packets with no setup, no resends and no ordering. It has less delay and fewer guarantees. Live video and voice, games and DNS lookups use it, where a late packet is worth less than a fresh one.
HTTP/1.1 and HTTP/2 run over TCP. HTTP/3 runs over QUIC, which is built on UDP and adds its own reliability, to avoid some of TCP's blocking.
How a request travels
Before the first byte of an API response arrives, several things happen, each of which can cost a round trip:
- DNS lookup: turn the name (
api.example.com) into an IP address, unless it is cached. - TCP handshake: client and server agree to talk, which costs one round trip.
- TLS handshake: agree on encryption for HTTPS, which with TLS 1.3 costs another round trip.
- HTTP request and response: send the request and wait for the answer.
That is why reusing connections (keep-alive, HTTP/2) and fewer, bigger calls matter. And why a distant server or a mobile network makes every extra round trip hurt.
Designing a good API
- Use nouns for resources (
/orders/17) and let the HTTP method be the verb. Avoid/getOrder. - Use the right status codes: 200 success, 201 created, 400 bad request, 401 and 403 for access, 404 not found, 429 too many requests, 500 server error. Do not return 200 with an error inside.
- Paginate every list, with a limit and a cursor or page, so a response cannot grow without bound.
- Make retries safe. GET, PUT and DELETE should be idempotent. For POST (create or pay), accept an idempotency key so a retried request does not create two orders.
- Version your API (
/v1/, or a header) and add fields rather than change or remove them. - Set timeouts and rate limits, and return clear, consistent errors.
REST vs GraphQL vs gRPC
| Concern | REST | GraphQL | gRPC |
|---|---|---|---|
| Works directly from a browser | Yes | Yes | Needs a proxy (gRPC-Web) |
| Fits HTTP caching | Yes, GET | Harder | No |
| Exact data in one trip | No, unless you add a custom endpoint | Yes | Yes, by design of the method |
| Strict typed contract | Optional (OpenAPI) | Yes (schema) | Yes (proto) |
| Message size and speed | Larger (JSON text) | Medium | Smallest and fastest |
| Streaming | Not natively | Subscriptions | Yes |
| Best for | Public and simple APIs | Flexible client screens | Internal service calls |
Many companies use all three: REST or GraphQL at the edge for apps and partners, and gRPC between internal services.
Avoiding a new single point of failure
The API is the front door, and a few things can make it fragile:
- A single API server or gateway. Run several behind a load balancer, and keep the gateway itself redundant.
- One slow dependency. Without timeouts, one slow service makes every caller hang. Set short timeouts and fall back or fail fast.
- Retries without limits. Unlimited retries from many clients can turn a small failure into an overload (a retry storm). Use backoff, a retry limit and idempotency keys.
- Breaking changes. A change that breaks old clients is an outage you caused. Version the API and deprecate slowly.
The trade-off: more layers (gateway, versions, limits) in return for stability and protection.
Common mistakes
- Breaking existing clients. Renaming or removing a field breaks apps you cannot update. Add, do not change, and version when you must.
- Verbs in URLs and wrong status codes.
/getUserand a 200 with an error body confuse clients and caches. Use resources and real status codes. - No pagination. One request returns millions of rows and takes the server down. Always page lists.
- Retrying non-idempotent calls. A retried payment charges twice. Use an idempotency key.
- A chatty API. One screen needs ten calls, and a slow network makes it crawl. Offer a combined endpoint or a query that returns what the screen needs.
- No timeouts. One slow service freezes everything that calls it. Set timeouts everywhere.
How to talk about API design in an interview
- State the clients and what they need, then pick REST, GraphQL or gRPC and say why.
- List the resources and endpoints, the status codes, pagination, and how you version.
- Cover safe retries with idempotency keys, and timeouts and rate limits.
- Say how many round trips a main screen needs and how you keep that low.
- Name the trade-off: for example, GraphQL's flexibility against caching and server complexity.