{"id":"protocols-and-apis","title":"Network protocols and APIs","sections":[{"id":"overview","title":"Overview","html":"<p>This chapter answers five questions:</p>\n<ul>\n<li>What happens between \"the app sends a request\" and \"the server gets it\", and what are the layers people refer to?</li>\n<li>What does HTTP promise, and what do its methods and status codes mean?</li>\n<li>When should data travel over TCP, and when over UDP?</li>\n<li>How do RPC and REST differ as ways to design an API, and what does each cost?</li>\n<li>Where do gRPC and GraphQL fit, and how do you keep an API usable as it changes?</li>\n</ul>\n<p>We'll keep using <strong>Crumb</strong>, the recipe site from the <a href=\"/learn/scalability\">Scalability</a> chapter. Crumb now has a website, a mobile app, app servers behind a load balancer, a memcached layer and a database. Every arrow between those boxes is a conversation between two programs on different machines. The rules for such a conversation are called a <strong>protocol</strong>. The set of requests a server agrees to answer is called its <strong>API</strong> (application programming interface).</p>","headings":[]},{"id":"layers-how-one-request-is-built","title":"Layers: how one request is built","html":"<h3 id=\"layers-how-one-request-is-built\">Layers: how one request is built</h3>\n<p>Think of a letter sent inside several envelopes. You write the letter and put it in an envelope addressed to a person. The mailroom puts that inside a bigger envelope addressed to their building. The postal service adds labels to get it to the right city. Each handler reads only its own envelope and passes the rest along.</p>\n<p>Network data is wrapped the same way. When Crumb's app asks for a recipe, the request gets several layers of wrapping on the way out, and the server unwraps them in reverse order. The best-known description of these layers is the <strong>OSI model</strong> (Open Systems Interconnection), published in 1984 as standard ISO 7498 (<a href=\"https://en.wikipedia.org/wiki/OSI_model\" target=\"_blank\" rel=\"noopener noreferrer\">Wikipedia</a>). It has seven layers, numbered from the wire upward.</p>\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n<div class=\"table-wrap\"><table><thead><tr><th>#</th><th>Layer</th><th>What it does</th><th>Unit of data</th><th>Examples</th></tr></thead><tbody><tr><td>7</td><td>Application</td><td>The conversation the program cares about, such as \"give me recipe 456\"</td><td>Data</td><td>HTTP, DNS, SMTP, SSH, DHCP</td></tr><tr><td>6</td><td>Presentation</td><td>Puts data in a shared format: character encoding, compression, encryption</td><td>Data</td><td>JSON, gzip, TLS (partly)</td></tr><tr><td>5</td><td>Session</td><td>Opens, manages and closes a longer conversation</td><td>Data</td><td>Sockets, RPC</td></tr><tr><td>4</td><td>Transport</td><td>Carries data from a program on one machine to a program on another, using port numbers</td><td>Segment (TCP) or datagram (UDP)</td><td>TCP, UDP, QUIC</td></tr><tr><td>3</td><td>Network</td><td>Gives machines addresses and routes data across many networks</td><td>Packet</td><td>IP, ICMP</td></tr><tr><td>2</td><td>Data link</td><td>Moves frames between machines on the same local network</td><td>Frame</td><td>Ethernet, Wi-Fi</td></tr><tr><td>1</td><td>Physical</td><td>Turns bits into signals on a cable, a fiber or a radio wave</td><td>Bit</td><td>Copper, fiber, radio</td></tr></tbody></table></div>\n<p>Two warnings make the table more useful.</p>\n<ul>\n<li><strong>The internet uses a simpler model.</strong> Its protocols weren't designed to fit OSI. <a href=\"https://www.rfc-editor.org/rfc/rfc1122\" target=\"_blank\" rel=\"noopener noreferrer\">RFC 1122</a> (1989) describes four layers: link, internet (IP), transport (TCP or UDP) and application. Its application layer \"essentially combines the functions of the top two layers\" of OSI, and Wikipedia adds most of the session layer too. In practice, people use the OSI numbers as labels and treat 5 to 7 as one layer.</li>\n<li><strong>Some protocols sit between layers.</strong> TLS, the encryption behind HTTPS, does presentation-layer work but runs on top of a transport connection. Wikipedia says it \"does not strictly fit inside the model.\"</li>\n</ul>\n<p>In system design you'll hear the numbers most often about load balancers. A <strong>layer 4</strong> load balancer routes by IP address and port without reading the request. A <strong>layer 7</strong> load balancer reads the HTTP request, so it can send <code>/api</code> to one pool of servers and <code>/images</code> to another. <a href=\"/learn/scalability#load-balancing\">Scalability</a> covers both.</p>","headings":[{"id":"layers-how-one-request-is-built","title":"Layers: how one request is built","depth":3}]},{"id":"http-requests-and-responses","title":"HTTP: requests and responses","html":"<h3 id=\"http-requests-and-responses\">HTTP: requests and responses</h3>\n<p><strong>HTTP</strong> (Hypertext Transfer Protocol) is a way to encode data and carry it between a client and a server. It is a <strong>request/response</strong> protocol. The client sends a request. The server sends back a response with the content asked for and a status code that says how the request went.</p>\n<p>Here is Crumb's app asking for a recipe, followed by the server's answer:</p>\n<pre class=\"shiki shiki-themes github-light github-dark\" style=\"background-color:#fff;--shiki-dark-bg:#24292e;color:#24292e;--shiki-dark:#e1e4e8\" tabindex=\"0\"><code><span class=\"line\"><span style=\"color:#D73A49;--shiki-dark:#F97583\">GET</span><span style=\"color:#24292E;--shiki-dark:#E1E4E8\"> /recipes/456 </span><span style=\"color:#D73A49;--shiki-dark:#F97583\">HTTP</span><span style=\"color:#24292E;--shiki-dark:#E1E4E8\">/</span><span style=\"color:#005CC5;--shiki-dark:#79B8FF\">1.1</span></span>\n<span class=\"line\"><span style=\"color:#22863A;--shiki-dark:#85E89D\">Host</span><span style=\"color:#D73A49;--shiki-dark:#F97583\">:</span><span style=\"color:#032F62;--shiki-dark:#9ECBFF\"> api.crumb.example</span></span>\n<span class=\"line\"><span style=\"color:#22863A;--shiki-dark:#85E89D\">Accept</span><span style=\"color:#D73A49;--shiki-dark:#F97583\">:</span><span style=\"color:#032F62;--shiki-dark:#9ECBFF\"> application/json</span></span>\n<span class=\"line\"></span>\n<span class=\"line\"><span style=\"color:#D73A49;--shiki-dark:#F97583\">HTTP</span><span style=\"color:#24292E;--shiki-dark:#E1E4E8\">/</span><span style=\"color:#005CC5;--shiki-dark:#79B8FF\">1.1</span><span style=\"color:#005CC5;--shiki-dark:#79B8FF\"> 200</span><span style=\"color:#032F62;--shiki-dark:#9ECBFF\"> OK</span></span>\n<span class=\"line\"><span style=\"color:#22863A;--shiki-dark:#85E89D\">Content-Type</span><span style=\"color:#D73A49;--shiki-dark:#F97583\">:</span><span style=\"color:#032F62;--shiki-dark:#9ECBFF\"> application/json</span></span>\n<span class=\"line\"><span style=\"color:#22863A;--shiki-dark:#85E89D\">Cache-Control</span><span style=\"color:#D73A49;--shiki-dark:#F97583\">:</span><span style=\"color:#032F62;--shiki-dark:#9ECBFF\"> max-age=60</span></span>\n<span class=\"line\"></span>\n<span class=\"line\"><span style=\"color:#24292E;--shiki-dark:#E1E4E8\">{</span><span style=\"color:#005CC5;--shiki-dark:#79B8FF\">\"id\"</span><span style=\"color:#24292E;--shiki-dark:#E1E4E8\">: </span><span style=\"color:#032F62;--shiki-dark:#9ECBFF\">\"456\"</span><span style=\"color:#24292E;--shiki-dark:#E1E4E8\">, </span><span style=\"color:#005CC5;--shiki-dark:#79B8FF\">\"title\"</span><span style=\"color:#24292E;--shiki-dark:#E1E4E8\">: </span><span style=\"color:#032F62;--shiki-dark:#9ECBFF\">\"Lemon drizzle cake\"</span><span style=\"color:#24292E;--shiki-dark:#E1E4E8\">, </span><span style=\"color:#005CC5;--shiki-dark:#79B8FF\">\"authorId\"</span><span style=\"color:#24292E;--shiki-dark:#E1E4E8\">: </span><span style=\"color:#032F62;--shiki-dark:#9ECBFF\">\"1234\"</span><span style=\"color:#24292E;--shiki-dark:#E1E4E8\">}</span></span></code></pre>\n<p>The first line holds the two essentials: a <strong>method</strong>, also called a verb (<code>GET</code>), and a <strong>resource</strong>, also called an endpoint (<code>/recipes/456</code>). The lines after it are <strong>headers</strong>, which carry extra information such as the format the client wants. A request can also carry a <strong>body</strong>, as a <code>POST</code> usually does.</p>\n<h4 id=\"every-message-stands-on-its-own\">Every message stands on its own</h4>\n<p>HTTP is <strong>stateless</strong>. <a href=\"https://www.rfc-editor.org/rfc/rfc9110\" target=\"_blank\" rel=\"noopener noreferrer\">RFC 9110</a>, the current definition of HTTP, says \"each request message's semantics can be understood in isolation.\" The server doesn't need to remember earlier requests to understand this one. If Crumb needs to know who is logged in, each request carries that itself, usually in a cookie or a token.</p>\n<p>Because each message is self-contained, other machines can sit between client and server and act on it. RFC 9110 calls them <strong>intermediaries</strong>. A load balancer can send each request to a different server. A cache can answer a repeated <code>GET</code> itself. A proxy can encrypt or compress traffic. The RFC notes that \"many implementations depend on HTTP's stateless design in order to reuse proxied connections or dynamically load balance requests across multiple servers.\" That is why the stateless app servers from the Scalability chapter fit HTTP so well.</p>\n<h4 id=\"methods-and-their-properties\">Methods and their properties</h4>\n<p>Three properties, defined in <a href=\"https://www.rfc-editor.org/rfc/rfc9110#section-9.2\" target=\"_blank\" rel=\"noopener noreferrer\">RFC 9110 section 9.2</a>, decide how clients, caches and proxies may treat a method:</p>\n<ul>\n<li><strong>Safe</strong> means read-only. The client isn't asking for anything to change, so a search engine crawler can follow safe links without harm.</li>\n<li><strong>Idempotent</strong> means sending the same request many times has the same intended effect as sending it once. This is what makes a request safe to retry after a timeout.</li>\n<li><strong>Cacheable</strong> means a cache may store the response and reuse it for later requests.</li>\n</ul>\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n<div class=\"table-wrap\"><table><thead><tr><th>Method</th><th>What it does</th><th>Idempotent</th><th>Safe</th><th>Cacheable</th></tr></thead><tbody><tr><td>GET</td><td>Reads a resource</td><td>Yes</td><td>Yes</td><td>Yes</td></tr><tr><td>POST</td><td>Creates a resource, or starts a process that handles the data sent</td><td>No</td><td>No</td><td>Yes if the response contains freshness info</td></tr><tr><td>PUT</td><td>Creates a resource or replaces it completely</td><td>Yes</td><td>No</td><td>No</td></tr><tr><td>PATCH</td><td>Changes part of a resource</td><td>No</td><td>No</td><td>Yes if the response contains freshness info</td></tr><tr><td>DELETE</td><td>Deletes a resource</td><td>Yes</td><td>No</td><td>No</td></tr></tbody></table></div>\n<p>These values follow RFC 9110 and RFC 5789. Four details are worth knowing:</p>\n<ol>\n<li><strong>Cacheable POST and PATCH responses are rare.</strong> RFC 9110 lets a cache store a POST response only if it has explicit freshness information <em>and</em> a <code>Content-Location</code> header equal to the URL that was posted to. Even then, the copy can answer a later <code>GET</code> or <code>HEAD</code>, never another <code>POST</code>. <a href=\"https://www.rfc-editor.org/rfc/rfc5789\" target=\"_blank\" rel=\"noopener noreferrer\">RFC 5789</a> sets the same rule for PATCH. RFC 9110 also notes that \"the overwhelming majority of cache implementations only support GET and HEAD.\" In practice, treat only GET as cacheable.</li>\n<li><strong>Idempotent doesn't mean the same response.</strong> Deleting recipe 456 twice leaves the same state as deleting it once. The first call might return <code>204 No Content</code> and the second <code>404 Not Found</code>. RFC 9110 says the effect is the same \"though the response might differ.\"</li>\n<li><strong>PATCH can be idempotent.</strong> RFC 5789 says PATCH \"is neither safe nor idempotent\" in general, because \"add 1 to the like count\" changes the result each time. \"Set the title to X\" is idempotent. A client can also make a PATCH conditional, so it fails if the resource changed since the client last read it.</li>\n<li><strong>PUT and DELETE clear caches.</strong> Their responses are never cached, and when one succeeds, caches it passes through mark their stored copy of that URL as out of date.</li>\n</ol>\n<p>Two more methods are worth knowing. <code>HEAD</code> is a <code>GET</code> that returns only headers, and <code>OPTIONS</code> asks which methods a resource supports. Both are safe.</p>\n<p>Idempotence matters because RFC 9110 says a client \"SHOULD NOT automatically retry a request with a non-idempotent method\" unless it knows the retry is harmless. If Crumb's app sends <code>POST /recipes</code> and the connection drops before the reply, the app can't tell whether the recipe was saved. A retry might create a duplicate. <a href=\"/learn/protocols-and-apis#idempotency-keys-make-post-safe-to-retry\">Idempotency keys</a>, later in this chapter, fix that. How caches decide what to store and for how long, using <code>Cache-Control</code> and <code>ETag</code>, is covered in the <a href=\"/learn/dns-and-cdn#http-caching-headers\">DNS and CDNs</a> chapter.</p>\n<h4 id=\"status-codes\">Status codes</h4>\n<p>Every response has a three-digit status code. RFC 9110 says \"the first digit of the status code defines the class of response\":</p>\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n<div class=\"table-wrap\"><table><thead><tr><th>Class</th><th>Meaning</th><th>Common codes</th></tr></thead><tbody><tr><td>1xx</td><td>Informational: received, still working</td><td>101 Switching Protocols</td></tr><tr><td>2xx</td><td>Success</td><td>200 OK, 201 Created, 204 No Content</td></tr><tr><td>3xx</td><td>Redirection: the client must take another step</td><td>301 Moved Permanently, 304 Not Modified</td></tr><tr><td>4xx</td><td>Client error: the request is wrong or can't be fulfilled</td><td>400 Bad Request, 401 Unauthorized, 403 Forbidden, 404 Not Found, 409 Conflict, 429 Too Many Requests</td></tr><tr><td>5xx</td><td>Server error: a valid request failed on the server</td><td>500 Internal Server Error, 502 Bad Gateway, 503 Service Unavailable</td></tr></tbody></table></div>\n<p>Two pairs are often confused. <strong>401</strong> means the request \"lacks valid authentication credentials\", so the client should log in. <strong>403</strong> means the server understood the request and \"refuses to fulfill it.\" <strong>429</strong> comes from <a href=\"https://www.rfc-editor.org/rfc/rfc6585\" target=\"_blank\" rel=\"noopener noreferrer\">RFC 6585</a> and means the client is being rate limited. <a href=\"/learn/rate-limiting#telling-the-client\">Rate limiting</a> covers how to tell clients that.</p>\n<p>The class tells a client whether to retry. A 400 will fail the same way every time. A 503 might succeed later.</p>\n<h4 id=\"http-needs-a-transport-underneath\">HTTP needs a transport underneath</h4>\n<p>HTTP is an application-layer protocol. It defines what messages mean and leaves moving the bytes to a lower-level <strong>transport</strong> protocol. For most of its history that has been <strong>TCP</strong>. Since HTTP/3 it can also be <strong>QUIC</strong>, which runs on <strong>UDP</strong>. The next sections cover those transports and then how the HTTP versions use them.</p>","headings":[{"id":"http-requests-and-responses","title":"HTTP: requests and responses","depth":3},{"id":"every-message-stands-on-its-own","title":"Every message stands on its own","depth":4},{"id":"methods-and-their-properties","title":"Methods and their properties","depth":4},{"id":"status-codes","title":"Status codes","depth":4},{"id":"http-needs-a-transport-underneath","title":"HTTP needs a transport underneath","depth":4}]},{"id":"tcp-reliable-delivery-with-a-price","title":"TCP: reliable delivery, with a price","html":"<h3 id=\"tcp-reliable-delivery-with-a-price\">TCP: reliable delivery, with a price</h3>\n<p><strong>TCP</strong> (Transmission Control Protocol) is like registered mail with tracking. Both sides agree to talk before anything is sent. Every piece is numbered, the receiver signs for what arrives, and anything lost is sent again. The receiving program gets all the data in the right order, or learns that the connection failed.</p>\n<p>TCP is <strong>connection-oriented</strong> and runs over <strong>IP</strong> (Internet Protocol), which moves single packets between machines with no delivery promise. <a href=\"https://www.rfc-editor.org/rfc/rfc9293\" target=\"_blank\" rel=\"noopener noreferrer\">RFC 9293</a>, the current TCP standard, sums up what TCP adds: \"a reliable, in-order, byte-stream service to applications.\"</p>\n<h4 id=\"the-three-way-handshake\">The three-way handshake</h4>\n<p>Before any data flows, the two sides open a connection with a <strong>three-way handshake</strong>. These numbers come from RFC 9293's own example:</p>\n<ol>\n<li><strong>SYN.</strong> The client sends a segment with the SYN (\"synchronize\") flag set, saying \"I'll number my bytes starting at 100.\"</li>\n<li><strong>SYN-ACK.</strong> The server replies with its own SYN, \"I'll start at 300\", and an ACK (\"acknowledge\") of 101, meaning \"I got your 100 and expect 101 next.\"</li>\n<li><strong>ACK.</strong> The client acknowledges with 301. The connection is open, and the client's next segment can carry data.</li>\n</ol>\n<p>The RFC says the main purpose is to stop \"old duplicate connection initiations from causing confusion\": a stray SYN from an earlier attempt can't open a ghost connection. To close, each side sends a segment with the FIN flag (\"no more data from sender\") and the other side acknowledges it.</p>\n<p>The handshake costs a full round trip before the request can be sent. If a Crumb user is 150 ms away, that's 150 ms before the app has asked for anything. <a href=\"/learn/performance-and-latency#latency-and-throughput\">Performance and latency</a> explains why round trips, more than bandwidth, decide how fast small requests are.</p>\n<h4 id=\"how-tcp-keeps-data-intact-and-in-order\">How TCP keeps data intact and in order</h4>\n<p>RFC 9293 says TCP detects \"packet losses (via sequence numbers) and errors (via per-segment checksums)\" and corrects them \"via retransmission\":</p>\n<ul>\n<li><strong>Sequence numbers</strong> let the receiver put segments back in order and spot gaps.</li>\n<li><strong>Checksums</strong>, computed from each segment's contents, reveal bits flipped in transit. A damaged segment is dropped.</li>\n<li><strong>Acknowledgements</strong> tell the sender which bytes arrived.</li>\n<li><strong>Retransmission</strong> resends data that wasn't acknowledged in time.</li>\n</ul>\n<p>TCP doesn't retry forever. After repeated timeouts on the same data, it closes the connection. RFC 9293 says that limit should be at least 100 seconds for data.</p>\n<h4 id=\"flow-control-and-congestion-control\">Flow control and congestion control</h4>\n<ul>\n<li><strong>Flow control</strong> protects the receiver. Each segment carries a <strong>window</strong>, the number of bytes its sender \"is willing to accept.\" A slow receiver shrinks its window, and the other side slows to match.</li>\n<li><strong>Congestion control</strong> protects the network. RFC 9293 requires \"slow start, congestion avoidance, and exponential backoff\" (detailed in <a href=\"https://www.rfc-editor.org/rfc/rfc5681\" target=\"_blank\" rel=\"noopener noreferrer\">RFC 5681</a>). A new connection sends a little and speeds up as acknowledgements return. When packets are lost, TCP takes that as a sign of overload and slows down.</li>\n</ul>\n<p>Together these let TCP find a good sending rate on its own, without knowing the network's capacity in advance.</p>\n<h4 id=\"what-the-guarantees-cost\">What the guarantees cost</h4>\n<ul>\n<li><strong>Setup time.</strong> The handshake adds a round trip, and TLS adds more (see <a href=\"/learn/protocols-and-apis#tls-and-the-three-versions-of-http\">below</a>).</li>\n<li><strong>Waiting for lost data.</strong> TCP delivers bytes in order. If segment 5 is lost, segments 6 to 20 wait until segment 5 is resent, even if they arrived. One missing piece holds up everything behind it. This is called <strong>head-of-line blocking</strong>.</li>\n<li><strong>Slower sending after a loss</strong>, because congestion control backs off.</li>\n<li><strong>Memory per connection</strong> on both machines, for buffers and state.</li>\n</ul>\n<p>So TCP generally moves data less efficiently than UDP. And because web servers keep many TCP connections open to get high throughput, the memory adds up.</p>\n<p>Facebook hit this limit with its cache. In <a href=\"https://www.cs.bu.edu/~jappavoo/jappavoo.github.com/451/papers/memcache-fb.pdf\" target=\"_blank\" rel=\"noopener noreferrer\">Scaling Memcache at Facebook</a> (NSDI 2013), its engineers wrote that \"the high memory demands of open TCP connections makes it prohibitively expensive to have an open connection between every web thread and memcached server.\" So each web server sends cache writes through a proxy called mcrouter on the same machine. Mcrouter coalesces the connections, so many threads share a few connections to each cache server.</p>\n<p>This is <strong>connection pooling</strong>: open a set of connections once and lend them to requests as needed. It saves a handshake per request and caps memory. Crumb does the same for its database, and <a href=\"/learn/performance-and-latency#littles-law\">Little's law</a> tells you how big the pool should be. Facebook also took a second route for cache reads: it moved them to UDP.</p>\n<h4 id=\"when-to-use-tcp\">When to use TCP</h4>\n<p>TCP suits programs that need high reliability and can accept some delay: web servers, database connections, email (SMTP), file transfer (FTP) and remote login (SSH). Use TCP when:</p>\n<ul>\n<li>you need all of the data to arrive intact, and</li>\n<li>you want the protocol to make the best use of the network's capacity for you.</li>\n</ul>","headings":[{"id":"tcp-reliable-delivery-with-a-price","title":"TCP: reliable delivery, with a price","depth":3},{"id":"the-three-way-handshake","title":"The three-way handshake","depth":4},{"id":"how-tcp-keeps-data-intact-and-in-order","title":"How TCP keeps data intact and in order","depth":4},{"id":"flow-control-and-congestion-control","title":"Flow control and congestion control","depth":4},{"id":"what-the-guarantees-cost","title":"What the guarantees cost","depth":4},{"id":"when-to-use-tcp","title":"When to use TCP","depth":4}]},{"id":"udp-fast-with-no-promises","title":"UDP: fast, with no promises","html":"<h3 id=\"udp-fast-with-no-promises\">UDP: fast, with no promises</h3>\n<p><strong>UDP</strong> (User Datagram Protocol) is like sending postcards. You address each card and drop it in the box. There's no setup and no tracking. Most cards arrive, some arrive out of order, some never arrive, and nobody tells you.</p>\n<p><a href=\"https://www.rfc-editor.org/rfc/rfc768\" target=\"_blank\" rel=\"noopener noreferrer\">RFC 768</a> defined UDP in 1980, in three pages. It sends messages \"with a minimum of protocol mechanism,\" and \"delivery and duplicate protection are not guaranteed.\" The header is 8 bytes: source port, destination port, length and checksum. A sender may even skip the checksum.</p>\n<p>A UDP message is called a <strong>datagram</strong>, the UDP word for a packet. Each one is handled on its own, and the only promise is at the datagram level: a datagram that arrives is whole. Datagrams may arrive out of order or not at all. UDP has no connection, no retransmission and no congestion control. Without TCP's guarantees there's less work and less waiting, so UDP is generally more efficient.</p>\n<h4 id=\"broadcast-and-why-dhcp-needs-it\">Broadcast, and why DHCP needs it</h4>\n<p>UDP can <strong>broadcast</strong>: send one datagram to every device on the local network. TCP can't, because a TCP connection has exactly two ends.</p>\n<p>This solves a chicken-and-egg problem. A laptop joining Wi-Fi uses <strong>DHCP</strong> (Dynamic Host Configuration Protocol) to get an IP address. Until it has one, it can't open a TCP connection to anybody. <a href=\"https://www.rfc-editor.org/rfc/rfc2131\" target=\"_blank\" rel=\"noopener noreferrer\">RFC 2131</a> says DHCP runs on UDP. The client, with its own address set to zero, broadcasts a DHCPDISCOVER message to the all-ones broadcast address on UDP port 67. Any DHCP server that hears it can reply with an offer.</p>\n<h4 id=\"where-udp-wins\">Where UDP wins</h4>\n<p>UDP is less reliable, but it suits real-time uses where fresh data matters more than complete data: voice calls (VoIP), video chat, live streaming and multiplayer games.</p>\n<p>Glenn Fiedler explains why in <a href=\"https://gafferongames.com/post/udp_vs_tcp/\" target=\"_blank\" rel=\"noopener noreferrer\">UDP vs. TCP</a> (2008). A game server sends every player's position many times a second. Over TCP, one lost update holds back the newer ones, which wait in a queue where \"you cannot access it until that lost packet has been retransmitted.\" The game freezes, then jumps. Over UDP, the game uses the newest datagram and moves on. Fiedler advises building only the reliability you need, such as resending important events, on top of UDP yourself.</p>\n<p>Facebook's cache reads are a server-side example. The NSDI paper says \"we rely on UDP for get requests to reduce latency and overhead,\" which cut latency by about 20%. At peak, 0.25% of reads were dropped or arrived out of order, and clients treat those as cache misses. Writes and deletes still go over TCP, because losing them isn't acceptable.</p>\n<h4 id=\"when-to-use-udp\">When to use UDP</h4>\n<p>Use UDP when:</p>\n<ul>\n<li>you need the lowest latency,</li>\n<li>late data is worse than lost data, and</li>\n<li>you're willing to build your own error handling where you need it.</li>\n</ul>\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n<div class=\"table-wrap\"><table><thead><tr><th></th><th>TCP</th><th>UDP</th></tr></thead><tbody><tr><td>Setup</td><td>Three-way handshake</td><td>None</td></tr><tr><td>Delivery</td><td>Guaranteed, or the connection fails</td><td>Best effort</td></tr><tr><td>Order</td><td>Preserved</td><td>Not preserved</td></tr><tr><td>Flow and congestion control</td><td>Built in</td><td>None</td></tr><tr><td>Broadcast</td><td>No</td><td>Yes</td></tr><tr><td>Typical uses</td><td>Web pages and APIs, databases, email, SSH</td><td>DHCP, VoIP, video calls, games, QUIC</td></tr></tbody></table></div>","headings":[{"id":"udp-fast-with-no-promises","title":"UDP: fast, with no promises","depth":3},{"id":"broadcast-and-why-dhcp-needs-it","title":"Broadcast, and why DHCP needs it","depth":4},{"id":"where-udp-wins","title":"Where UDP wins","depth":4},{"id":"when-to-use-udp","title":"When to use UDP","depth":4}]},{"id":"tls-and-the-three-versions-of-http","title":"TLS and the three versions of HTTP","html":"<h3 id=\"tls-and-the-three-versions-of-http\">TLS and the three versions of HTTP</h3>\n<h4 id=\"tls-on-top-of-tcp\">TLS on top of TCP</h4>\n<p>Plain TCP carries bytes anyone along the path can read. <strong>TLS</strong> (Transport Layer Security) encrypts them and proves the server's identity. HTTPS is HTTP inside TLS inside TCP. TLS runs its own handshake after TCP's. In TLS 1.3 (<a href=\"https://www.rfc-editor.org/rfc/rfc8446\" target=\"_blank\" rel=\"noopener noreferrer\">RFC 8446</a>, 2018) that takes one round trip, and a client returning to a recent server can send data immediately in a \"0-RTT\" mode, \"at the cost of certain security properties.\" So a new HTTPS connection over TCP spends two round trips before the request goes out. <a href=\"/learn/security#in-transit-tls-13\">Security</a> covers what TLS protects.</p>\n<h4 id=\"http11-keep-the-connection-open\">HTTP/1.1: keep the connection open</h4>\n<p><a href=\"https://www.rfc-editor.org/rfc/rfc9112\" target=\"_blank\" rel=\"noopener noreferrer\">HTTP/1.1</a>, first published in January 1997 (<a href=\"https://developer.mozilla.org/en-US/docs/Web/HTTP/Guides/Evolution_of_HTTP\" target=\"_blank\" rel=\"noopener noreferrer\">MDN</a>), made <strong>persistent connections</strong> (keep-alive) the default, \"allowing multiple requests and responses to be carried over a single connection.\" That saves a handshake per request.</p>\n<p>A persistent connection still handles one exchange at a time in practice. HTTP/1.1 allows <strong>pipelining</strong>, sending several requests without waiting, but the server \"MUST send the corresponding responses in the same order.\" One slow response holds up all the others: head-of-line blocking at the HTTP layer. So clients open several connections to the same server, and RFC 9112 warns that \"each connection consumes server resources.\"</p>\n<h4 id=\"http2-many-streams-on-one-connection\">HTTP/2: many streams on one connection</h4>\n<p><a href=\"https://www.rfc-editor.org/rfc/rfc9113\" target=\"_blank\" rel=\"noopener noreferrer\">HTTP/2</a>, standardized in 2015 and revised as RFC 9113 in 2022, splits messages into binary <strong>frames</strong> and gives each request its own <strong>stream</strong>. Frames from many streams are interleaved on one TCP connection. Crumb's recipe page can fetch the recipe, photos and comments at once, and a slow response no longer blocks the others. HTTP/2 also encodes headers compactly.</p>\n<p>RFC 9113 states the catch: \"TCP head-of-line blocking is not addressed by this protocol.\" All the streams share one TCP byte stream. When one packet is lost, TCP holds back everything after it, including frames for streams that lost nothing. As the HTTP/3 standard puts it, \"a lost or reordered packet causes all active transactions to experience a stall.\"</p>\n<h4 id=\"http3-streams-move-into-the-transport\">HTTP/3: streams move into the transport</h4>\n<p><a href=\"https://www.rfc-editor.org/rfc/rfc9114\" target=\"_blank\" rel=\"noopener noreferrer\">HTTP/3</a> (2022) keeps HTTP's meaning and runs it over <strong>QUIC</strong> (<a href=\"https://www.rfc-editor.org/rfc/rfc9000\" target=\"_blank\" rel=\"noopener noreferrer\">RFC 9000</a>, 2021). QUIC is a reliable transport that knows about streams, providing \"reliability at the stream level and congestion control across the entire connection.\" In MDN's words, \"only the stream with data in that packet is blocked.\" QUIC also:</p>\n<ul>\n<li><strong>builds in TLS 1.3</strong>, so the transport and encryption handshakes happen together instead of one after the other, and</li>\n<li><strong>identifies a connection by an ID</strong> instead of an IP address and port, so a phone can move from Wi-Fi to mobile data without starting over.</li>\n</ul>\n<p>Why build reliability on top of unreliable UDP? RFC 9000 says QUIC uses UDP \"to better facilitate deployment in existing systems and networks.\" Routers, firewalls and operating systems already pass UDP. A new transport protocol would be blocked by many of them. A server advertises HTTP/3 with an <code>Alt-Svc</code> response header, and the client switches on later requests.</p>\n<figure class=\"diagram\" data-text=\"HTTP/1.1 -> TLS; TLS -> TCP; HTTP/2 streams in frames -> TLS; TLS -> TCP; HTTP/3 -> QUIC streams + TLS 1.3; QUIC streams + TLS 1.3 -> UDP\"><div class=\"diagram-scroll\"><svg class=\"dg\" viewBox=\"0 0 800 318\" width=\"800\" role=\"img\" aria-label=\"What each HTTP version runs on. HTTP/3 moves streams and encryption into QUIC.\"><defs><marker id=\"dg-arrow\" viewBox=\"0 0 10 10\" refX=\"9\" refY=\"5\" markerWidth=\"7\" markerHeight=\"7\" orient=\"auto-start-reverse\"><path class=\"dg-arrowhead\" d=\"M0,1 L9,5 L0,9 z\"></path></marker></defs><line class=\"dg-edge\" x1=\"80\" y1=\"69\" x2=\"80\" y2=\"133\" marker-end=\"url(#dg-arrow)\"></line><line class=\"dg-edge\" x1=\"80\" y1=\"189\" x2=\"80\" y2=\"253\" marker-end=\"url(#dg-arrow)\"></line><line class=\"dg-edge\" x1=\"400\" y1=\"71\" x2=\"400\" y2=\"133\" marker-end=\"url(#dg-arrow)\"></line><line class=\"dg-edge\" x1=\"400\" y1=\"189\" x2=\"400\" y2=\"253\" marker-end=\"url(#dg-arrow)\"></line><line class=\"dg-edge\" x1=\"720\" y1=\"69\" x2=\"720\" y2=\"131\" marker-end=\"url(#dg-arrow)\"></line><line class=\"dg-edge\" x1=\"720\" y1=\"191\" x2=\"720\" y2=\"253\" marker-end=\"url(#dg-arrow)\"></line><rect class=\"dg-node dg-box\" x=\"12\" y=\"18\" width=\"136\" height=\"48\" rx=\"8\"></rect><text class=\"dg-label\" text-anchor=\"middle\"><tspan x=\"80\" y=\"46.5\">HTTP/1.1</tspan></text><rect class=\"dg-node dg-box\" x=\"12\" y=\"138\" width=\"136\" height=\"48\" rx=\"8\"></rect><text class=\"dg-label\" text-anchor=\"middle\"><tspan x=\"80\" y=\"166.5\">TLS</tspan></text><rect class=\"dg-node dg-box\" x=\"12\" y=\"258\" width=\"136\" height=\"48\" rx=\"8\"></rect><text class=\"dg-label\" text-anchor=\"middle\"><tspan x=\"80\" y=\"286.5\">TCP</tspan></text><rect class=\"dg-node dg-box\" x=\"332\" y=\"16\" width=\"136\" height=\"52\" rx=\"8\"></rect><text class=\"dg-label\" text-anchor=\"middle\"><tspan x=\"400\" y=\"38.5\">HTTP/2</tspan><tspan x=\"400\" y=\"54.5\">streams in frames</tspan></text><rect class=\"dg-node dg-box\" x=\"332\" y=\"138\" width=\"136\" height=\"48\" rx=\"8\"></rect><text class=\"dg-label\" text-anchor=\"middle\"><tspan x=\"400\" y=\"166.5\">TLS</tspan></text><rect class=\"dg-node dg-box\" x=\"332\" y=\"258\" width=\"136\" height=\"48\" rx=\"8\"></rect><text class=\"dg-label\" text-anchor=\"middle\"><tspan x=\"400\" y=\"286.5\">TCP</tspan></text><rect class=\"dg-node dg-box\" x=\"652\" y=\"18\" width=\"136\" height=\"48\" rx=\"8\"></rect><text class=\"dg-label\" text-anchor=\"middle\"><tspan x=\"720\" y=\"46.5\">HTTP/3</tspan></text><rect class=\"dg-node dg-box\" x=\"652\" y=\"136\" width=\"136\" height=\"52\" rx=\"8\"></rect><text class=\"dg-label\" text-anchor=\"middle\"><tspan x=\"720\" y=\"158.5\">QUIC</tspan><tspan x=\"720\" y=\"174.5\">streams + TLS 1.3</tspan></text><rect class=\"dg-node dg-box\" x=\"652\" y=\"258\" width=\"136\" height=\"48\" rx=\"8\"></rect><text class=\"dg-label\" text-anchor=\"middle\"><tspan x=\"720\" y=\"286.5\">UDP</tspan></text></svg></div><figcaption>What each HTTP version runs on. HTTP/3 moves streams and encryption into QUIC.</figcaption></figure>\n<p>All three versions share the same methods, status codes and headers. When Crumb's load balancer starts speaking HTTP/3 to phones, the API code doesn't change.</p>","headings":[{"id":"tls-and-the-three-versions-of-http","title":"TLS and the three versions of HTTP","depth":3},{"id":"tls-on-top-of-tcp","title":"TLS on top of TCP","depth":4},{"id":"http11-keep-the-connection-open","title":"HTTP/1.1: keep the connection open","depth":4},{"id":"http2-many-streams-on-one-connection","title":"HTTP/2: many streams on one connection","depth":4},{"id":"http3-streams-move-into-the-transport","title":"HTTP/3: streams move into the transport","depth":4}]},{"id":"rpc-calling-a-function-on-another-machine","title":"RPC: calling a function on another machine","html":"<h3 id=\"rpc-calling-a-function-on-another-machine\">RPC: calling a function on another machine</h3>\n<p>In a <strong>remote procedure call</strong> (RPC), a client makes a procedure run in a different address space, usually on a remote server. The call is written as if it were local, and the details of reaching the server are hidden from the client program. On Crumb, an API server might get nutrition facts from a separate nutrition service like this:</p>\n<pre class=\"shiki shiki-themes github-light github-dark\" style=\"background-color:#fff;--shiki-dark-bg:#24292e;color:#24292e;--shiki-dark:#e1e4e8\" tabindex=\"0\"><code><span class=\"line\"><span style=\"color:#24292E;--shiki-dark:#E1E4E8\">facts </span><span style=\"color:#D73A49;--shiki-dark:#F97583\">=</span><span style=\"color:#24292E;--shiki-dark:#E1E4E8\"> nutrition.get_facts(</span><span style=\"color:#E36209;--shiki-dark:#FFAB70\">recipe_id</span><span style=\"color:#D73A49;--shiki-dark:#F97583\">=</span><span style=\"color:#032F62;--shiki-dark:#9ECBFF\">\"456\"</span><span style=\"color:#24292E;--shiki-dark:#E1E4E8\">)</span></span></code></pre>\n<p>It reads like a local function call, but it behaves differently. A local call takes nanoseconds and never half-fails. A remote call takes milliseconds, can time out, and can fail after the server already did the work. Because remote calls are slower and less reliable, it helps to make them easy to tell apart from local calls, for example by naming and by giving every call an explicit timeout.</p>\n<h4 id=\"how-a-call-travels\">How a call travels</h4>\n<p>Generated code called <strong>stubs</strong> makes the remote call look local. A call goes through these steps:</p>\n<ol>\n<li><strong>Client program.</strong> It calls the client stub. The arguments are pushed onto the stack, just as for a local call.</li>\n<li><strong>Client stub.</strong> It <strong>marshals</strong> (packs) the procedure's identifier and the arguments into a request message.</li>\n<li><strong>Client communication module.</strong> The operating system sends the message to the server.</li>\n<li><strong>Server communication module.</strong> The server's operating system passes the incoming packets to the server stub.</li>\n<li><strong>Server stub.</strong> It <strong>unmarshals</strong> (unpacks) the message and calls the procedure matching the identifier, passing the arguments.</li>\n<li><strong>The reply</strong> repeats these steps in reverse, back to the client program.</li>\n</ol>\n<figure class=\"diagram\" data-text=\"Client program <-> Client stub; Client stub <-> Client OS and network; Client OS and network <-> Server OS and network (request / reply); Server OS and network <-> Server stub; Server stub <-> Server procedure\"><div class=\"diagram-scroll\"><svg class=\"dg\" viewBox=\"0 0 640 260\" width=\"640\" role=\"img\" aria-label=\"RPC. Stubs on each side make a remote call look like a local one.\"><defs><marker id=\"dg-arrow\" viewBox=\"0 0 10 10\" refX=\"9\" refY=\"5\" markerWidth=\"7\" markerHeight=\"7\" orient=\"auto-start-reverse\"><path class=\"dg-arrowhead\" d=\"M0,1 L9,5 L0,9 z\"></path></marker></defs><line class=\"dg-edge\" x1=\"153\" y1=\"42\" x2=\"247\" y2=\"42\" marker-end=\"url(#dg-arrow)\" marker-start=\"url(#dg-arrow)\"></line><line class=\"dg-edge\" x1=\"393\" y1=\"42\" x2=\"487\" y2=\"42\" marker-end=\"url(#dg-arrow)\" marker-start=\"url(#dg-arrow)\"></line><line class=\"dg-edge\" x1=\"560\" y1=\"73\" x2=\"560\" y2=\"191\" marker-end=\"url(#dg-arrow)\" marker-start=\"url(#dg-arrow)\"></line><line class=\"dg-edge\" x1=\"487\" y1=\"222\" x2=\"393\" y2=\"222\" marker-end=\"url(#dg-arrow)\" marker-start=\"url(#dg-arrow)\"></line><line class=\"dg-edge\" x1=\"247\" y1=\"222\" x2=\"153\" y2=\"222\" marker-end=\"url(#dg-arrow)\" marker-start=\"url(#dg-arrow)\"></line><rect class=\"dg-node dg-client\" x=\"12\" y=\"18\" width=\"136\" height=\"48\" rx=\"24\"></rect><text class=\"dg-label\" text-anchor=\"middle\"><tspan x=\"80\" y=\"46.5\">Client program</tspan></text><rect class=\"dg-node dg-box\" x=\"252\" y=\"18\" width=\"136\" height=\"48\" rx=\"8\"></rect><text class=\"dg-label\" text-anchor=\"middle\"><tspan x=\"320\" y=\"46.5\">Client stub</tspan></text><rect class=\"dg-node dg-box\" x=\"492\" y=\"16\" width=\"136\" height=\"52\" rx=\"8\"></rect><text class=\"dg-label\" text-anchor=\"middle\"><tspan x=\"560\" y=\"38.5\">Client OS</tspan><tspan x=\"560\" y=\"54.5\">and network</tspan></text><rect class=\"dg-node dg-box\" x=\"12\" y=\"198\" width=\"136\" height=\"48\" rx=\"8\"></rect><text class=\"dg-label\" text-anchor=\"middle\"><tspan x=\"80\" y=\"226.5\">Server procedure</tspan></text><rect class=\"dg-node dg-box\" x=\"252\" y=\"198\" width=\"136\" height=\"48\" rx=\"8\"></rect><text class=\"dg-label\" text-anchor=\"middle\"><tspan x=\"320\" y=\"226.5\">Server stub</tspan></text><rect class=\"dg-node dg-box\" x=\"492\" y=\"196\" width=\"136\" height=\"52\" rx=\"8\"></rect><text class=\"dg-label\" text-anchor=\"middle\"><tspan x=\"560\" y=\"218.5\">Server OS</tspan><tspan x=\"560\" y=\"234.5\">and network</tspan></text><rect class=\"dg-edge-bg\" x=\"508.5\" y=\"123\" width=\"103\" height=\"18\" rx=\"4\"></rect><text class=\"dg-edge-label\" x=\"560\" y=\"136\" text-anchor=\"middle\">request / reply</text></svg></div><figcaption>RPC. Stubs on each side make a remote call look like a local one.</figcaption></figure>\n<p>Tyson Trautmann's <a href=\"https://web.archive.org/web/20170608193645/http://etherealbits.com/2012/12/debunking-the-myths-of-rpc-rest/\" target=\"_blank\" rel=\"noopener noreferrer\">Debunking the Myths of RPC &#x26; REST</a> (2012) adds that \"nothing about RPC is inherently bound to HTTP.\" RPC can use HTTP to carry messages, or its own protocol.</p>\n<p>Frameworks generate the stubs from a description of the service. Three popular ones:</p>\n<ul>\n<li><strong><a href=\"https://protobuf.dev/overview/\" target=\"_blank\" rel=\"noopener noreferrer\">Protocol Buffers</a></strong> (Protobuf), from Google, is \"a language-neutral, platform-neutral extensible mechanism for serializing structured data,\" \"like JSON, except it's smaller and faster.\" gRPC uses it.</li>\n<li><strong><a href=\"https://thrift.apache.org/\" target=\"_blank\" rel=\"noopener noreferrer\">Apache Thrift</a></strong> pairs a data format with \"a code generation engine\" for many languages. Facebook <a href=\"https://engineering.fb.com/2014/02/20/open-source/under-the-hood-building-and-open-sourcing-fbthrift/\" target=\"_blank\" rel=\"noopener noreferrer\">built it in 2006</a> and open-sourced it in 2007.</li>\n<li><strong><a href=\"https://avro.apache.org/docs/\" target=\"_blank\" rel=\"noopener noreferrer\">Apache Avro</a></strong> is \"a data serialization system\" that also supports RPC. Data files carry their schema, so \"code generation is not required.\"</li>\n</ul>\n<h4 id=\"what-rpc-calls-look-like-over-http\">What RPC calls look like over HTTP</h4>\n<p>The URL names an action:</p>\n<pre class=\"shiki shiki-themes github-light github-dark\" style=\"background-color:#fff;--shiki-dark-bg:#24292e;color:#24292e;--shiki-dark:#e1e4e8\" tabindex=\"0\"><code><span class=\"line\"><span style=\"color:#D73A49;--shiki-dark:#F97583\">GET</span><span style=\"color:#24292E;--shiki-dark:#E1E4E8\"> /getRecipe?recipeId=456</span></span>\n<span class=\"line\"></span>\n<span class=\"line\"><span style=\"color:#D73A49;--shiki-dark:#F97583\">POST</span><span style=\"color:#24292E;--shiki-dark:#E1E4E8\"> /rateRecipe</span></span>\n<span class=\"line\"><span style=\"color:#22863A;--shiki-dark:#85E89D\">Content-Type</span><span style=\"color:#D73A49;--shiki-dark:#F97583\">:</span><span style=\"color:#032F62;--shiki-dark:#9ECBFF\"> application/json</span></span>\n<span class=\"line\"></span>\n<span class=\"line\"><span style=\"color:#24292E;--shiki-dark:#E1E4E8\">{</span><span style=\"color:#005CC5;--shiki-dark:#79B8FF\">\"recipeId\"</span><span style=\"color:#24292E;--shiki-dark:#E1E4E8\">: </span><span style=\"color:#032F62;--shiki-dark:#9ECBFF\">\"456\"</span><span style=\"color:#24292E;--shiki-dark:#E1E4E8\">, </span><span style=\"color:#005CC5;--shiki-dark:#79B8FF\">\"userId\"</span><span style=\"color:#24292E;--shiki-dark:#E1E4E8\">: </span><span style=\"color:#032F62;--shiki-dark:#9ECBFF\">\"1234\"</span><span style=\"color:#24292E;--shiki-dark:#E1E4E8\">, </span><span style=\"color:#005CC5;--shiki-dark:#79B8FF\">\"stars\"</span><span style=\"color:#24292E;--shiki-dark:#E1E4E8\">: </span><span style=\"color:#005CC5;--shiki-dark:#79B8FF\">5</span><span style=\"color:#24292E;--shiki-dark:#E1E4E8\">}</span></span></code></pre>\n<p>RPC is focused on exposing <strong>behaviors</strong>. It is often used for internal communication for performance reasons, because you can hand-craft calls to fit each use case and use a compact binary format.</p>\n<p>The same reasoning applies to offering a <strong>native library</strong> (an SDK) that customers call from their own code. Choose one when:</p>\n<ul>\n<li>you know the platform your users are on,</li>\n<li>you want to control how your logic is accessed,</li>\n<li>you want to control how errors are handled on the library's side, and</li>\n<li>performance and the end user's experience are your main concern.</li>\n</ul>\n<p>For public APIs called by unknown developers in any language, HTTP APIs in the REST style are more common.</p>\n<h4 id=\"grpc-rpc-over-http2\">gRPC: RPC over HTTP/2</h4>\n<p>Google long used an internal RPC system called Stubby. In March 2015 it <a href=\"https://grpc.io/about/\" target=\"_blank\" rel=\"noopener noreferrer\">decided to build the next version as open source</a>, called <strong>gRPC</strong>. It lets a client \"call a method on a server application on a different machine as if it were a local object\" (<a href=\"https://grpc.io/docs/what-is-grpc/introduction/\" target=\"_blank\" rel=\"noopener noreferrer\">introduction</a>). You describe the service in a <code>.proto</code> file:</p>\n<pre class=\"shiki shiki-themes github-light github-dark\" style=\"background-color:#fff;--shiki-dark-bg:#24292e;color:#24292e;--shiki-dark:#e1e4e8\" tabindex=\"0\"><code><span class=\"line\"><span>syntax = \"proto3\";</span></span>\n<span class=\"line\"><span></span></span>\n<span class=\"line\"><span>service NutritionService {</span></span>\n<span class=\"line\"><span>  rpc GetFacts (GetFactsRequest) returns (NutritionFacts);</span></span>\n<span class=\"line\"><span>  rpc WatchPrices (WatchPricesRequest) returns (stream IngredientPrice);</span></span>\n<span class=\"line\"><span>}</span></span>\n<span class=\"line\"><span></span></span>\n<span class=\"line\"><span>message GetFactsRequest {</span></span>\n<span class=\"line\"><span>  string recipe_id = 1;</span></span>\n<span class=\"line\"><span>}</span></span></code></pre>\n<p>A compiler generates client and server code in each language. On the wire, gRPC <a href=\"https://github.com/grpc/grpc/blob/master/doc/PROTOCOL-HTTP2.md\" target=\"_blank\" rel=\"noopener noreferrer\">runs over HTTP/2</a>: every call is a <code>POST</code> to a path like <code>/NutritionService/GetFacts</code>, with content type <code>application/grpc</code> and a body of length-prefixed Protobuf messages. The result code arrives in <strong>trailers</strong>, headers sent after the body.</p>\n<p>HTTP/2 streams give gRPC <a href=\"https://grpc.io/docs/what-is-grpc/core-concepts/\" target=\"_blank\" rel=\"noopener noreferrer\">four kinds of method</a>:</p>\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n<div class=\"table-wrap\"><table><thead><tr><th>Kind</th><th>What happens</th><th>Crumb example</th></tr></thead><tbody><tr><td>Unary</td><td>One request, one response</td><td>Get a recipe's nutrition facts</td></tr><tr><td>Server streaming</td><td>One request, a stream of responses</td><td>Watch ingredient prices change</td></tr><tr><td>Client streaming</td><td>A stream of requests, one response</td><td>Upload a recipe video in chunks</td></tr><tr><td>Bidirectional streaming</td><td>Both sides stream independently</td><td>A live cook-along session</td></tr></tbody></table></div>\n<p>gRPC also builds in two habits remote calls need. <strong>Deadlines</strong> let the client say how long it will wait, after which the call fails with <code>DEADLINE_EXCEEDED</code>. <strong>Cancellation</strong> lets either side stop a call, though \"changes made before a cancellation are not rolled back.\"</p>\n<p>Browsers can't call gRPC directly, because \"raw HTTP/2 frames are inaccessible in browsers\" (<a href=\"https://grpc.io/blog/state-of-grpc-web/\" target=\"_blank\" rel=\"noopener noreferrer\">gRPC blog</a>). The workaround, gRPC-Web, needs a translating proxy. So most teams use gRPC between their own services and JSON over HTTP for browsers, often with an API gateway translating between them (<a href=\"/learn/reverse-proxy-and-api-gateway#protocol-translation-http-to-grpc\">Reverse proxies and API gateways</a>).</p>\n<h4 id=\"disadvantages-of-rpc\">Disadvantages of RPC</h4>\n<ul>\n<li><strong>Clients become tightly coupled to the service.</strong> Clients call specific procedures with specific arguments. If the nutrition team renames <code>GetFacts</code> or changes its arguments, clients must be rebuilt and redeployed in step.</li>\n<li><strong>Every new operation needs a new API.</strong> Facts for ten recipes at once means a new <code>GetFactsBatch</code>. Trautmann warns that hand-rolled interfaces tend to be \"too narrow for future use cases,\" leading to \"an explosion in the number of methods.\"</li>\n<li><strong>RPC can be hard to debug.</strong> Binary messages can't be read in a log or replayed with <code>curl</code>.</li>\n<li><strong>Existing tools may not work out of the box.</strong> An HTTP cache such as <a href=\"http://www.squid-cache.org/\" target=\"_blank\" rel=\"noopener noreferrer\">Squid</a> understands <code>GET</code> and URLs. Trautmann notes that RPC inputs may not be passed \"in a standard way that makes it possible to map requests to cached responses,\" so caching RPC results takes extra work. gRPC sends every call as a <code>POST</code>, so standard HTTP caches won't store any of them.</li>\n</ul>","headings":[{"id":"rpc-calling-a-function-on-another-machine","title":"RPC: calling a function on another machine","depth":3},{"id":"how-a-call-travels","title":"How a call travels","depth":4},{"id":"what-rpc-calls-look-like-over-http","title":"What RPC calls look like over HTTP","depth":4},{"id":"grpc-rpc-over-http2","title":"gRPC: RPC over HTTP/2","depth":4},{"id":"disadvantages-of-rpc","title":"Disadvantages of RPC","depth":4}]},{"id":"rest-resources-and-representations","title":"REST: resources and representations","html":"<h3 id=\"rest-resources-and-representations\">REST: resources and representations</h3>\n<p><strong>REST</strong> (Representational State Transfer) is an architectural style, meaning a set of design rules a system can follow. Roy Fielding defined it in <a href=\"https://ics.uci.edu/~fielding/pubs/dissertation/rest_arch_style.htm\" target=\"_blank\" rel=\"noopener noreferrer\">chapter 5 of his 2000 dissertation</a>.</p>\n<p>REST uses a client/server model. The client acts on a set of <strong>resources</strong> that the server manages. Fielding says \"any information that can be named can be a resource\": a recipe, a user, a user's saved recipes. The server provides <strong>representations</strong> of resources, such as a JSON document describing a recipe, and actions that either change resources or return new representations.</p>\n<p>All communication must be <strong>stateless</strong> and <strong>cacheable</strong>. Each request \"must contain all of the information necessary to understand the request,\" and responses must be labeled as cacheable or not, so clients and intermediaries know when they can reuse one.</p>\n<h4 id=\"the-four-qualities-of-a-restful-interface\">The four qualities of a RESTful interface</h4>\n<p>Fielding's central rule is a <strong>uniform interface</strong>: every resource is handled the same way. It has four parts:</p>\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n<div class=\"table-wrap\"><table><thead><tr><th>Quality</th><th>Over HTTP</th><th>In practice</th></tr></thead><tbody><tr><td>Identify resources</td><td>URIs</td><td>Each resource has one URI, used whatever the operation. <code>/recipes/456</code> is read, replaced and deleted at the same address.</td></tr><tr><td>Change resources through representations</td><td>Methods, headers and body</td><td>The method says what to do, headers describe the format, the body carries the representation.</td></tr><tr><td>Self-descriptive messages</td><td>Status codes</td><td>Use the standard status codes. Don't reinvent the wheel with your own error scheme.</td></tr><tr><td>HATEOAS: hypermedia as the engine of application state</td><td>HTML and links</td><td>Responses link to what the client can do next. A website is the model: the whole service is usable from a browser by following links.</td></tr></tbody></table></div>\n<p>Most APIs that call themselves REST skip the last one. Fielding complained in 2008 about \"the number of people calling any HTTP-based interface a REST API\" (<a href=\"https://roy.gbiv.com/untangled/2008/rest-apis-must-be-hypertext-driven\" target=\"_blank\" rel=\"noopener noreferrer\">REST APIs must be hypertext-driven</a>). His rule is that \"a REST API should be entered with no prior knowledge beyond the initial URI.\" A client that follows links instead of building URLs keeps working when the server reorganizes them.</p>\n<h4 id=\"what-rest-calls-look-like\">What REST calls look like</h4>\n<pre class=\"shiki shiki-themes github-light github-dark\" style=\"background-color:#fff;--shiki-dark-bg:#24292e;color:#24292e;--shiki-dark:#e1e4e8\" tabindex=\"0\"><code><span class=\"line\"><span style=\"color:#D73A49;--shiki-dark:#F97583\">GET</span><span style=\"color:#24292E;--shiki-dark:#E1E4E8\"> /recipes/456</span></span>\n<span class=\"line\"></span>\n<span class=\"line\"><span style=\"color:#D73A49;--shiki-dark:#F97583\">HTTP</span><span style=\"color:#24292E;--shiki-dark:#E1E4E8\">/</span><span style=\"color:#005CC5;--shiki-dark:#79B8FF\">1.1</span><span style=\"color:#005CC5;--shiki-dark:#79B8FF\"> 200</span><span style=\"color:#032F62;--shiki-dark:#9ECBFF\"> OK</span></span>\n<span class=\"line\"><span style=\"color:#22863A;--shiki-dark:#85E89D\">Content-Type</span><span style=\"color:#D73A49;--shiki-dark:#F97583\">:</span><span style=\"color:#032F62;--shiki-dark:#9ECBFF\"> application/json</span></span>\n<span class=\"line\"></span>\n<span class=\"line\"><span style=\"color:#24292E;--shiki-dark:#E1E4E8\">{</span></span>\n<span class=\"line\"><span style=\"color:#005CC5;--shiki-dark:#79B8FF\">  \"id\"</span><span style=\"color:#24292E;--shiki-dark:#E1E4E8\">: </span><span style=\"color:#032F62;--shiki-dark:#9ECBFF\">\"456\"</span><span style=\"color:#24292E;--shiki-dark:#E1E4E8\">,</span></span>\n<span class=\"line\"><span style=\"color:#005CC5;--shiki-dark:#79B8FF\">  \"title\"</span><span style=\"color:#24292E;--shiki-dark:#E1E4E8\">: </span><span style=\"color:#032F62;--shiki-dark:#9ECBFF\">\"Lemon drizzle cake\"</span><span style=\"color:#24292E;--shiki-dark:#E1E4E8\">,</span></span>\n<span class=\"line\"><span style=\"color:#005CC5;--shiki-dark:#79B8FF\">  \"links\"</span><span style=\"color:#24292E;--shiki-dark:#E1E4E8\">: {</span><span style=\"color:#005CC5;--shiki-dark:#79B8FF\">\"author\"</span><span style=\"color:#24292E;--shiki-dark:#E1E4E8\">: </span><span style=\"color:#032F62;--shiki-dark:#9ECBFF\">\"/users/1234\"</span><span style=\"color:#24292E;--shiki-dark:#E1E4E8\">, </span><span style=\"color:#005CC5;--shiki-dark:#79B8FF\">\"comments\"</span><span style=\"color:#24292E;--shiki-dark:#E1E4E8\">: </span><span style=\"color:#032F62;--shiki-dark:#9ECBFF\">\"/recipes/456/comments\"</span><span style=\"color:#24292E;--shiki-dark:#E1E4E8\">}</span></span>\n<span class=\"line\"><span style=\"color:#24292E;--shiki-dark:#E1E4E8\">}</span></span>\n<span class=\"line\"></span>\n<span class=\"line\"><span style=\"color:#24292E;--shiki-dark:#E1E4E8\">PUT /recipes/</span><span style=\"color:#005CC5;--shiki-dark:#79B8FF\">456</span></span>\n<span class=\"line\"><span style=\"color:#24292E;--shiki-dark:#E1E4E8\">Content-Type: application/json</span></span>\n<span class=\"line\"></span>\n<span class=\"line\"><span style=\"color:#24292E;--shiki-dark:#E1E4E8\">{</span><span style=\"color:#005CC5;--shiki-dark:#79B8FF\">\"title\"</span><span style=\"color:#24292E;--shiki-dark:#E1E4E8\">: </span><span style=\"color:#032F62;--shiki-dark:#9ECBFF\">\"Lemon drizzle cake (gluten-free)\"</span><span style=\"color:#24292E;--shiki-dark:#E1E4E8\">, </span><span style=\"color:#005CC5;--shiki-dark:#79B8FF\">\"servings\"</span><span style=\"color:#24292E;--shiki-dark:#E1E4E8\">: </span><span style=\"color:#005CC5;--shiki-dark:#79B8FF\">8</span><span style=\"color:#24292E;--shiki-dark:#E1E4E8\">}</span></span></code></pre>\n<p>The URLs are nouns and the method carries the action. REST is focused on exposing <strong>data</strong>.</p>\n<h4 id=\"why-rest-suits-public-apis\">Why REST suits public APIs</h4>\n<ul>\n<li><strong>It minimizes coupling.</strong> A client needs only the URIs and the standard methods. Crumb can change how it stores recipes without any client noticing, so REST is the usual choice for public HTTP APIs.</li>\n<li><strong>It is uniform.</strong> Resources are exposed through URIs, <a href=\"https://github.com/for-GET/know-your-http-well/blob/master/headers.md\" target=\"_blank\" rel=\"noopener noreferrer\">representations are described in headers</a>, and actions use the verbs GET, POST, PUT, DELETE and PATCH. Browsers, <code>curl</code>, caches, CDNs and proxies already understand all of it.</li>\n<li><strong>It scales.</strong> Because requests are stateless, any server can handle any request. That makes REST services easy to scale horizontally behind a load balancer and to partition across servers. Fielding lists scalability among the benefits of statelessness.</li>\n</ul>\n<h4 id=\"disadvantages-of-rest\">Disadvantages of REST</h4>\n<ul>\n<li><strong>Not every question fits a resource path.</strong> REST works best when data forms a simple hierarchy, like <code>/users/1234/recipes</code>. \"Every recipe updated in the past hour that matches these dietary tags\" doesn't. It ends up as a mix of path, query parameters and maybe a request body: <code>GET /recipes?updatedSince=2026-10-17T09:00Z&#x26;tags=vegan</code>.</li>\n<li><strong>A few verbs may not fit the action.</strong> \"Move all expired recipes into the archive\" isn't clearly a create, replace or delete. Teams fall back on workarounds like <code>POST /recipes/archive-jobs</code>.</li>\n<li><strong>Nested data takes many round trips.</strong> One recipe page might need the recipe, then its author, then its comments: several requests, often one after another. On a phone with a weak signal, each round trip hurts.</li>\n<li><strong>Responses grow.</strong> As fields are added over time, old clients receive all of them, including ones they never use. Payloads get bigger and slower.</li>\n</ul>\n<p>Fielding named the trade-off himself: \"a uniform interface degrades efficiency, since information is transferred in a standardized form rather than one which is specific to an application's needs.\"</p>","headings":[{"id":"rest-resources-and-representations","title":"REST: resources and representations","depth":3},{"id":"the-four-qualities-of-a-restful-interface","title":"The four qualities of a RESTful interface","depth":4},{"id":"what-rest-calls-look-like","title":"What REST calls look like","depth":4},{"id":"why-rest-suits-public-apis","title":"Why REST suits public APIs","depth":4},{"id":"disadvantages-of-rest","title":"Disadvantages of REST","depth":4}]},{"id":"rpc-and-rest-side-by-side","title":"RPC and REST side by side","html":"<h3 id=\"rpc-and-rest-side-by-side\">RPC and REST side by side</h3>\n<p>The same operations on Crumb, designed both ways. User 1234 saves recipe 456 to their list.</p>\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n<div class=\"table-wrap\"><table><thead><tr><th>Operation</th><th>RPC</th><th>REST</th></tr></thead><tbody><tr><td>Sign up</td><td><code>POST /signup</code></td><td><code>POST /users</code></td></tr><tr><td>Close an account</td><td><code>POST /closeAccount</code> with <code>{\"userId\": \"1234\"}</code></td><td><code>DELETE /users/1234</code></td></tr><tr><td>Read a user</td><td><code>GET /getUser?userId=1234</code></td><td><code>GET /users/1234</code></td></tr><tr><td>Read a user's saved recipes</td><td><code>GET /getSavedRecipes?userId=1234</code></td><td><code>GET /users/1234/saved-recipes</code></td></tr><tr><td>Save a recipe</td><td><code>POST /saveRecipe</code> with <code>{\"userId\": \"1234\", \"recipeId\": \"456\"}</code></td><td><code>POST /users/1234/saved-recipes</code> with <code>{\"recipeId\": \"456\"}</code></td></tr><tr><td>Update a saved recipe's note</td><td><code>POST /updateSavedRecipe</code> with <code>{\"userId\": \"1234\", \"recipeId\": \"456\", \"note\": \"less sugar\"}</code></td><td><code>PUT /users/1234/saved-recipes/456</code> with <code>{\"note\": \"less sugar\"}</code></td></tr><tr><td>Remove a saved recipe</td><td><code>POST /unsaveRecipe</code> with <code>{\"userId\": \"1234\", \"recipeId\": \"456\"}</code></td><td><code>DELETE /users/1234/saved-recipes/456</code></td></tr></tbody></table></div>\n<p>RPC names every action, so the endpoint list grows with each feature. REST keeps a small set of resource URLs and lets the method do the work. REST's methods also tell intermediaries something: a cache knows it can store the <code>GET</code>, and a retry library knows the <code>PUT</code> and <code>DELETE</code> are safe to repeat. Every RPC write is a <code>POST</code>, which tells them nothing.</p>\n<p>Arnaud Lauret's <a href=\"https://apihandyman.io/do-you-really-know-why-you-prefer-rest-over-rpc/\" target=\"_blank\" rel=\"noopener noreferrer\">Do you really know why you prefer REST over RPC?</a> (2015) argues that neither wins in general. RPC can be easier when you mostly expose operations, and REST when you \"deal mainly with data.\" One API can mix both.</p>","headings":[{"id":"rpc-and-rest-side-by-side","title":"RPC and REST side by side","depth":3}]},{"id":"graphql-the-client-picks-the-fields","title":"GraphQL: the client picks the fields","html":"<h3 id=\"graphql-the-client-picks-the-fields\">GraphQL: the client picks the fields</h3>\n<p>Two of REST's problems have names. <strong>Over-fetching</strong> means getting fields you don't need. <strong>Under-fetching</strong> means getting too little per response, so you need more round trips. <strong>GraphQL</strong> addresses both. Its <a href=\"https://graphql.org/learn/introduction/\" target=\"_blank\" rel=\"noopener noreferrer\">introduction</a> defines it as \"a query language for your API, and a server-side runtime for executing queries using a type system you define for your data.\"</p>\n<p>A GraphQL server usually has <a href=\"https://graphql.org/learn/serving-over-http/\" target=\"_blank\" rel=\"noopener noreferrer\">a single endpoint, typically <code>/graphql</code></a>. The client sends a query shaped like the data it wants, so Crumb's recipe page becomes one request:</p>\n<pre class=\"shiki shiki-themes github-light github-dark\" style=\"background-color:#fff;--shiki-dark-bg:#24292e;color:#24292e;--shiki-dark:#e1e4e8\" tabindex=\"0\"><code><span class=\"line\"><span>{</span></span>\n<span class=\"line\"><span>  recipe(id: \"456\") {</span></span>\n<span class=\"line\"><span>    title</span></span>\n<span class=\"line\"><span>    author { name }</span></span>\n<span class=\"line\"><span>    comments(first: 10) { text author { name } }</span></span>\n<span class=\"line\"><span>  }</span></span>\n<span class=\"line\"><span>}</span></span></code></pre>\n<p>The client gets \"just that data in the expected shape with a single request.\" A phone can ask for fewer fields than the website, and old clients never receive fields added later. Instead of versions, you add fields and mark old ones <code>@deprecated</code> until nobody uses them.</p>\n<p>The costs:</p>\n<ul>\n<li><strong>HTTP caching is harder.</strong> The <a href=\"https://graphql.org/learn/caching/\" target=\"_blank\" rel=\"noopener noreferrer\">caching guide</a> notes \"there's no URL-like primitive\" identifying each object. Writes must use <code>POST</code>, and long queries can exceed URL limits for <code>GET</code>.</li>\n<li><strong>Status codes say less.</strong> A response with some data and some errors still returns 2xx, because \"HTTP does not yet have a status code representing 'partial success.'\"</li>\n<li><strong>One query can fan out</strong> to many tables and services, so the server must resolve each field efficiently.</li>\n</ul>\n<p>If you only need a tailored response for one kind of client, a <strong>Backend for Frontend</strong> (BFF) is an alternative: a small server per client type that calls your APIs and returns what each screen needs (<a href=\"/learn/reverse-proxy-and-api-gateway#the-backend-for-frontend-bff-pattern\">Reverse proxies and API gateways</a>).</p>","headings":[{"id":"graphql-the-client-picks-the-fields","title":"GraphQL: the client picks the fields","depth":3}]},{"id":"designing-an-api-that-lasts","title":"Designing an API that lasts","html":"<h3 id=\"designing-an-api-that-lasts\">Designing an API that lasts</h3>\n<h4 id=\"versioning\">Versioning</h4>\n<p>Once an app is on millions of phones, you can't update every client at once, so you need to know which changes break them. GitHub's <a href=\"https://docs.github.com/en/rest/about-the-rest-api/api-versions\" target=\"_blank\" rel=\"noopener noreferrer\">versioning policy</a> is a clear model. \"Adding an optional parameter\" is additive and appears in every supported version. \"Removing or renaming a parameter\" or \"changing the type of a parameter or response field\" is breaking, so it goes into a new version.</p>\n<p>Three common ways to choose a version:</p>\n<ul>\n<li><strong>In the URL</strong>, such as <code>/v2/recipes/456</code>. Simple and visible, but each version is a new set of URLs.</li>\n<li><strong>In a header.</strong> GitHub uses <code>X-GitHub-Api-Version</code> with date-based names and supports old versions \"for at least 24 more months.\" <a href=\"https://docs.stripe.com/api/versioning\" target=\"_blank\" rel=\"noopener noreferrer\">Stripe</a> uses <code>Stripe-Version</code>, releasing monthly versions without breaking changes and a breaking major version twice a year.</li>\n<li><strong>Never break</strong>, only add. GraphQL takes this approach, and Protobuf supports it: old code \"will read new messages without issues, ignoring any newly added fields\" (<a href=\"https://protobuf.dev/overview/\" target=\"_blank\" rel=\"noopener noreferrer\">overview</a>).</li>\n</ul>\n<h4 id=\"pagination\">Pagination</h4>\n<p>\"List recipe 456's comments\" can't return a million rows. APIs return a page plus a way to get the next. Offsets (<code>?offset=40&#x26;limit=20</code>) are simple but get slower deep in a list and can skip or repeat items as new ones arrive. A cursor, an opaque token pointing just past the last item seen, avoids both. <a href=\"/learn/design-social-news-feed#cursor-based-pagination-why-offsets-fail\">Designing a social news feed</a> explains in detail.</p>\n<h4 id=\"idempotency-keys-make-post-safe-to-retry\">Idempotency keys make POST safe to retry</h4>\n<p>To make a <code>POST</code> safe to retry, the client generates a unique <strong>idempotency key</strong> per operation and sends it with every attempt, usually in an <code>Idempotency-Key</code> header. The server stores the result under that key and, on a repeat, returns the stored result instead of doing the work again. <a href=\"https://docs.stripe.com/api/idempotent_requests\" target=\"_blank\" rel=\"noopener noreferrer\">Stripe's rules</a> are a good template:</p>\n<ul>\n<li>Keys should be V4 UUIDs or similarly random strings, up to 255 characters.</li>\n<li>The first result is saved and replayed on retries, \"including <code>500</code> errors.\"</li>\n<li>A retry whose parameters differ from the original's gets an error, which catches reused keys.</li>\n<li>Keys may be removed once they are at least 24 hours old.</li>\n<li><code>GET</code> and <code>DELETE</code> don't need keys, because they \"are idempotent by definition.\"</li>\n</ul>\n<p>An IETF draft to standardize the <a href=\"https://datatracker.ietf.org/doc/draft-ietf-httpapi-idempotency-key-header/\" target=\"_blank\" rel=\"noopener noreferrer\">Idempotency-Key header</a> expired without becoming an RFC, so it remains a convention. <a href=\"/learn/reliability-patterns#idempotency-keys\">Reliability patterns</a> shows how to store and replay results.</p>","headings":[{"id":"designing-an-api-that-lasts","title":"Designing an API that lasts","depth":3},{"id":"versioning","title":"Versioning","depth":4},{"id":"pagination","title":"Pagination","depth":4},{"id":"idempotency-keys-make-post-safe-to-retry","title":"Idempotency keys make POST safe to retry","depth":4}]},{"id":"choosing-between-rest-grpc-and-graphql","title":"Choosing between REST, gRPC and GraphQL","html":"<h3 id=\"choosing-between-rest-grpc-and-graphql\">Choosing between REST, gRPC and GraphQL</h3>\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n<div class=\"table-wrap\"><table><thead><tr><th></th><th>REST over HTTP</th><th>gRPC</th><th>GraphQL</th></tr></thead><tbody><tr><td>Models the API as</td><td>Resources (nouns)</td><td>Procedures (verbs)</td><td>A typed graph the client queries</td></tr><tr><td>Wire format</td><td>Usually JSON</td><td>Protobuf (binary)</td><td>JSON</td></tr><tr><td>HTTP caching and CDNs</td><td>Works for GET</td><td>Not with standard caches</td><td>Harder</td></tr><tr><td>Browsers</td><td>Native</td><td>Needs gRPC-Web and a proxy</td><td>Native</td></tr><tr><td>Streaming</td><td>Not built in (see <a href=\"/learn/realtime\">realtime</a>)</td><td>Four kinds built in</td><td>Subscriptions, if the server supports them</td></tr><tr><td>Debugging</td><td>Readable text, <code>curl</code></td><td>Needs gRPC tools</td><td>Readable, but one URL for everything</td></tr><tr><td>Best for</td><td>Public APIs, simple CRUD, cache-heavy reads</td><td>Internal service calls, low latency, many languages</td><td>Varied clients, nested data</td></tr></tbody></table></div>\n<p>Crumb uses each where it fits. The public API and website use REST over HTTPS, so the CDN can cache <code>GET /recipes/456</code>. Internal services call each other with gRPC, with deadlines on every call. The mobile app could move to GraphQL or a BFF if nested REST calls become a real cost. Live features, like a new comment appearing as you read, need a long-lived connection, which <a href=\"/learn/realtime\">Realtime</a> covers.</p>\n<figure class=\"diagram\" data-text=\"App and browser -> Load balancer (HTTPS); Load balancer -> API servers; API servers -> Nutrition and search services (gRPC); API servers -> Memcached (pooled TCP); API servers -> Database (pooled TCP)\"><div class=\"diagram-scroll\"><svg class=\"dg\" viewBox=\"0 -10 800 249\" width=\"800\" role=\"img\" aria-label=\"Crumb uses a different protocol on each hop.\"><defs><marker id=\"dg-arrow\" viewBox=\"0 0 10 10\" refX=\"9\" refY=\"5\" markerWidth=\"7\" markerHeight=\"7\" orient=\"auto-start-reverse\"><path class=\"dg-arrowhead\" d=\"M0,1 L9,5 L0,9 z\"></path></marker></defs><line class=\"dg-edge\" x1=\"151\" y1=\"42\" x2=\"167\" y2=\"42\" marker-end=\"url(#dg-arrow)\"></line><line class=\"dg-edge\" x1=\"311\" y1=\"42\" x2=\"327\" y2=\"42\" marker-end=\"url(#dg-arrow)\"></line><line class=\"dg-edge\" x1=\"471\" y1=\"42\" x2=\"647\" y2=\"42\" marker-end=\"url(#dg-arrow)\"></line><line class=\"dg-edge\" x1=\"400\" y1=\"69\" x2=\"400\" y2=\"164\" marker-end=\"url(#dg-arrow)\"></line><line class=\"dg-edge\" x1=\"455.4\" y1=\"69\" x2=\"650.3\" y2=\"164\" marker-end=\"url(#dg-arrow)\"></line><rect class=\"dg-node dg-client\" x=\"12\" y=\"16\" width=\"136\" height=\"52\" rx=\"26\"></rect><text class=\"dg-label\" text-anchor=\"middle\"><tspan x=\"80\" y=\"38.5\">App and</tspan><tspan x=\"80\" y=\"54.5\">browser</tspan></text><rect class=\"dg-node dg-box\" x=\"172\" y=\"18\" width=\"136\" height=\"48\" rx=\"8\"></rect><text class=\"dg-label\" text-anchor=\"middle\"><tspan x=\"240\" y=\"46.5\">Load balancer</tspan></text><rect class=\"dg-node dg-box\" x=\"332\" y=\"18\" width=\"136\" height=\"48\" rx=\"8\"></rect><text class=\"dg-label\" text-anchor=\"middle\"><tspan x=\"400\" y=\"46.5\">API servers</tspan></text><rect class=\"dg-node dg-box\" x=\"652\" y=\"16\" width=\"136\" height=\"52\" rx=\"8\"></rect><text class=\"dg-label\" text-anchor=\"middle\"><tspan x=\"720\" y=\"38.5\">Nutrition and</tspan><tspan x=\"720\" y=\"54.5\">search services</tspan></text><path class=\"dg-node dg-store\" d=\"M332,176 a68,7 0 0,1 136,0 v44 a68,7 0 0,1 -136,0 z\"></path><path class=\"dg-line\" d=\"M332,176 a68,7 0 0,0 136,0\"></path><text class=\"dg-label\" text-anchor=\"middle\"><tspan x=\"400\" y=\"205.5\">Memcached</tspan></text><path class=\"dg-node dg-store\" d=\"M652,176 a68,7 0 0,1 136,0 v44 a68,7 0 0,1 -136,0 z\"></path><path class=\"dg-line\" d=\"M652,176 a68,7 0 0,0 136,0\"></path><text class=\"dg-label\" text-anchor=\"middle\"><tspan x=\"720\" y=\"205.5\">Database</tspan></text><rect class=\"dg-edge-bg\" x=\"138.5\" y=\"-6\" width=\"41\" height=\"18\" rx=\"4\"></rect><text class=\"dg-edge-label\" x=\"159\" y=\"7\" text-anchor=\"middle\">HTTPS</text><rect class=\"dg-edge-bg\" x=\"541.6\" y=\"33\" width=\"34.8\" height=\"18\" rx=\"4\"></rect><text class=\"dg-edge-label\" x=\"559\" y=\"46\" text-anchor=\"middle\">gRPC</text><rect class=\"dg-edge-bg\" x=\"364\" y=\"107.5\" width=\"72\" height=\"18\" rx=\"4\"></rect><text class=\"dg-edge-label\" x=\"400\" y=\"120.5\" text-anchor=\"middle\">pooled TCP</text><rect class=\"dg-edge-bg\" x=\"516.8\" y=\"107.5\" width=\"72\" height=\"18\" rx=\"4\"></rect><text class=\"dg-edge-label\" x=\"552.8\" y=\"120.5\" text-anchor=\"middle\">pooled TCP</text></svg></div><figcaption>Crumb uses a different protocol on each hop.</figcaption></figure>","headings":[{"id":"choosing-between-rest-grpc-and-graphql","title":"Choosing between REST, gRPC and GraphQL","depth":3}]},{"id":"check-yourself","title":"Check yourself","html":"<h3 id=\"check-yourself\">Check yourself</h3>\n<details>\n<summary>Crumb's app sends <code>POST /recipes</code> to publish a recipe, and it times out with no response. Should the app retry automatically? What would make a retry safe?</summary>\n<p>Not as things stand. POST isn't idempotent, and RFC 9110 says clients shouldn't automatically retry non-idempotent requests. The first request may have succeeded with only the reply lost, so a retry could publish the recipe twice. The fix is an idempotency key. The app generates a UUID when the user taps \"Publish\" and sends it with every attempt. The server saves the result under that key and returns it on a retry instead of creating a second recipe.</p>\n</details>\n<details>\n<summary>A teammate says PATCH responses are cacheable, so the CDN should cache <code>PATCH /recipes/456</code>. What's wrong with that?</summary>\n<p>RFC 5789 lets a PATCH response be cached only with explicit freshness information and a <code>Content-Location</code> header matching the URL. Even then, it can only answer later <code>GET</code> or <code>HEAD</code> requests, never another PATCH. Most caches only support GET and HEAD anyway. A PATCH that passes through a cache also marks the stored copy of <code>/recipes/456</code> as stale. Cache the GET responses instead, and let PATCH invalidate them.</p>\n</details>\n<details>\n<summary>Crumb is adding a live cook-along where the host's video reaches viewers with as little delay as possible. TCP or UDP for the video, and why?</summary>\n<p>UDP, or a protocol built on it. For live video, a late frame is worse than a missing one. With TCP, one lost packet holds back everything after it until it is resent, and congestion control slows sending after the loss. With UDP, the player skips the gap and shows the newest frames, and the application adds error correction only where it matters. Sign-in and chat messages around the session can still use TCP, because they must arrive intact.</p>\n</details>\n<details>\n<summary>Crumb's recipe page loads over HTTP/2. On a lossy mobile network, the text, photos and comments all stall together whenever one packet is lost. Why, and what would help?</summary>\n<p>HTTP/2 sends them as separate streams, but they share one TCP connection. TCP delivers bytes strictly in order, so one lost packet holds back all later bytes, including those for streams that lost nothing. RFC 9113 says HTTP/2 doesn't address TCP head-of-line blocking. HTTP/3 does. It runs over QUIC, which recovers losses per stream, so a lost packet blocks only the streams whose data it carried. Crumb can enable HTTP/3 at its load balancer or CDN and advertise it with an <code>Alt-Svc</code> header.</p>\n</details>\n<details>\n<summary>Design REST endpoints for Crumb's comments: list a recipe's comments, add one, edit one and delete one. Then name an operation that doesn't fit REST cleanly.</summary>\n<p>One reasonable design:</p>\n<ul>\n<li><code>GET /recipes/456/comments?cursor=...</code> lists comments a page at a time.</li>\n<li><code>POST /recipes/456/comments</code> adds one and returns <code>201 Created</code> with a <code>Location</code> such as <code>/comments/789</code>.</li>\n<li><code>PATCH /comments/789</code> edits one, returning <code>403</code> if the caller didn't write it.</li>\n<li><code>DELETE /comments/789</code> deletes one and returns <code>204</code>.</li>\n</ul>\n<p>\"Hide every comment by a spammer across all recipes\" doesn't fit. It isn't one resource, and no standard verb matches it. Teams usually model it as a new resource, such as <code>POST /moderation-actions</code> with <code>{\"type\": \"hide_all\", \"userId\": \"666\"}</code>.</p>\n</details>\n<details>\n<summary>Crumb's API servers call the nutrition service 2,000 times a second. REST with JSON or gRPC? What do you gain and give up?</summary>\n<p>gRPC fits a high-volume internal call between services Crumb controls. Protobuf is smaller and faster than JSON, HTTP/2 lets many calls share a connection, the <code>.proto</code> file gives both teams a contract with generated code, and deadlines are built in. The costs are tighter coupling and harder debugging. Changing <code>GetFacts</code> means regenerating clients, so the team should only ever add fields, and debugging needs gRPC tools instead of <code>curl</code>. If browsers ever need the service, an API gateway can translate JSON over HTTP into gRPC.</p>\n</details>\n<h3 id=\"further-reading\">Further reading</h3>\n<p>Specifications:</p>\n<ul>\n<li><a href=\"https://www.rfc-editor.org/rfc/rfc9110\" target=\"_blank\" rel=\"noopener noreferrer\">RFC 9110: HTTP Semantics</a>: the definitive source for methods and status codes. Section 9 covers methods.</li>\n<li><a href=\"https://www.rfc-editor.org/rfc/rfc9293\" target=\"_blank\" rel=\"noopener noreferrer\">RFC 9293: TCP</a>: the 2022 TCP standard. Section 3.5 has the handshake example used above.</li>\n<li><a href=\"https://www.rfc-editor.org/rfc/rfc768\" target=\"_blank\" rel=\"noopener noreferrer\">RFC 768: UDP</a>: all of UDP in three pages.</li>\n<li><a href=\"https://www.rfc-editor.org/rfc/rfc9113\" target=\"_blank\" rel=\"noopener noreferrer\">RFC 9113 (HTTP/2)</a> and <a href=\"https://www.rfc-editor.org/rfc/rfc9114\" target=\"_blank\" rel=\"noopener noreferrer\">RFC 9114 (HTTP/3)</a>: their introductions explain in a few paragraphs why each version exists.</li>\n<li><a href=\"https://www.rfc-editor.org/rfc/rfc5789\" target=\"_blank\" rel=\"noopener noreferrer\">RFC 5789: PATCH</a>: short, and clear on why PATCH is neither safe nor idempotent.</li>\n</ul>\n<p>HTTP, TCP and UDP:</p>\n<ul>\n<li><a href=\"https://developer.mozilla.org/en-US/docs/Web/HTTP/Guides/Evolution_of_HTTP\" target=\"_blank\" rel=\"noopener noreferrer\">Evolution of HTTP</a>: MDN's history from HTTP/0.9 to HTTP/3.</li>\n<li><a href=\"https://www.f5.com/glossary/hypertext-transfer-protocol-http\" target=\"_blank\" rel=\"noopener noreferrer\">What is HTTP?</a>: a short glossary entry from F5. The old NGINX link redirects here.</li>\n<li><a href=\"https://laracasts.com/discuss/channels/general-discussion/whats-the-differences-between-put-and-patch?page=1\" target=\"_blank\" rel=\"noopener noreferrer\">What's the difference between PUT and PATCH?</a>: a forum thread with practical examples of full and partial updates.</li>\n<li><a href=\"https://gafferongames.com/post/udp_vs_tcp/\" target=\"_blank\" rel=\"noopener noreferrer\">UDP vs. TCP</a>: Glenn Fiedler on why real-time games avoid TCP.</li>\n<li><a href=\"https://stackoverflow.com/questions/5970383/difference-between-tcp-and-udp\" target=\"_blank\" rel=\"noopener noreferrer\">Difference between TCP and UDP</a>: a Stack Overflow thread with short, clear answers.</li>\n<li><a href=\"https://en.wikipedia.org/wiki/Transmission_Control_Protocol\" target=\"_blank\" rel=\"noopener noreferrer\">Transmission Control Protocol</a> and <a href=\"https://en.wikipedia.org/wiki/User_Datagram_Protocol\" target=\"_blank\" rel=\"noopener noreferrer\">User Datagram Protocol</a>: Wikipedia's articles, with header diagrams and connection states.</li>\n<li><a href=\"https://www.cs.bu.edu/~jappavoo/jappavoo.github.com/451/papers/memcache-fb.pdf\" target=\"_blank\" rel=\"noopener noreferrer\">Scaling Memcache at Facebook</a>: the NSDI 2013 paper. Section 3.1 covers UDP reads, TCP writes and shared connections.</li>\n</ul>\n<p>RPC and REST:</p>\n<ul>\n<li><a href=\"https://ics.uci.edu/~fielding/pubs/dissertation/rest_arch_style.htm\" target=\"_blank\" rel=\"noopener noreferrer\">Fielding's dissertation, chapter 5</a>: the original definition of REST, readable without the rest of the dissertation.</li>\n<li><a href=\"https://roy.gbiv.com/untangled/2008/rest-apis-must-be-hypertext-driven\" target=\"_blank\" rel=\"noopener noreferrer\">REST APIs must be hypertext-driven</a>: Fielding on what most \"REST\" APIs leave out.</li>\n<li><a href=\"https://apihandyman.io/do-you-really-know-why-you-prefer-rest-over-rpc/\" target=\"_blank\" rel=\"noopener noreferrer\">Do you really know why you prefer REST over RPC?</a>: Arnaud Lauret's operation-by-operation comparison.</li>\n<li><a href=\"https://web.archive.org/web/20170608193645/http://etherealbits.com/2012/12/debunking-the-myths-of-rpc-rest/\" target=\"_blank\" rel=\"noopener noreferrer\">Debunking the Myths of RPC &#x26; REST</a>: Tyson Trautmann on why HTTP caching works for REST and not for RPC (archived copy).</li>\n<li><a href=\"https://stackoverflow.com/questions/15056878/rest-vs-json-rpc\" target=\"_blank\" rel=\"noopener noreferrer\">REST vs JSON-RPC?</a>: a Stack Overflow discussion of the trade-offs.</li>\n<li><a href=\"https://web.archive.org/web/20221030091901/https://arstechnica.com/civis/viewtopic.php?t=1190508\" target=\"_blank\" rel=\"noopener noreferrer\">Why REST for internal use and not RPC</a>: an Ars Technica forum thread on the same question for internal services (archived copy).</li>\n<li><a href=\"https://web.archive.org/web/20260820115227/https://www.restcookbook.com/Basics/hateoas/\" target=\"_blank\" rel=\"noopener noreferrer\">HATEOAS</a>: the REST Cookbook's short page on hypermedia links (archived copy).</li>\n<li><a href=\"https://github.com/for-GET/know-your-http-well/blob/master/headers.md\" target=\"_blank\" rel=\"noopener noreferrer\">Know your HTTP headers</a>: a table of HTTP headers with links to the specs that define them.</li>\n<li><a href=\"https://tianpan.co/notes/2016-02-13-crack-the-system-design-interview\" target=\"_blank\" rel=\"noopener noreferrer\">Crack the system design interview</a>: an interview walkthrough of a photo-sharing service, including how services find each other. The old puncsky.com link redirects here.</li>\n<li><a href=\"https://engineering.fb.com/2014/02/20/open-source/under-the-hood-building-and-open-sourcing-fbthrift/\" target=\"_blank\" rel=\"noopener noreferrer\">Building and open-sourcing fbthrift</a>: Facebook's account of where Thrift came from.</li>\n</ul>\n<p>gRPC, GraphQL and API design:</p>\n<ul>\n<li><a href=\"https://grpc.io/docs/what-is-grpc/core-concepts/\" target=\"_blank\" rel=\"noopener noreferrer\">gRPC core concepts</a> and <a href=\"https://github.com/grpc/grpc/blob/master/doc/PROTOCOL-HTTP2.md\" target=\"_blank\" rel=\"noopener noreferrer\">gRPC over HTTP/2</a>: the method kinds, deadlines, and how calls map onto HTTP/2.</li>\n<li><a href=\"https://grpc.io/blog/state-of-grpc-web/\" target=\"_blank\" rel=\"noopener noreferrer\">The state of gRPC in the browser</a>: why browsers need gRPC-Web and a proxy.</li>\n<li><a href=\"https://graphql.org/learn/introduction/\" target=\"_blank\" rel=\"noopener noreferrer\">Introduction to GraphQL</a> and <a href=\"https://graphql.org/learn/caching/\" target=\"_blank\" rel=\"noopener noreferrer\">GraphQL caching</a>: the official guide, including where caching gets harder.</li>\n<li><a href=\"https://docs.stripe.com/api/idempotent_requests\" target=\"_blank\" rel=\"noopener noreferrer\">Stripe idempotent requests</a>, <a href=\"https://docs.stripe.com/api/versioning\" target=\"_blank\" rel=\"noopener noreferrer\">Stripe versioning</a> and <a href=\"https://docs.github.com/en/rest/about-the-rest-api/api-versions\" target=\"_blank\" rel=\"noopener noreferrer\">GitHub API versions</a>: real public APIs' rules, worth copying.</li>\n</ul>","headings":[{"id":"check-yourself","title":"Check yourself","depth":3},{"id":"further-reading","title":"Further reading","depth":3}]}]}