All chapters in this track
- 01Chapter 1: Why Networking Fundamentals Matter
- 02Chapter 2: The OSI Model and TCP/IP Stack, at a Working Level
- 03Chapter 3: Sockets, Ports, and the Connection Lifecycle
- 04Chapter 4: TCP vs UDP: Choosing the Right Transport
- 05Chapter 5: DNS Resolution, Step by Step
- 06Chapter 6: The HTTP Family: HTTP/1.1, HTTP/2, HTTP/3, and WebSockets
- 07Chapter 7: TLS/SSL and Certificates: The Handshake and Why HTTPS Matters
- 08Chapter 8: Load Balancers, Reverse Proxies, and CDNs
- 09Chapter 9: Latency, Bandwidth, and Congestion Control
- 10Chapter 10: API Protocol Choices: REST vs. gRPC vs. GraphQL, from a Networking Angle
- 11Chapter 11: Common Failure Modes: Timeouts, Connection Resets, Retry Storms, Thundering Herd
- 12Chapter 12: Where Networking Abstractions Leak: NAT, Middleboxes, and Other Surprises
- 13Chapter 13: Full Walkthrough: What Happens When You Type a URL and Press Enter
- 14Chapter 14: Common Networking Interview Questions
- 15Chapter 15: Networking Concepts in System Design and Production Debugging
Chapter 10 of 15
Chapter 10: API Protocol Choices: REST vs. gRPC vs. GraphQL, from a Networking Angle
Why this matters
REST, gRPC, and GraphQL are usually compared on ergonomics and developer experience — but each one makes distinct, consequential choices about how it uses the network underneath, and those choices explain real performance and operational differences that a purely API-design-level comparison misses. This chapter looks specifically at the networking angle: what's on the wire, and why it matters.
REST: HTTP semantics, human-readable payloads
[BEGINNER] REST (Representational State Transfer) is an architectural style built directly on HTTP's existing semantics — HTTP methods (GET, POST, PUT, DELETE) map to actions, status codes communicate outcomes, and payloads are typically JSON, sent over whichever HTTP version the client and server negotiate (Chapter 6).
From a networking angle: REST's JSON payloads are text-based and human-readable, which is a real ergonomic win (easy to inspect with curl, easy to debug in browser dev tools) but a real bandwidth cost — JSON's verbosity (repeated field names in every object, no compact binary encoding) means more bytes on the wire for the same logical data compared to a binary format. REST typically makes one HTTP request per logical operation, which (especially pre-HTTP/2) could mean many separate round trips for a page needing several related pieces of data — a problem often solved at the application level by combining data into fewer, larger responses, or by a client making requests in parallel (helped significantly by HTTP/2's multiplexing, Chapter 6).
gRPC: binary, HTTP/2-native, contract-first
[BEGINNER] gRPC is built specifically on top of HTTP/2 (not an incidental choice — it depends on HTTP/2's multiplexing and streaming support directly) and uses Protocol Buffers (protobuf), a compact binary serialization format, instead of JSON.
From a networking angle: protobuf's binary encoding is significantly smaller on the wire than equivalent JSON — field names aren't repeated in every message (a schema, defined once, tells both sides what each field number means), and numeric/structured data is encoded compactly rather than as text. Because gRPC is built directly on HTTP/2, it gets multiplexing and true bidirectional streaming natively — a gRPC service can support client-streaming, server-streaming, or full bidirectional streaming RPCs as first-class features, not a workaround bolted onto a request/response model.
Same logical message, roughly:
JSON (REST): {"user_id": 12345, "name": "Alice", "active": true} <- ~55 bytes, human-readable
Protobuf (gRPC): [binary-encoded equivalent] <- often 15-20 bytes for the same data
Trade-off: the compact binary format and native streaming are real networking wins, but at a real cost — protobuf payloads aren't human-readable on the wire (you can't just eyeball a tcpdump capture the way you can inspect JSON), and gRPC's browser support historically required a proxy layer (gRPC-Web) since browsers didn't expose the low-level HTTP/2 features gRPC needs directly — making gRPC a much more natural fit for service-to-service (backend-to-backend) communication than for public, browser-facing APIs.
GraphQL: one endpoint, client-specified queries
[BEGINNER] GraphQL takes a different approach to the networking problem entirely: rather than exposing many separate REST-style endpoints (each returning a fixed shape of data), GraphQL typically exposes one single endpoint, and the client specifies exactly which fields it needs in the request itself — the server returns precisely that shape, no more and no less.
From a networking angle: this directly targets the "REST often needs multiple round trips, or returns more data than the client actually needs" problem — a single GraphQL query can fetch exactly the fields needed from what would otherwise be several separate REST resources, in one HTTP request/response. This is a genuine reduction in both round trips and over-fetched data, compared to a naive multi-endpoint REST design. Under the hood, GraphQL still typically runs over standard HTTP (usually POST requests to that one endpoint), so it doesn't change the underlying transport story the way gRPC's HTTP/2 dependency does — the win is specifically in how much is requested/returned per exchange, not in the transport mechanism itself.
Trade-off: a single flexible endpoint makes HTTP-level caching (which typically keys on URL, per Chapter 8's CDN discussion) much harder to apply naively, since every distinct query is technically the same URL/endpoint with different content — GraphQL-specific caching strategies exist, but they're not "free" the way caching a REST GET request by its URL is.
Comparing all three, from purely the networking angle
| REST | gRPC | GraphQL | |
|---|---|---|---|
| Payload format | JSON (text, larger) | Protobuf (binary, compact) | Typically JSON (text, larger) |
| Transport | Any HTTP version | HTTP/2 specifically (required) | Typically standard HTTP |
| Round trips for related data | Often multiple (unless designed carefully) | Multiple, but streaming can reduce this | Typically one (client specifies exactly what's needed) |
| Native streaming support | No (WebSockets/SSE needed separately) | Yes, built-in (client/server/bidirectional) | No (subscriptions exist but are a separate mechanism, often over WebSockets) |
| Browser-friendliness | High (native fetch/XHR support) | Lower (needs gRPC-Web proxy historically) | High (standard HTTP POST) |
| HTTP-level caching fit | Good (GET requests cache naturally by URL) | N/A (not typically used this way) | Poor (single endpoint defeats naive URL-based caching) |
Common mistakes
- Choosing gRPC for a public, browser-facing API without accounting for its historical browser support limitations — it's an excellent fit for internal service-to-service communication specifically because that environment fully controls both ends of the connection.
- Assuming GraphQL's single-endpoint flexibility is "free" from a networking/caching perspective — the same flexibility that avoids REST's over-fetching problem also complicates straightforward HTTP/CDN-level caching that a REST GET endpoint gets close to for free.
- Not recognizing that JSON's human-readability is a genuine, deliberate trade-off, not just an inferior legacy choice — it costs bandwidth but buys debuggability (any engineer can read a JSON payload directly in
curl -voutput or browser dev tools) that binary protobuf payloads don't offer without additional tooling. - Assuming REST inherently requires many round trips. With HTTP/2 multiplexing (Chapter 6) and thoughtful API design (e.g., allowing clients to request related resources together), REST's round-trip cost can be substantially mitigated, even if it's not as structurally addressed as GraphQL's single-query model.
Discussion / interview questions
-
"From a purely networking perspective, what's the main trade-off between REST's JSON payloads and gRPC's protobuf payloads?" Strong answer: JSON is text-based and human-readable but more verbose (more bytes on the wire for equivalent data); protobuf is a compact binary format requiring a predefined schema, significantly reducing payload size at the cost of the payload no longer being directly human-readable without tooling.
-
"Why is gRPC built specifically on HTTP/2, and what does that dependency buy it?" Strong answer: gRPC relies on HTTP/2's native multiplexing and streaming support to provide first-class client-streaming, server-streaming, and bidirectional-streaming RPCs — capabilities that would otherwise need to be bolted on (like WebSockets alongside REST) rather than being a built-in part of the protocol itself.
-
"How does GraphQL address REST's tendency toward multiple round trips or over-fetched data, and what does it give up in exchange?" Strong answer: A single GraphQL query lets the client specify exactly which fields it needs across what would be several REST resources, fetched in one request/response; in exchange, GraphQL's single, flexible endpoint makes straightforward HTTP/CDN-level URL-based caching much harder to apply compared to REST's cacheable GET endpoints.
-
"Why might gRPC be a poor choice for a public, browser-facing API even though it offers real networking advantages?" Strong answer: Browsers historically don't expose the low-level HTTP/2 features gRPC needs directly, requiring an additional proxy layer (gRPC-Web) to work at all in a browser — making gRPC a much more natural fit for backend-to-backend communication where both ends are fully controlled.
Chapter summary / cheat sheet
- REST: HTTP-native semantics, JSON payloads (larger, human-readable), works over any HTTP version, cache-friendly via standard URL-based HTTP caching.
- gRPC: HTTP/2-dependent, protobuf payloads (compact, binary, schema-required), native streaming support — excellent for service-to-service communication, weaker fit for public browser-facing APIs.
- GraphQL: single flexible endpoint, client-specified field selection, reduces round trips and over-fetching compared to naive REST — at the cost of straightforward HTTP-level caching.
- None of the three is strictly "better" from a networking angle — each makes a different, deliberate trade-off between payload size, round-trip efficiency, debuggability, and cacheability, and the right choice depends on which of those actually matters most for a given system's constraints.