<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Kevin McDonald</title>
    <description>The latest articles on DEV Community by Kevin McDonald (@sudorandom).</description>
    <link>https://dev.to/sudorandom</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F203549%2F9300d0e1-7673-46f7-a4e0-0f679f92b71a.jpg</url>
      <title>DEV Community: Kevin McDonald</title>
      <link>https://dev.to/sudorandom</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/sudorandom"/>
    <language>en</language>
    <item>
      <title>My Favorite Interview Question</title>
      <dc:creator>Kevin McDonald</dc:creator>
      <pubDate>Mon, 22 Sep 2025 10:00:00 +0000</pubDate>
      <link>https://dev.to/sudorandom/my-favorite-interview-question-4150</link>
      <guid>https://dev.to/sudorandom/my-favorite-interview-question-4150</guid>
      <description>&lt;p&gt;Interviews are a strange dance. As an interviewer, you’re trying to get a signal on a candidate’s skills, experience, and personality in a very short amount of time. As a candidate, you’re trying to showcase your best self while under pressure. It’s a tough situation for everyone involved.&lt;/p&gt;

&lt;p&gt;Over the years, I’ve asked a lot of interview questions. Some have been great, some have been duds. But I always come back to my absolute favorite question:&lt;/p&gt;

&lt;p&gt;“How does the internet work?”&lt;/p&gt;

&lt;p&gt;I know what you’re thinking. That’s a huge, open-ended question! And you’re right, it is. That’s exactly why I love it. You also might be thinking that this is a super common question. I agree! But unlike most questions, this one is still extremely useful despite its popularity.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why this question is so powerful
&lt;/h2&gt;

&lt;p&gt;This question is a fantastic tool for a few reasons:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;It’s a blank canvas.&lt;/strong&gt; The candidate can start anywhere they want. Do they start with the physical layer? The application layer? The philosophy of interconnectedness? It gives them a chance to show me what they think is important.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;It reveals their depth of knowledge.&lt;/strong&gt; The question can be answered at many different levels of abstraction. A junior engineer might give a high-level overview, while a senior engineer might be able to dive into the nitty-gritty details of TCP handshakes and BGP routing.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;It tests communication skills.&lt;/strong&gt; Can the candidate take a complex topic and explain it in a clear, concise way? This is a crucial skill for any engineer, especially in a team environment.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;It’s a conversation starter.&lt;/strong&gt; The question often leads to a natural back-and-forth conversation. I can ask follow-up questions to probe deeper into areas where the candidate seems knowledgeable or to help them along if they’re struggling.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Sometimes, the sheer open-endedness of the question can be a bit overwhelming for a candidate. If they’re struggling to start or getting too philosophical, I’ll give them a more concrete prompt:&lt;/p&gt;

&lt;p&gt;“What happens when you type google.com into your browser and press Enter?”&lt;/p&gt;

&lt;p&gt;This usually gets the ball rolling. Here’s a breakdown of the kind of answer I’m looking for, with varying levels of detail depending on the candidate’s seniority.&lt;/p&gt;

&lt;h3&gt;
  
  
  What I expect from most candidates
&lt;/h3&gt;

&lt;p&gt;For any software engineering role that involves web development, I expect the candidate to be able to walk me through the following steps:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;DNS Lookup:&lt;/strong&gt; The browser needs to translate the human-readable domain name &lt;code&gt;google.com&lt;/code&gt; into a machine-readable IP address. This involves checking the browser’s cache, the OS’s cache, and then querying a DNS resolver, which has its own upstream sources and cache.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;TCP Connection:&lt;/strong&gt; Once the browser has the IP address, it establishes a TCP connection with the server. This involves establishing a connection using TCP’s three-way handshake (SYN, SYN-ACK, ACK).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;HTTP Request/Response:&lt;/strong&gt; The browser sends an HTTP &lt;code&gt;GET&lt;/code&gt; request to the server. The server processes the request and sends back an HTTP response, which includes the HTML, CSS, and JavaScript files that make up the Google homepage.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Rendering:&lt;/strong&gt; The browser parses the HTML and renders the page. It also executes the JavaScript, which might make additional requests to the server to fetch more data.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  What I hope for from senior candidates
&lt;/h3&gt;

&lt;p&gt;For a more senior role, especially at a company that deals with networking or infrastructure, I’m hoping for a deeper dive into some of the following topics:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;ARP (Address Resolution Protocol):&lt;/strong&gt; Before the browser can even send a packet to the router, it needs to know the router’s MAC address. This is where ARP comes in.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;DHCP (Dynamic Host Configuration Protocol):&lt;/strong&gt; - How did our computer get an IP address in the first place? A senior candidate might explain that the machine likely requested an IP address, a subnet mask, the default gateway’s IP, and the DNS server’s IP from a DHCP server on the local network.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;NAT (Network Address Translation):&lt;/strong&gt; - To conserve the limited global supply of IPv4 addresses, most home and office networks use a single public IP address for many devices. NAT is the mechanism that allows a router to translate between private, internal IP addresses and that single public one.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;BGP (Border Gateway Protocol):&lt;/strong&gt; How does my request find its way from my local network to Google’s servers, potentially halfway across the world? Mentioning BGP shows an understanding that the internet is a ’network of networks’ and that routing between these large autonomous systems is a complex, solved problem.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;HTTP/2 and HTTP/3 (QUIC):&lt;/strong&gt; Is the connection really just a single TCP connection? A senior candidate might discuss the limitations of TCP, like head-of-line blocking, and how newer protocols solve them. Pointing out that since the destination is Google, the connection is likely using HTTP/3 (which runs on QUIC over UDP) is a huge plus.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;TCP Keep-Alives:&lt;/strong&gt; How does the connection stay open for subsequent requests? What are the trade-offs of keep-alives?&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Caching:&lt;/strong&gt; Where does caching happen in this whole process? DNS caching, browser caching, CDN caching… there are many layers.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Pre-flight Requests (CORS):&lt;/strong&gt; If the page makes requests to other domains, the browser might need to make a pre-flight &lt;code&gt;OPTIONS&lt;/code&gt; request to check if the cross-origin request is allowed.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Packet Breakdown:&lt;/strong&gt; What does an actual packet look like? What are the different headers (Ethernet, IP, TCP, HTTP)?&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  It’s not about getting it “right”
&lt;/h2&gt;

&lt;p&gt;I want to be clear: I don’t expect any candidate to know everything about the internet. That’s impossible. What I’m looking for is a candidate who has a solid foundation, can reason about complex systems, and is curious to learn more. How does the candidate behave when I press into an area that they don’t know a lot about? Are they curious? Are they willing to say they don’t know? Do they make things up? All of that is very useful for evaluating skill and knowledge level.&lt;/p&gt;

&lt;p&gt;Sometimes a candidate’s answer goes in a completely unexpected direction. I once had a candidate describe the hardware interrupts and OS context switches that happen just from typing ‘google.com’. Even though it was a tangent, it was a valuable signal that showed me how they reason about systems from the hardware up.&lt;/p&gt;

&lt;p&gt;This question is a fantastic way to gauge all of those things. It’s a journey we go on together, and I often learn something new from the candidate’s answer.&lt;/p&gt;

&lt;p&gt;What’s your go-to interview question? I’d love to hear it.&lt;/p&gt;

</description>
      <category>interview</category>
      <category>hiring</category>
      <category>networking</category>
    </item>
    <item>
      <title>Visualizing the Internet (2025)</title>
      <dc:creator>Kevin McDonald</dc:creator>
      <pubDate>Mon, 16 Jun 2025 00:00:00 +0000</pubDate>
      <link>https://dev.to/sudorandom/visualizing-the-internet-2025-102i</link>
      <guid>https://dev.to/sudorandom/visualizing-the-internet-2025-102i</guid>
      <description>&lt;p&gt;For the past couple of years, I’ve been creating visualizations of the internet’s physical infrastructure. This project pieces together data from a few sources, and for me, seeing this data visualized together is compelling. These maps show the undersea fiber optic cables that form the backbone of global connectivity and the Internet Exchange Points (IXPs) where networks meet. This year, I’m thrilled to announce a major evolution of the project. Instead of static images and pre-rendered videos, the Internet Map is now a fully interactive, animated map that you can see online.&lt;/p&gt;

&lt;p&gt;You can explore the new map live at &lt;a href="https://map.kmcd.dev" rel="noopener noreferrer"&gt;&lt;strong&gt;map.kmcd.dev&lt;/strong&gt;&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;The new version lets you take control. You can pan, zoom, and step through time from the earliest days of the subsea cable network to the latest deployments in 2025. A new statistics panel provides a detailed snapshot for any given year, offering a richer understanding of how our connected world has grown.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://map.kmcd.dev" rel="noopener noreferrer"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fg3p0vjdvllrykyrv83qk.png" alt="Map of the Internet" width="800" height="423"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  What You’re Looking At
&lt;/h2&gt;

&lt;p&gt;The map visualizes two critical components of the internet’s physical layer.&lt;/p&gt;

&lt;h3&gt;
  
  
  A Note on What You’re Seeing (and Not Seeing)
&lt;/h3&gt;

&lt;p&gt;It’s important to note that this map visualizes publicly available data, which doesn’t capture the full picture of global connectivity. The city peering information, for instance, is sourced from &lt;a href="https://www.peeringdb.com/" rel="noopener noreferrer"&gt;PeeringDB&lt;/a&gt;, which tracks publicly advertised connections at IXPs. A vast amount of internet traffic also flows through private peering arrangements and paid transit links that are not publicly documented and therefore do not appear here.&lt;/p&gt;

&lt;p&gt;Similarly, the map focuses on the &lt;em&gt;intercontinental&lt;/em&gt; backbone of submarine cables. It does not show the incredibly dense web of terrestrial fiber optic cables that run under our streets and alongside major roads. While that data would be fascinating, visualizing it would be overwhelming, and acquiring a complete dataset is nearly impossible as network providers rarely share this proprietary information.&lt;/p&gt;

&lt;h3&gt;
  
  
  Submarine Cables
&lt;/h3&gt;

&lt;p&gt;The lines snaking across the ocean floors are &lt;a href="https://en.wikipedia.org/wiki/Submarine_communications_cable" rel="noopener noreferrer"&gt;submarine communications cables&lt;/a&gt;. These bundles of fiber optic strands are the high-speed data arteries that connect continents. Laying and maintaining them is a modern marvel of engineering, involving everything from specialized cable-laying ships to underwater robots for repairs. As you explore the map, you can see how the web of these cables has become denser over time, enabling the global, real-time communication we now take for granted. By the start of 2025, the network has grown to &lt;strong&gt;599&lt;/strong&gt; cables, spanning a staggering &lt;strong&gt;1,602,092 kilometers&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fq0uvltp53ed78tscnocl.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fq0uvltp53ed78tscnocl.png" alt="Map of the Internet" width="800" height="449"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h4&gt;
  
  
  A Physical Target: Vulnerabilities and Sabotage
&lt;/h4&gt;

&lt;p&gt;While these cables are heavily armored, especially in shallower coastal waters where most damage occurs, their isolation on the seabed makes them vulnerable. For decades, the most common threat has been accidental damage from fishing trawlers and dragged anchors. However, in recent years, a more alarming trend has emerged: intentional sabotage. The increasing frequency of suspicious cable cuts suggests that these vital arteries of communication are becoming targets in geopolitical conflicts, a reality that may have brought many new visitors to this map.&lt;/p&gt;

&lt;p&gt;Here are just a few of the many recent incidents:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;&lt;a href="https://jamestown.org/program/strangers-on-a-seabed-sino-russian-collaboration-on-undersea-cable-sabotage-operations/" rel="noopener noreferrer"&gt;The Balticconnector Pipeline and Cable Damage (October 2023)&lt;/a&gt;&lt;/strong&gt;: The Balticconnector gas pipeline and two telecom cables between Finland and Estonia were damaged by a dragged anchor from the Hong Kong-flagged ship &lt;em&gt;Newnew Polar Bear&lt;/em&gt;. China later admitted its vessel was responsible but claimed it was an accident.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;&lt;a href="https://www.cbsnews.com/news/undersea-cables-cut-europe-finland-germany-hint-russia-sabotage/" rel="noopener noreferrer"&gt;The C-Lion1 and BCS East-West Interlink Cuts (November 2024)&lt;/a&gt;&lt;/strong&gt;: Two key telecom cables in the Baltic Sea, C-Lion1 (Finland-Germany) and BCS East-West Interlink (Lithuania-Sweden), were severed. The Chinese-owned cargo ship &lt;em&gt;Yi Peng 3&lt;/em&gt; was the primary suspect after it was observed making anomalous movements in the area.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;&lt;a href="https://apnews.com/article/nato-france-russia-baltic-cables-ships-damage-764964a275530915c2cc5af1125ec125" rel="noopener noreferrer"&gt;The Christmas Day Baltic Cable Cuts (December 2024)&lt;/a&gt;&lt;/strong&gt;: On Christmas Day 2024, the Estlink 2 power cable and other telecom cables between Finland and Estonia were damaged by a dragged anchor. Finnish authorities seized the suspected vessel, the oil tanker &lt;em&gt;Eagle S&lt;/em&gt;, which was identified as part of Russia’s “shadow fleet”.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;&lt;a href="https://www.vice.com/en/article/taiwan-internet-cables-matsu-china/" rel="noopener noreferrer"&gt;The Matsu Islands Blackout (February 2023)&lt;/a&gt;&lt;/strong&gt;: Two undersea cables connecting Taiwan to its outlying Matsu Islands were severed by Chinese vessels, leaving the 14,000 residents with severely disrupted internet for over 50 days. The incident highlighted the societal impact of such disruptions and was seen as part of a broader pressure campaign by China.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;&lt;a href="https://www.twz.com/news-features/taiwan-coast-guard-blames-chinese-owned-ship-for-cutting-undersea-communications-cable" rel="noopener noreferrer"&gt;The Trans-Pacific Express Cable Cut (January 2025)&lt;/a&gt;&lt;/strong&gt;: The major Trans-Pacific Express international cable was cut near Taiwan, with suspicion falling on the Chinese-owned cargo ship &lt;em&gt;Shunxin 39&lt;/em&gt;. The vessel had a history of using multiple identities to evade tracking and sailed erratically over the cable’s location before the incident.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;&lt;a href="https://www.csis.org/analysis/red-sea-cable-damage-reveals-soft-underbelly-global-economy" rel="noopener noreferrer"&gt;The Red Sea Cable Disruption (February 2024)&lt;/a&gt;&lt;/strong&gt;: Three critical cables in the Red Sea were severed by the anchor of the sinking cargo ship &lt;em&gt;Rubymar&lt;/em&gt;, which had been struck by a Houthi missile. The incident disrupted a significant portion of Europe-Asia data traffic and highlighted the vulnerability of infrastructure in contested maritime chokepoints.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;&lt;a href="https://www.cbc.ca/news/canada/nova-scotia/bell-subsea-fibre-optic-cable-newfoundland-1.7461963" rel="noopener noreferrer"&gt;The Gulf of St. Lawrence Sabotage (December 2023 &amp;amp; 2024)&lt;/a&gt;&lt;/strong&gt;: A subsea cable connecting Nova Scotia and Newfoundland was deliberately cut in December 2023 and again in December 2024. Evidence showed an “angle grinder cut” through the steel-wrapped cable, confirming sabotage, though the perpetrator and motive remain unknown.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;These examples represent only a fraction of such incidents, which have escalated in frequency and impact in recent years.&lt;/p&gt;

&lt;h3&gt;
  
  
  Internet Exchange Points (IXPs)
&lt;/h3&gt;

&lt;p&gt;The circles on the map represent cities with &lt;a href="https://www.cloudflare.com/learning/cdn/glossary/internet-exchange-point-ixp/" rel="noopener noreferrer"&gt;Internet Exchange Points&lt;/a&gt;. If submarine cables are the interstate highways of the internet, then IXPs are the bustling, hyper-connected metropolitan areas where all the traffic is headed.&lt;/p&gt;

&lt;p&gt;An IXP is a physical data center, or a set of connected data centers, where many different networks can physically plug into each other to exchange traffic directly. This process is called “peering.”&lt;/p&gt;

&lt;p&gt;So why do hundreds of networks choose to gather in the same buildings in cities like Frankfurt or Amsterdam? The answer is a powerful network effect that you could call digital gravity. The value of an IXP is determined by the networks present there. Once a major network joins, it becomes exponentially more attractive for others to join as well.&lt;/p&gt;

&lt;p&gt;The most powerful sources of this gravity are large content providers like Google, Meta, Apple, and Netflix. These companies have an “open peering policy.” In essence, they are saying to any Internet Service Provider (ISP): “Connect with us directly here at the IXP, and we will give your customers a faster, better path to our services.”&lt;/p&gt;

&lt;p&gt;This creates a powerful win-win scenario:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The ISP wins because their customers get lightning-fast, low-latency access to YouTube, Google Drive, or Apple’s App Store. This makes the ISP’s own service more valuable and competitive.&lt;/li&gt;
&lt;li&gt;Google and Apple win because they get to deliver their content without paying high fees to third-party backbone carriers. Every byte of data they serve over a direct peering connection is money saved.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This economic incentive is the engine that drives the growth you see on the map. ISPs flock to the IXPs where the big content providers are, which in turn attracts more content providers, creating a feedback loop of ever-increasing capacity and value. This is why a handful of cities have become global hubs with staggering traffic volumes, while others remain smaller, regional nodes.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F79vkzai8rkeumuu9q22h.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F79vkzai8rkeumuu9q22h.png" alt=" " width="798" height="804"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  The World in 2025: A Snapshot
&lt;/h2&gt;

&lt;p&gt;The animation and data now extend to 2025, revealing significant ongoing investment. In this year alone, &lt;strong&gt;31 new cables&lt;/strong&gt; were added (or are promised very soon), stretching over &lt;strong&gt;144,320 kilometers&lt;/strong&gt; , enough to circle the earth &lt;em&gt;three and a half times&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;Some of the longest and most impactful new cables of 2025 include:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;&lt;a href="https://www.submarinenetworks.com/en/systems/trans-pacific/bifrost" rel="noopener noreferrer"&gt;Bifrost&lt;/a&gt; (19,888 km):&lt;/strong&gt; A monumental project directly connecting Singapore to North America via Indonesia, the Philippines, and Guam. It’s a joint effort by Meta, Keppel, and Telin to bolster connectivity across the Asia-Pacific region.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;a href="https://www.submarinenetworks.com/en/systems/trans-pacific/echo/echo-cable-system-overview" rel="noopener noreferrer"&gt;Echo&lt;/a&gt; (17,184 km):&lt;/strong&gt; Another critical trans-Pacific cable, built by Google and Meta, that forges a new, resilient path from the U.S. to Singapore, also landing in Guam and Indonesia. This cable deliberately avoids the crowded northern routes to increase network diversity.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;a href="https://www.submarinenetworks.com/en/systems/brazil-us/firmina" rel="noopener noreferrer"&gt;Firmina&lt;/a&gt; (14,517 km):&lt;/strong&gt; A Google-led cable enhancing the North-South America connection. It runs from the U.S. East Coast to Argentina, with landings in Brazil and Uruguay, dramatically improving access to Google services in South America.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;a href="https://www.submarinenetworks.com/en/systems/trans-pacific/tpu" rel="noopener noreferrer"&gt;TPU&lt;/a&gt; (13,470 km):&lt;/strong&gt; A Google-owned cable system connecting the U.S. with Taiwan and the Philippines, bolstering trans-Pacific capacity.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;a href="https://www.submarinenetworks.com/en/systems/trans-pacific/juno" rel="noopener noreferrer"&gt;JUNO&lt;/a&gt; (11,710 km):&lt;/strong&gt; A cable system by Seren Juno Network connecting Japan to the U.S., utilizing advanced technology to offer a high number of fiber pairs and enhance communication resiliency.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Regional Peering Powerhouses
&lt;/h2&gt;

&lt;p&gt;Looking at the total peering capacity reveals a clear global hierarchy. Europe remains the undisputed leader, with an incredible &lt;strong&gt;1.5 Pbit/s&lt;/strong&gt; of capacity. Asia and North America follow with robust networks of their own, while South America shows impressive growth.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fzybjlpb10svy98dvmc7b.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fzybjlpb10svy98dvmc7b.png" alt=" " width="585" height="462"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This massive regional capacity is concentrated in a few key metropolitan hubs. The list of top peering cities shows just how vital they are to the global network:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Amsterdam, NL&lt;/strong&gt; : 200 Tbit (+12.7 Tbit)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Frankfurt, DE&lt;/strong&gt; : 166 Tbit (+8.19 Tbit)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;São Paulo, BR&lt;/strong&gt; : 157 Tbit (+3.16 Tbit)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;London, GB&lt;/strong&gt; : 113 Tbit (+3.21 Tbit)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Tokyo, JP&lt;/strong&gt; : 90.2 Tbit (+2.18 Tbit)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The growth in a city like São Paulo is remarkable and shows the increasing investment in internet infrastructure in South America, directly supported by new cables like Firmina.&lt;/p&gt;

&lt;h2&gt;
  
  
  How It’s Made: The New Tech Stack
&lt;/h2&gt;

&lt;p&gt;The transition to an interactive map required a complete overhaul of the technology stack. The previous versions, which relied on generating static SVG images, faced several challenges. It was difficult to dynamically size the lines representing cables and find the right balance of detail for country borders; too much detail slowed the map down, while too little looked simplistic when zoomed in.&lt;/p&gt;

&lt;p&gt;The solution was to adopt a tile-based methodology, where map tiles at different levels of detail are fetched dynamically as a user zooms—the same concept used by Google Maps. I was faced with a choice: implement this highly complex tiling logic myself or use a well-supported library. Since the project’s focus was on data visualization, I opted for the more direct path by using &lt;a href="https://leafletjs.com/" rel="noopener noreferrer"&gt;Leaflet&lt;/a&gt;, a powerful library for creating dynamic and interactive maps.&lt;/p&gt;

&lt;p&gt;The back-end Go scripts that gather and process the data from sources like &lt;a href="https://telegeography.com/" rel="noopener noreferrer"&gt;TeleGeography&lt;/a&gt; and &lt;a href="https://www.peeringdb.com/" rel="noopener noreferrer"&gt;PeeringDB&lt;/a&gt; were largely unchanged, only needing a few new fields in the JSON output to power the new front end.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fd81exnt3bz08em84z3n1.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fd81exnt3bz08em84z3n1.png" alt="Map of the Internet" width="500" height="701"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Beyond the core technology, I also wanted to incorporate a few small details to improve the user experience and make the map feel more intuitive and personal:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Personalized View&lt;/strong&gt; : The map automatically geolocates your region and centers the initial view there. My hope is that the map feels familiar and relevant the moment you open it, no matter where you are in the world.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Persistent ‘About’ Section&lt;/strong&gt; : The ‘About this map’ panel remembers its state. If you close it, it stays closed on subsequent visits and refreshes until you decide to open it again.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Relative City Sizing&lt;/strong&gt; : The size of each city circle is relative to its total peering bandwidth, giving an immediate visual sense of where the major hubs of connectivity are concentrated.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Interactive Highlighting&lt;/strong&gt; : Hovering over or clicking on any cable or city highlights it and brings it to the forefront. This small detail makes it much easier to focus on and explore individual parts of the network.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Closing Thoughts
&lt;/h2&gt;

&lt;p&gt;This project continues to be a fascinating exploration of the physical reality of our digital world. By making the map interactive, I hope to provide a more powerful tool for anyone curious about the immense and intricate infrastructure that underpins our daily lives. As global data demand soars, the growth of these subsea cables and peering exchanges will only become more critical. Explore the map, watch the internet grow, and see for yourself how the world gets connected.&lt;/p&gt;

&lt;p&gt;This project was a significant undertaking, and I’ve been thrilled to see it shared in various places online. If you choose to share it, I only ask that you please provide attribution by linking back to the project, just as I give credit to my own data sources, &lt;a href="https://telegeography.com/" rel="noopener noreferrer"&gt;TeleGeography&lt;/a&gt; and &lt;a href="https://www.peeringdb.com/" rel="noopener noreferrer"&gt;PeeringDB&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Thank you for reading, and please feel free to explore and share the map with others! &lt;a href="https://map.kmcd.dev" rel="noopener noreferrer"&gt;map.kmcd.dev&lt;/a&gt;&lt;/p&gt;

</description>
      <category>map</category>
      <category>internet</category>
      <category>infrastructure</category>
    </item>
    <item>
      <title>HTTP QUERY and Go</title>
      <dc:creator>Kevin McDonald</dc:creator>
      <pubDate>Wed, 04 Jun 2025 10:00:00 +0000</pubDate>
      <link>https://dev.to/sudorandom/http-query-and-go-4jcm</link>
      <guid>https://dev.to/sudorandom/http-query-and-go-4jcm</guid>
      <description>&lt;p&gt;You’re likely familiar with the HTTP methods, &lt;strong&gt;GET&lt;/strong&gt; and &lt;strong&gt;POST&lt;/strong&gt; , the workhorses of HTTP. These have both worked surprisingly well and have provided well-defined caching behavior for over a quarter of a century. However, neither of these solves the problem of complex request parameters without completely throwing out the caching semantics of &lt;code&gt;GET&lt;/code&gt;. This is where a concept like a &lt;strong&gt;QUERY&lt;/strong&gt; method comes in, and it’s not just a thought experiment; it’s an area actively being &lt;a href="https://httpwg.org/http-extensions/draft-ietf-httpbis-safe-method-w-body.html" rel="noopener noreferrer"&gt;explored by the IETF HTTP Working Group&lt;/a&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  But, why?
&lt;/h3&gt;

&lt;p&gt;The standard HTTP methods serve us well, except the ones that don’t… but that’s a topic for another day. &lt;strong&gt;GET&lt;/strong&gt; is great for simple data retrieval, but its reliance on URL parameters makes it cumbersome for complex queries or large sets of input parameters. URLs have practical length limits, and embedding deeply nested structures is awkward.&lt;/p&gt;

&lt;p&gt;This often leads developers to use &lt;strong&gt;POST&lt;/strong&gt; for what are semantically read-only query operations. Many widely-used protocols effectively do this:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;GraphQL&lt;/strong&gt; typically uses POST requests with JSON bodies to send queries.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;gRPC&lt;/strong&gt; and &lt;strong&gt;gRPC-Web&lt;/strong&gt; typically rely exclusively on POST.&lt;/li&gt;
&lt;li&gt;Older protocols like &lt;strong&gt;SOAP&lt;/strong&gt; and &lt;strong&gt;XML-RPC&lt;/strong&gt; almost exclusively use POST to encapsulate their operations, including data retrieval. This has been an issue for a long time!&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;While using POST works, it’s a compromise. POST traditionally implies an action that might change state on the server, which means intermediaries (read: caches) and even client-side logic might treat these “query-via-POST” requests with undue caution, forgoing caching or automatic retries that would be safe for a truly read-only operation.&lt;/p&gt;

&lt;p&gt;This is precisely the problem that a dedicated safe method with a request body aims to solve. The IETF HTTP Working Group is discussing such a method in a draft titled &lt;a href="https://httpwg.org/http-extensions/draft-ietf-httpbis-safe-method-w-body.html" rel="noopener noreferrer"&gt;“A Safe HTTP Method with a Request Content”&lt;/a&gt;. As the draft states:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;The QUERY method provides a solution that spans the gap between the use of GET and POST. As with POST, the input to the query operation is passed along within the content of the request rather than as part of the request URI. Unlike POST, however, the method is explicitly safe and idempotent, allowing functions like caching and automatic retries to operate.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;While the draft uses “QUERY” as a candidate name (among others), the core idea is what we’re exploring: a method for safe, idempotent data retrieval that can carry a payload. For the rest of this article, we’ll continue to use “QUERY” to represent this concept and show how you can implement such a custom method in Go today.&lt;/p&gt;

&lt;p&gt;It’s crucial to remember that until such a method is formally standardized and widely adopted, &lt;strong&gt;using custom HTTP methods can impact interoperability&lt;/strong&gt;. However, for internal APIs, tightly controlled systems, or as a forward-looking experiment, they can be very useful.&lt;/p&gt;

&lt;h3&gt;
  
  
  Server-Side with Go
&lt;/h3&gt;

&lt;p&gt;Go 1.22 introduced enhancements to &lt;code&gt;http.ServeMux&lt;/code&gt; that allow you to register handlers for specific HTTP methods and paths more directly. Let’s build a server that handles our custom &lt;strong&gt;QUERY&lt;/strong&gt; method:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="k"&gt;package&lt;/span&gt; &lt;span class="n"&gt;main&lt;/span&gt;

&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="s"&gt;"fmt"&lt;/span&gt;
    &lt;span class="s"&gt;"io"&lt;/span&gt;
    &lt;span class="s"&gt;"log"&lt;/span&gt;
    &lt;span class="s"&gt;"net/http"&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="n"&gt;queryHandler&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;w&lt;/span&gt; &lt;span class="n"&gt;http&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ResponseWriter&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;r&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;http&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Request&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c"&gt;// The new ServeMux handles method checking based on registration.&lt;/span&gt;

    &lt;span class="n"&gt;body&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;io&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ReadAll&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;r&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Body&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;http&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;w&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"Error reading request body"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;http&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;StatusInternalServerError&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="k"&gt;defer&lt;/span&gt; &lt;span class="n"&gt;r&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Body&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Close&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

    &lt;span class="c"&gt;// In a real application, you'd parse the query from the body&lt;/span&gt;
    &lt;span class="c"&gt;// (e.g., JSON) and fetch data accordingly.&lt;/span&gt;
    &lt;span class="n"&gt;fmt&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Fprintf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;w&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"Received your QUERY request with body: %s&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;body&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
    &lt;span class="n"&gt;log&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Printf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Handled QUERY request with body: %s"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;body&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="n"&gt;main&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;mux&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;http&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;NewServeMux&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="c"&gt;// Register handler specifically for QUERY method on /data path&lt;/span&gt;
    &lt;span class="n"&gt;mux&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;HandleFunc&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"QUERY /data"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;queryHandler&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="n"&gt;log&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Println&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Server starting on port 8080, handling custom QUERY method..."&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;http&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ListenAndServe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;":8080"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;mux&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;log&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Fatal&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;err&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In this server:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;We use &lt;code&gt;mux.HandleFunc("QUERY /data", queryHandler)&lt;/code&gt; to directly associate the &lt;code&gt;queryHandler&lt;/code&gt; with our custom &lt;strong&gt;QUERY&lt;/strong&gt; HTTP method for the &lt;code&gt;/data&lt;/code&gt; path.&lt;/li&gt;
&lt;li&gt;The &lt;code&gt;queryHandler&lt;/code&gt; reads the request body, where the complex query parameters would reside.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Congratulations, we’ve made an API with an endpoint no browser can use. So useful. Since we can’t test with a browser, let’s write a client to pair with this server.&lt;/p&gt;

&lt;h3&gt;
  
  
  Client-Side, also with Go
&lt;/h3&gt;

&lt;p&gt;Here’s how a Go client can send a &lt;strong&gt;QUERY&lt;/strong&gt; request:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="k"&gt;package&lt;/span&gt; &lt;span class="n"&gt;main&lt;/span&gt;

&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="s"&gt;"fmt"&lt;/span&gt;
    &lt;span class="s"&gt;"io"&lt;/span&gt;
    &lt;span class="s"&gt;"log"&lt;/span&gt;
    &lt;span class="s"&gt;"net/http"&lt;/span&gt;
    &lt;span class="s"&gt;"strings"&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="n"&gt;main&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;queryPayload&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="s"&gt;`{"filters": {"status": "active", "category": "electronics"}, "fields": ["name", "price"]}`&lt;/span&gt;
    &lt;span class="n"&gt;client&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;http&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Client&lt;/span&gt;&lt;span class="p"&gt;{}&lt;/span&gt;

    &lt;span class="c"&gt;// Create a new request with the custom "QUERY" method&lt;/span&gt;
    &lt;span class="n"&gt;req&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;http&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;NewRequest&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"QUERY"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"http://localhost:8080/data"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;strings&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;NewReader&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;queryPayload&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;log&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Fatalf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Error creating request: %v"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="n"&gt;req&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Header&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Set&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Content-Type"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"application/json"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="c"&gt;// Important for body processing&lt;/span&gt;

    &lt;span class="n"&gt;resp&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Do&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;req&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;log&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Fatalf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Error sending request: %v"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="k"&gt;defer&lt;/span&gt; &lt;span class="n"&gt;resp&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Body&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Close&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

    &lt;span class="n"&gt;fmt&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Println&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Response Status:"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;resp&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Status&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;body&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;io&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ReadAll&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;resp&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Body&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;log&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Fatalf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Error reading response body: %v"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="n"&gt;fmt&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Println&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Response Body:"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;body&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The client uses &lt;code&gt;http.NewRequest("QUERY", ...)&lt;/code&gt; to specify the custom method and sends the &lt;code&gt;queryPayload&lt;/code&gt; in the request body.&lt;/p&gt;

&lt;h3&gt;
  
  
  Client-Side with cURL
&lt;/h3&gt;

&lt;p&gt;Although web browsers won’t randomly make QUERY calls, many other tools can use QUERY. Many also support arbitrary HTTP verbs, although this isn’t typically leveraged due to filters from load balancers, firewalls, etc.&lt;/p&gt;

&lt;p&gt;Anyway, here’s what that request looks like with cURL:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;curl &lt;span class="nt"&gt;-X&lt;/span&gt; QUERY &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="s1"&gt;'{"filters": {"status": "active", "category": "electronics"}, "
fields": ["name", "price"]}'&lt;/span&gt; http://localhost:8080/data &lt;span class="nt"&gt;-v&lt;/span&gt;
&lt;span class="k"&gt;*&lt;/span&gt; Host localhost:8080 was resolved.
&lt;span class="k"&gt;*&lt;/span&gt; IPv6: ::1
&lt;span class="k"&gt;*&lt;/span&gt; IPv4: 127.0.0.1
&lt;span class="k"&gt;*&lt;/span&gt; Trying &lt;span class="o"&gt;[&lt;/span&gt;::1]:8080...
&lt;span class="k"&gt;*&lt;/span&gt; Connected to localhost &lt;span class="o"&gt;(&lt;/span&gt;::1&lt;span class="o"&gt;)&lt;/span&gt; port 8080
&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; QUERY /data HTTP/1.1
&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; Host: localhost:8080
&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; User-Agent: curl/8.7.1
&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; Accept: &lt;span class="k"&gt;*&lt;/span&gt;/&lt;span class="k"&gt;*&lt;/span&gt;
&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; Content-Length: 89
&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; Content-Type: application/x-www-form-urlencoded
&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; 
&lt;span class="k"&gt;*&lt;/span&gt; upload completely sent off: 89 bytes
&amp;lt; HTTP/1.1 200 OK
&amp;lt; Date: Sat, 31 May 2025 15:47:45 GMT
&amp;lt; Content-Length: 129
&amp;lt; Content-Type: text/plain&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nv"&gt;charset&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;utf-8
&amp;lt; 
Received your QUERY request with body: &lt;span class="o"&gt;{&lt;/span&gt;&lt;span class="s2"&gt;"filters"&lt;/span&gt;: &lt;span class="o"&gt;{&lt;/span&gt;&lt;span class="s2"&gt;"status"&lt;/span&gt;: &lt;span class="s2"&gt;"active"&lt;/span&gt;, &lt;span class="s2"&gt;"category"&lt;/span&gt;: &lt;span class="s2"&gt;"electronics"&lt;/span&gt;&lt;span class="o"&gt;}&lt;/span&gt;, &lt;span class="s2"&gt;"fields"&lt;/span&gt;: &lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"name"&lt;/span&gt;, &lt;span class="s2"&gt;"price"&lt;/span&gt;&lt;span class="o"&gt;]}&lt;/span&gt;
&lt;span class="k"&gt;*&lt;/span&gt; Connection &lt;span class="c"&gt;#0 to host localhost left intact&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  QUERY vs. GET and POST - A Clearer Separation
&lt;/h3&gt;

&lt;p&gt;Let’s summarize the distinctions with our (potentially future-standard) QUERY method in mind:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;GET&lt;/strong&gt; : Used for retrieving data. Parameters are typically sent in the URL. GET requests &lt;em&gt;must&lt;/em&gt; be safe and idempotent. They generally don’t have a body.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;POST&lt;/strong&gt; : Used for submitting data to be processed, often resulting in a change of state or side effects on the server (e.g., creating a new resource). Parameters are sent in the request body. Not necessarily safe or idempotent.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;QUERY (custom/proposed)&lt;/strong&gt;: Used to &lt;em&gt;request data&lt;/em&gt; (like GET) but with the ability to send a &lt;em&gt;complex query in the request body&lt;/em&gt; (like POST). Crucially, it is defined as &lt;strong&gt;safe and idempotent&lt;/strong&gt; (like GET). This explicitly tells intermediaries and clients that the request has no side effects and can be cached or retried automatically.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Caching Behavior: GET, POST, and QUERY
&lt;/h3&gt;

&lt;p&gt;HTTP caching is vital for performance. A method’s characteristics (especially safety and idempotency) directly influence its cacheability.&lt;/p&gt;

&lt;h4&gt;
  
  
  1. GET
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Behavior&lt;/strong&gt; : GET requests are inherently cacheable. Caches readily store and serve responses to GET requests if caching headers (like &lt;code&gt;Cache-Control&lt;/code&gt;, &lt;code&gt;Expires&lt;/code&gt;, &lt;code&gt;ETag&lt;/code&gt;) allow. This is a foundational aspect of HTTP performance.&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;
  
  
  2. POST
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Behavior&lt;/strong&gt; : POST requests are generally &lt;em&gt;not&lt;/em&gt; cacheable by default. Since POST can have side effects, caching responses could lead to unintended consequences or stale data if the action isn’t repeated.&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;
  
  
  3. QUERY
&lt;/h4&gt;

&lt;p&gt;The IETF draft emphasizes that a method like QUERY is “explicitly safe and idempotent, allowing functions like caching and automatic retries to operate.” This is key.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Behavior (Current Custom Method)&lt;/strong&gt;: Because our &lt;strong&gt;QUERY&lt;/strong&gt; method is non-standard &lt;em&gt;today&lt;/em&gt;, caches will &lt;strong&gt;not cache it by default&lt;/strong&gt;. They typically only automatically consider standard safe methods like GET. To make responses to a custom QUERY request cacheable:

&lt;ul&gt;
&lt;li&gt;The &lt;strong&gt;server must send explicit caching headers&lt;/strong&gt; (e.g., &lt;code&gt;Cache-Control: public, max-age=3600&lt;/code&gt;). These headers signal the cacheability of the response.&lt;/li&gt;
&lt;li&gt;Intermediary caches (CDNs, reverse proxies) might need &lt;strong&gt;specific configuration&lt;/strong&gt; to recognize and cache responses for this custom method.&lt;/li&gt;
&lt;li&gt;The &lt;code&gt;Vary&lt;/code&gt; HTTP header is important if the response depends on the request body content.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;

&lt;strong&gt;Behavior (Future Standardized Method)&lt;/strong&gt;: If a method like QUERY becomes a recognized HTTP standard, caches would likely treat it similarly to GET for caching purposes, provided it’s implemented according to its safe and idempotent semantics. This would be a major advantage, allowing complex queries with bodies to be cached as effectively as GET requests.&lt;/li&gt;

&lt;/ul&gt;

&lt;h3&gt;
  
  
  Key Takeaway for QUERY Caching
&lt;/h3&gt;

&lt;p&gt;The &lt;em&gt;intent&lt;/em&gt; of a QUERY method is to be cacheable. If using a custom method today, you must provide explicit caching directives. The ongoing standardization effort aims to make this caching behavior more automatic and universally understood, unlocking performance benefits for complex, body-inclusive queries that are currently often forced into less cache-friendly POST requests. How long will this process take? Who knows! These kinds of changes can take decades.&lt;/p&gt;

&lt;h2&gt;
  
  
  What’s Next?
&lt;/h2&gt;

&lt;p&gt;The discussion around a safe HTTP method with a request body is an exciting development. By understanding its purpose and experimenting with custom methods like QUERY in Go, we can better appreciate the nuances of HTTP and prepare for potential future standards that will make our APIs more robust and performant.&lt;/p&gt;

&lt;p&gt;Some projects have already taken to adding support for the QUERY method. Take a look at &lt;a href="https://github.com/nodejs/node/issues/51562" rel="noopener noreferrer"&gt;NodeJS: Support for ‘QUERY’ method&lt;/a&gt;, which added support last year. As demonstrated earlier in this article, using &lt;code&gt;QUERY&lt;/code&gt; is already possible in Go. This general support is often true for other languages and libraries as well, since custom HTTP methods are a feature of the HTTP specification.&lt;/p&gt;

&lt;p&gt;The other aspect of moving this forward is a bit more nebulous: bureaucracy. There is still work and review to be done to graduate the draft into an official RFC from the IETF. But fear not. There’s actually steady changes being made to the draft document. You can tell this from the &lt;a href="https://github.com/httpwg/http-extensions/commits/main/draft-ietf-httpbis-safe-method-w-body.xml" rel="noopener noreferrer"&gt;git history&lt;/a&gt; on the httpwg’s repo. At the time of writing May 19th was when the last change was made, so I’m certain that this hasn’t been forgotten about.&lt;/p&gt;

</description>
      <category>http</category>
      <category>webdev</category>
      <category>go</category>
    </item>
    <item>
      <title>FauxRPC</title>
      <dc:creator>Kevin McDonald</dc:creator>
      <pubDate>Tue, 20 Aug 2024 10:00:00 +0000</pubDate>
      <link>https://dev.to/sudorandom/fauxrpc-1egf</link>
      <guid>https://dev.to/sudorandom/fauxrpc-1egf</guid>
      <description>&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fxnjkvjzsjf8j4qrodibg.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fxnjkvjzsjf8j4qrodibg.jpg" alt="FauxRPC" width="800" height="452"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I would like to introduce &lt;strong&gt;&lt;a href="http://fauxrpc.com/" rel="noopener noreferrer"&gt;FauxRPC&lt;/a&gt;&lt;/strong&gt;, a powerful tool that empowers you to accelerate development and testing by effortlessly generating fake implementations of gRPC, gRPC-Web, Connect, and REST services. If you have a &lt;a href="https://kmcd.dev/posts/api-contracts/" rel="noopener noreferrer"&gt;protobuf-based workflow&lt;/a&gt;, this tool could help.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why FauxRPC?
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Faster Development &amp;amp; Testing:&lt;/strong&gt; Work independently without relying on fully functional backend services.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Isolation &amp;amp; Control:&lt;/strong&gt; Test frontend components in isolation with controlled fake data.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Multi-Protocol Support:&lt;/strong&gt; Supports multiple protocols (gRPC, gRPC-Web, Connect, and REST).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Prototyping &amp;amp; Demos:&lt;/strong&gt; Create prototypes and demos quickly without building the full backend. Fake it till you make it.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Improved Collaboration:&lt;/strong&gt; Bridge the gap between frontend and backend teams.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Plays well with others:&lt;/strong&gt; Test data from FauxRPC will try to automatically follow any &lt;a href="https://github.com/bufbuild/protovalidate" rel="noopener noreferrer"&gt;protovalidate&lt;/a&gt; constraints that are defined.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  How it Works
&lt;/h2&gt;

&lt;p&gt;FauxRPC leverages your Protobuf definitions to generate fake services that mimic the behavior of real ones. You can easily configure the fake data returned, allowing you to simulate various scenarios and edge cases. It takes in &lt;code&gt;*.proto&lt;/code&gt; files or protobuf descriptors (in binpb, json, txtpb, yaml formats), then it automatically starts up a server that can speak gRPC/gRPC-Web/Connect and REST (as long as there are &lt;code&gt;google.api.http&lt;/code&gt; annotations defined). Descriptors contain all of the information found in a set of &lt;code&gt;.proto&lt;/code&gt; files. You can generate them with &lt;code&gt;protoc&lt;/code&gt; or the &lt;code&gt;buf build&lt;/code&gt; command.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fkmcd.dev%2Fposts%2Ffauxrpc%2Fdiagram.svg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fkmcd.dev%2Fposts%2Ffauxrpc%2Fdiagram.svg" alt="Diagram" width="1035" height="847"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Get Started
&lt;/h2&gt;

&lt;p&gt;FauxRPC is available as an open-source project. Check out &lt;a href="https://github.com/sudorandom/fauxrpc" rel="noopener noreferrer"&gt;the documentation&lt;/a&gt; and examples to get started. Here’s a quick overview, but be sure to check the official documentation for the most up-to-date instructions:&lt;/p&gt;

&lt;h3&gt;
  
  
  Install via source
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;go &lt;span class="nb"&gt;install &lt;/span&gt;github.com/sudorandom/fauxrpc/cmd/fauxrpc@latest
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Pre-built binaries
&lt;/h3&gt;

&lt;p&gt;Binaries are built for several platforms for each release. See the latest ones on &lt;a href="https://github.com/sudorandom/fauxrpc/releases/latest" rel="noopener noreferrer"&gt;the releases page&lt;/a&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Use Descriptors
&lt;/h3&gt;

&lt;p&gt;Make an &lt;code&gt;example.proto&lt;/code&gt; file (or use a file that already exists):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight protobuf"&gt;&lt;code&gt;&lt;span class="na"&gt;syntax&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"proto3"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kn"&gt;package&lt;/span&gt; &lt;span class="nn"&gt;greet&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;v1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;message&lt;/span&gt; &lt;span class="nc"&gt;GreetRequest&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="kd"&gt;message&lt;/span&gt; &lt;span class="nc"&gt;GreetResponse&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="na"&gt;greeting&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="kd"&gt;service&lt;/span&gt; &lt;span class="n"&gt;GreetService&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;rpc&lt;/span&gt; &lt;span class="n"&gt;Greet&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;GreetRequest&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;returns&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;GreetResponse&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Create a descriptors file and use it to start the FauxRPC server:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;buf build ./example.proto &lt;span class="nt"&gt;-o&lt;/span&gt; ./example.binpb
&lt;span class="nv"&gt;$ &lt;/span&gt;fauxrpc run &lt;span class="nt"&gt;--schema&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;./example.binpb
2024/08/17 08:01:19 INFO Listening on http://127.0.0.1:6660
2024/08/17 08:01:19 INFO See available methods: buf curl &lt;span class="nt"&gt;--http2-prior-knowledge&lt;/span&gt; http://127.0.0.1:6660 &lt;span class="nt"&gt;--list-methods&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Done! It’s that easy. Now you can call the service with any tooling that supports gRPC, gRPC-Web, or connect. So &lt;a href="https://buf.build/docs/reference/cli/buf/curl" rel="noopener noreferrer"&gt;buf curl&lt;/a&gt;, &lt;a href="https://github.com/fullstorydev/grpcurl" rel="noopener noreferrer"&gt;grpcurl&lt;/a&gt;, &lt;a href="https://www.postman.com/" rel="noopener noreferrer"&gt;Postman&lt;/a&gt;, &lt;a href="https://insomnia.rest/" rel="noopener noreferrer"&gt;Insomnia&lt;/a&gt; all work fine!&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;buf curl &lt;span class="nt"&gt;--http2-prior-knowledge&lt;/span&gt; http://127.0.0.1:6660/greet.v1.GreetService/Greet
&lt;span class="o"&gt;{&lt;/span&gt;
  &lt;span class="s2"&gt;"greeting"&lt;/span&gt;: &lt;span class="s2"&gt;"dream"&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Server Reflection
&lt;/h3&gt;

&lt;p&gt;If there’s an existing gRPC service running that you want to emulate, you can use server reflection to start the FauxRPC service:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;fauxrpc run &lt;span class="nt"&gt;--schema&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;https://demo.connectrpc.com
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  From BSR (Buf Schema Registry)
&lt;/h3&gt;

&lt;p&gt;Buf has a &lt;a href="https://buf.build/product/bsr" rel="noopener noreferrer"&gt;schema registry&lt;/a&gt; where many schemas are hosted. Here’s how to use FauxRPC using images from the registry.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;buf build buf.build/bufbuild/registry &lt;span class="nt"&gt;-o&lt;/span&gt; bufbuild.registry.json
&lt;span class="nv"&gt;$ &lt;/span&gt;fauxrpc run &lt;span class="nt"&gt;--schema&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;./bufbuild.registry.json
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Multiple Sources
&lt;/h3&gt;

&lt;p&gt;You can define this &lt;code&gt;--schema&lt;/code&gt; option as many times as you want. That means you can add services from multiple descriptors and even mix and match from descriptors and from server reflection:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;fauxrpc run &lt;span class="nt"&gt;--schema&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;https://demo.connectrpc.com &lt;span class="nt"&gt;--schema&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;./example.binpb
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Multi-protocol Support
&lt;/h2&gt;

&lt;p&gt;The multi-protocol support &lt;a href="https://connectrpc.com/docs/multi-protocol/" rel="noopener noreferrer"&gt;is based on ConnectRPC&lt;/a&gt;. So with FauxRPC, you get &lt;strong&gt;gRPC, gRPC-Web and Connect&lt;/strong&gt; out of the box. However, FauxRPC does one thing more. It allows you to use &lt;a href="https://grpc-ecosystem.github.io/grpc-gateway/docs/tutorials/adding_annotations/" rel="noopener noreferrer"&gt;&lt;code&gt;google.api.http&lt;/code&gt; annotations&lt;/a&gt; to present a JSON/HTTP API, so you can gRPC and REST together! This is normally done with &lt;a href="https://github.com/grpc-ecosystem/grpc-gateway" rel="noopener noreferrer"&gt;an additional service&lt;/a&gt; that runs in-between the outside world and your actual gRPC service but with FauxRPC you get the so-called transcoding from HTTP/JSON to gRPC all in the same package. Here’s a concrete example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight protobuf"&gt;&lt;code&gt;&lt;span class="na"&gt;syntax&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"proto3"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kn"&gt;package&lt;/span&gt; &lt;span class="nn"&gt;http&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;service&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="s"&gt;"google/api/annotations.proto"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;service&lt;/span&gt; &lt;span class="n"&gt;HTTPService&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;rpc&lt;/span&gt; &lt;span class="n"&gt;GetMessage&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;GetMessageRequest&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;returns&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Message&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;option&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;google.api.http&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;get&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="s"&gt;"/v1/{name=messages/*}"&lt;/span&gt;&lt;span class="p"&gt;};&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="kd"&gt;message&lt;/span&gt; &lt;span class="nc"&gt;GetMessageRequest&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;// Mapped to URL path.&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="kd"&gt;message&lt;/span&gt; &lt;span class="nc"&gt;Message&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="na"&gt;text&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;// The resource content.&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Again, we start the service by building the descriptors and using&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;buf build ./httpservice.proto &lt;span class="nt"&gt;-o&lt;/span&gt; ./httpservice.binpb
&lt;span class="nv"&gt;$ &lt;/span&gt;fauxrpc run &lt;span class="nt"&gt;--schema&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;httpservice.binpb
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now that we have the server running we can test this with the “normal” curl:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;curl http://127.0.0.1:6660/v1/messages/123456
&lt;span class="o"&gt;{&lt;/span&gt;&lt;span class="s2"&gt;"text"&lt;/span&gt;:&lt;span class="s2"&gt;"Retro."&lt;/span&gt;&lt;span class="o"&gt;}&lt;/span&gt;⏎
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Sweet. You can now easily support REST alongside gRPC. If you are wondering how to do this with “real” services, look into &lt;a href="https://github.com/connectrpc/vanguard-go" rel="noopener noreferrer"&gt;vanguard-go&lt;/a&gt;. This library is doing the real heavy lifting.&lt;/p&gt;

&lt;h2&gt;
  
  
  What does the fake data look like?
&lt;/h2&gt;

&lt;p&gt;You might be wondering what actual responses look like. FauxRPC’s fake data generation is continually improving so these details might change as time goes on. It uses a library called &lt;a href="https://github.com/brianvoe/gofakeit" rel="noopener noreferrer"&gt;fakeit&lt;/a&gt; to generate fake data. Because protobufs have pretty well-defined types, we can easily generate data that technically matches the types. This works well for most use cases, but FauxRPC tries to be a little bit better. If you annotate your protobuf files with &lt;a href="https://github.com/bufbuild/protovalidate" rel="noopener noreferrer"&gt;protovalidate&lt;/a&gt; constraints, FauxRPC will try its best to generate data that matches these constraints. Let’s look at some examples!&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight protobuf"&gt;&lt;code&gt;&lt;span class="na"&gt;syntax&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"proto3"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kn"&gt;package&lt;/span&gt; &lt;span class="nn"&gt;greet&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;v1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;message&lt;/span&gt; &lt;span class="nc"&gt;GreetRequest&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="kd"&gt;message&lt;/span&gt; &lt;span class="nc"&gt;GreetResponse&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="na"&gt;greeting&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="kd"&gt;service&lt;/span&gt; &lt;span class="n"&gt;GreetService&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;rpc&lt;/span&gt; &lt;span class="n"&gt;Greet&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;GreetRequest&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;returns&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;GreetResponse&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;With FauxRPC, you will get any kind of word, so it might look like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"greeting"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"sufficient"&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is fine, but for the RPC, we know a bit more about the type being returned. We know that it sends a greeting back that looks like “Hello, [name]”. So here’s what the same protobuf file might look like with protovalidate constraints:&lt;/p&gt;

&lt;p&gt;Now let’s see what this looks like with protovalidate constraints:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight protobuf"&gt;&lt;code&gt;&lt;span class="na"&gt;syntax&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"proto3"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="s"&gt;"buf/validate/validate.proto"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kn"&gt;package&lt;/span&gt; &lt;span class="nn"&gt;greet&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;v1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;message&lt;/span&gt; &lt;span class="nc"&gt;GreetRequest&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="p"&gt;[(&lt;/span&gt;&lt;span class="n"&gt;buf.validate.field&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;min_len&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;max_len&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;100&lt;/span&gt;&lt;span class="p"&gt;}];&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="kd"&gt;message&lt;/span&gt; &lt;span class="nc"&gt;GreetResponse&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="na"&gt;greeting&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="p"&gt;[(&lt;/span&gt;&lt;span class="n"&gt;buf.validate.field&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;pattern&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"^Hello, [a-zA-Z]+$"&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="kd"&gt;service&lt;/span&gt; &lt;span class="n"&gt;GreetService&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;rpc&lt;/span&gt; &lt;span class="n"&gt;Greet&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;GreetRequest&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;returns&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;GreetResponse&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;With this new protobuf file, this is what FauxRPC might output now:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"greeting"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Hello, TWXxF"&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This shows how protovalidate constraints enable FauxRPC to generate more realistic and contextually relevant fake data, aligning it closer to the expected behavior of your actual services. As another example, I will show one of Buf’s services used to manage users, &lt;a href="https://buf.build/bufbuild/registry/docs/main:buf.registry.owner.v1#buf.registry.owner.v1.UserService" rel="noopener noreferrer"&gt;buf.registry.owner.v1.UserService&lt;/a&gt;. Here’s what the &lt;code&gt;UserRef&lt;/code&gt; message looks like:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight protobuf"&gt;&lt;code&gt;&lt;span class="kd"&gt;message&lt;/span&gt; &lt;span class="nc"&gt;UserRef&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;option&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;buf.registry.priv.extension.v1beta1.message&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;request_only&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="k"&gt;oneof&lt;/span&gt; &lt;span class="n"&gt;value&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;option&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;buf.validate.oneof&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="k"&gt;required&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="c1"&gt;// The id of the User.&lt;/span&gt;
    &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="na"&gt;id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="p"&gt;[(&lt;/span&gt;&lt;span class="n"&gt;buf.validate.field&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;tuuid&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
    &lt;span class="c1"&gt;// The name of the User.&lt;/span&gt;
    &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt; &lt;span class="p"&gt;[(&lt;/span&gt;&lt;span class="n"&gt;buf.validate.field&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="n"&gt;min_len&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;
      &lt;span class="n"&gt;max_len&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;32&lt;/span&gt;
      &lt;span class="n"&gt;pattern&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="s"&gt;"^[a-z][a-z0-9-]*[a-z0-9]$"&lt;/span&gt;
    &lt;span class="p"&gt;}];&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;So let’s make our descriptors for this service, start the FauxRPC server and make our example request:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;buf build buf.build/bufbuild/registry &lt;span class="nt"&gt;-o&lt;/span&gt; bufbuild.registry.binpb
&lt;span class="nv"&gt;$ &lt;/span&gt;fauxrpc run &lt;span class="nt"&gt;--schema&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;./bufbuild.registry.binpb
&lt;span class="nv"&gt;$ &lt;/span&gt;buf curl &lt;span class="nt"&gt;--http2-prior-knowledge&lt;/span&gt; http://127.0.0.1:6660/buf.registry.owner.v1.UserService/ListUsers
&lt;span class="o"&gt;{&lt;/span&gt;
  &lt;span class="s2"&gt;"nextPageToken"&lt;/span&gt;: &lt;span class="s2"&gt;"Food truck."&lt;/span&gt;,
  &lt;span class="s2"&gt;"users"&lt;/span&gt;: &lt;span class="o"&gt;[&lt;/span&gt;
    &lt;span class="o"&gt;{&lt;/span&gt;
      &lt;span class="s2"&gt;"id"&lt;/span&gt;: &lt;span class="s2"&gt;"c4468393f926400d8880a264df9c284a"&lt;/span&gt;,
      &lt;span class="s2"&gt;"createTime"&lt;/span&gt;: &lt;span class="s2"&gt;"2012-03-06T12:15:03.239463070Z"&lt;/span&gt;,
      &lt;span class="s2"&gt;"updateTime"&lt;/span&gt;: &lt;span class="s2"&gt;"1990-10-29T13:12:31.224347086Z"&lt;/span&gt;,
      &lt;span class="s2"&gt;"name"&lt;/span&gt;: &lt;span class="s2"&gt;"jexox"&lt;/span&gt;,
      &lt;span class="s2"&gt;"type"&lt;/span&gt;: &lt;span class="s2"&gt;"USER_TYPE_STANDARD"&lt;/span&gt;,
      &lt;span class="s2"&gt;"description"&lt;/span&gt;: &lt;span class="s2"&gt;"Tattooed taxidermy."&lt;/span&gt;,
      &lt;span class="s2"&gt;"url"&lt;/span&gt;: &lt;span class="s2"&gt;"http://www.productexploit.name/synergies/target"&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;,
    &lt;span class="o"&gt;{&lt;/span&gt;
      &lt;span class="s2"&gt;"id"&lt;/span&gt;: &lt;span class="s2"&gt;"0e4ca24f4ff54761b109daab0da1bea2"&lt;/span&gt;,
      &lt;span class="s2"&gt;"createTime"&lt;/span&gt;: &lt;span class="s2"&gt;"1955-05-16T02:37:30.643378679Z"&lt;/span&gt;,
      &lt;span class="s2"&gt;"updateTime"&lt;/span&gt;: &lt;span class="s2"&gt;"1923-08-28T04:28:43.330711919Z"&lt;/span&gt;,
      &lt;span class="s2"&gt;"name"&lt;/span&gt;: &lt;span class="s2"&gt;"ya0"&lt;/span&gt;,
      &lt;span class="s2"&gt;"type"&lt;/span&gt;: &lt;span class="s2"&gt;"USER_TYPE_STANDARD"&lt;/span&gt;,
      &lt;span class="s2"&gt;"state"&lt;/span&gt;: &lt;span class="s2"&gt;"USER_STATE_INACTIVE"&lt;/span&gt;,
      &lt;span class="s2"&gt;"description"&lt;/span&gt;: &lt;span class="s2"&gt;"Helvetica."&lt;/span&gt;,
      &lt;span class="s2"&gt;"url"&lt;/span&gt;: &lt;span class="s2"&gt;"https://www.centralengage.info/markets/scale/e-commerce/exploit"&lt;/span&gt;,
      &lt;span class="s2"&gt;"verificationStatus"&lt;/span&gt;: &lt;span class="s2"&gt;"USER_VERIFICATION_STATUS_UNVERIFIED"&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;
  &lt;span class="o"&gt;]&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Hopefully, this gives you a good idea of what the output might look like. The better your validation rules, the better the FauxRPC data will be.&lt;/p&gt;

&lt;h2&gt;
  
  
  What’s left?
&lt;/h2&gt;

&lt;p&gt;FauxRPC is already great for some use cases but it’s not “done” as there’s more to do to make it better. I have plans to add the ability to configure stubs for each RPC method. This will allow you to define specific responses or behaviors for each RPC, giving you more control over the simulated service. I hope this will make it easier to iterate on protobuf designs without needing to actually implement services until later.&lt;/p&gt;

&lt;h2&gt;
  
  
  Stay Tuned
&lt;/h2&gt;

&lt;p&gt;I made a &lt;a href="http://fauxrpc.com/" rel="noopener noreferrer"&gt;documentation website&lt;/a&gt; to organize documentation. I think it looks pretty good for how quickly I threw it together. The code for FauxRPC lives on GitHub at &lt;a href="https://kmcd.devgithub.com/sudorandom/fauxrpc" rel="noopener noreferrer"&gt;sudorandom/fauxrpc&lt;/a&gt;. It’s a little thin now but there’s a lot that I can write about in there. I’m actively developing FauxRPC and have many exciting features planned for the future. This is early on for this project but it has come together as a coherent and useful program for me extremely quickly. So please try it out and let me know your feedback and suggestions. Stay tuned for updates!&lt;/p&gt;

&lt;p&gt;&lt;em&gt;... and don't forget to &lt;a href="https://github.com/sudorandom/fauxrpc" rel="noopener noreferrer"&gt;star the repo on GitHub&lt;/a&gt;. It helps more than you know!&lt;/em&gt;&lt;/p&gt;

</description>
      <category>grpc</category>
      <category>api</category>
      <category>testing</category>
      <category>protobuf</category>
    </item>
    <item>
      <title>Tracking the Wins</title>
      <dc:creator>Kevin McDonald</dc:creator>
      <pubDate>Tue, 30 Apr 2024 00:00:00 +0000</pubDate>
      <link>https://dev.to/sudorandom/tracking-the-wins-3paf</link>
      <guid>https://dev.to/sudorandom/tracking-the-wins-3paf</guid>
      <description>&lt;p&gt;Mental health is hugely important for software engineers. When I first started coding, the thrill of solving problems and getting that hit of dopamine from the instant feedback loop was incredible. It's easy to forget those initial challenges and accomplishments as you progress, though, and problems that used to look like unscalable walls turn into speed bumps. That's why I believe regularly reflecting on your work is so valuable. Otherwise, you might find yourself dwelling on the struggles and forgetting the successes you've achieved along the way.&lt;/p&gt;

&lt;h2&gt;
  
  
  Writing down the wins
&lt;/h2&gt;

&lt;p&gt;Here's how I track my progress: Every week or two, I set aside some time to quickly &lt;strong&gt;&lt;code&gt;document my achievements&lt;/code&gt;&lt;/strong&gt;, even small ones. I keep it concise to avoid procrastination. I have some anxiety around tasks with too many unknowns, so a simple, mechanical process works best for me. I'm also a very forgetful person, so I usually have to review artifacts created by the work that I do, like following digital breadcrumbs that I leave around. Here's a list of where I might find these things:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;issues I've worked on&lt;/li&gt;
&lt;li&gt;pull requests&lt;/li&gt;
&lt;li&gt;emails&lt;/li&gt;
&lt;li&gt;edits to documentation&lt;/li&gt;
&lt;li&gt;slack messages&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The more concrete evidence, the better, but some achievements don't leave clear evidence so be sure to capture interesting anecdotes as they happen. Anything that has allowed me to progress forward should be highlighted, even if it is a result of compromise or realizing that you made a mistake.&lt;/p&gt;

&lt;p&gt;I also apply a similar strategy to my personal life. Did I stay on top of my Danish lessons and language learning apps? Did I have periods of being ill during this period? Was I physically active, by cycling, exercising, or simply taking the stairs? Tracking these things helps me see the broader picture.&lt;/p&gt;

&lt;h2&gt;
  
  
  Quarterly reviews
&lt;/h2&gt;

&lt;p&gt;To pair with the weekly tracking. &lt;strong&gt;&lt;code&gt;I conduct a more in-depth reflection every 3 months&lt;/code&gt;&lt;/strong&gt;. Here, I add my thoughts to these accomplishments. I create a narrative of the past quarter, encompassing both highs and lows, as well as the inevitable "neutral" experiences. Three months seems like a good timeframe to identify any significant shifts in my attitude or goals. It allows me to assess my progress on past goals and set new ones for the future. I believe this time frame is &lt;em&gt;MUCH&lt;/em&gt; better than the yearly cycle of New Year resolutions. Twelve months is way too long. One month is usually not enough to make good progress on goals or to establish habits. Three months seems just right to me. So here's the list of what might appear on a quarterly review:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A "narrative" of the last three months using concrete examples from my journal to back it up.&lt;/li&gt;
&lt;li&gt;Report on the progress of previous goals.&lt;/li&gt;
&lt;li&gt;Create new goals or revise the older goals if needed.&lt;/li&gt;
&lt;li&gt;Pictures and video of important moments.&lt;/li&gt;
&lt;li&gt;Reflect on big family budget items and see if they align with my priorities in life.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Yearly themes
&lt;/h2&gt;

&lt;p&gt;Yes, &lt;a href="https://www.relay.fm/cortex" rel="noopener noreferrer"&gt;I am a Cortexan&lt;/a&gt; and podcast episodes outlining &lt;a href="https://www.youtube.com/watch?v=cXexYmOHkas" rel="noopener noreferrer"&gt;yearly themes&lt;/a&gt; are among my favorites. Yearly themes give a framework for me to push myself to do things I want to do while allowing me to adjust (every quarter) exactly what it means to accomplish my theme. Sometimes life throws additional challenges. For example, it's silly to fail at achieving a goal of "running a mile every day" if you break your leg. This year is &lt;strong&gt;"The Year of Building"&lt;/strong&gt;. Here's what I wrote about my theme shortly before the year began:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;I want to build some cool things. I want to make some useful open source projects. I want to implement some neat solutions at work and actually prove them out. My follow-through sometimes lacks when I lose interest in things. I want to make more cool blog posts.&lt;/p&gt;

&lt;p&gt;I also want to build on my Danish by continuing my Danish classes, self study with and using it more "in the wild".&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;I think I'm doing a good job with this one. You may notice though that the latest batch of blog posts didn't start in January. I "slacked" on this goal for over a month but in my theme framework, it's fine because I was ramping up my Danish classes again. My yearly theme allows me a lot of grace on the day-week-month scale while hopefully keeping me honest on the yearly scale and allowing for adjustment of individual goals to match reality.&lt;/p&gt;

&lt;h2&gt;
  
  
  Try it yourself
&lt;/h2&gt;

&lt;p&gt;Journaling and goal-setting are extremely personal things so your process may vary a lot from mine. But one thing is likely true of everyone: by keeping track of your wins, both big and small, you can maintain motivation and see your progress over time. Give it a try and see how it impacts your software engineering (and life) journey!&lt;/p&gt;

</description>
    </item>
    <item>
      <title>Making Greppable Code</title>
      <dc:creator>Kevin McDonald</dc:creator>
      <pubDate>Tue, 16 Apr 2024 00:00:00 +0000</pubDate>
      <link>https://dev.to/sudorandom/making-greppable-code-3899</link>
      <guid>https://dev.to/sudorandom/making-greppable-code-3899</guid>
      <description>&lt;p&gt;Have you ever felt like you're sifting through lines of code, desperately searching for that elusive function or variable? Fear not, for there's a way to make your code more discoverable: &lt;strong&gt;greppable naming conventions&lt;/strong&gt;. &lt;/p&gt;

&lt;h3&gt;
  
  
  What is Greppability?
&lt;/h3&gt;

&lt;p&gt;Drawing inspiration from the powerful &lt;a href="https://www.gnu.org/software/grep/manual/grep.html" rel="noopener noreferrer"&gt;Linux command &lt;code&gt;grep&lt;/code&gt;&lt;/a&gt; used for text searching, greppable code refers to code that's easy to search through using clear and meaningful names. Just like &lt;code&gt;grep&lt;/code&gt; helps you quickly find specific text within a file, greppable names allow you to efficiently locate relevant code sections by searching for terms that reflect their purpose.&lt;/p&gt;

&lt;h3&gt;
  
  
  Crafting Greppable Names
&lt;/h3&gt;

&lt;p&gt;Here are some key principles to follow:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Specificity:&lt;/strong&gt; Ditch generic names like &lt;code&gt;processData()&lt;/code&gt; or &lt;code&gt;utils&lt;/code&gt;. Opt for terms that reflect the specific content (&lt;code&gt;cleanAndValidateOrderData()&lt;/code&gt;, &lt;code&gt;orderId&lt;/code&gt;).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Priority:&lt;/strong&gt; It is far more important for filenames, class names and methods to have specific names than it is for variables inside of functions. Variables inside of functions already have a lot of implied context, like the filename, class (if your language uses classes) and the function that the variable appears in. All of that extra context should be greppable but the variables probably don't need to be.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Consistency:&lt;/strong&gt; Maintain a consistent naming style throughout your codebase (e.g. camelCase or snake_case) and always use the 'standard' for your language or framework.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Verbs when naming methods:&lt;/strong&gt; Methods describe actions. Use verbs at the beginning of method names to convey their purpose (&lt;code&gt;calculateOrderTotal()&lt;/code&gt;, &lt;code&gt;updateCustomerRecord()&lt;/code&gt;).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Abbreviations with Caution:&lt;/strong&gt; Use abbreviations sparingly and only for widely recognized terms (e.g. &lt;code&gt;HTTP&lt;/code&gt;, &lt;code&gt;XML&lt;/code&gt;). Overuse can hinder readability. If your variable names look like 2010s-era tech startups, you're probably doing it wrong.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Dynamic Dispatch:&lt;/strong&gt; Doing some more complex programming patterns like dynamic dispatching can greatly hurt the ability for the code to be greppable. It can also add indirection so be sure you use these methods sparingly and add more documentation where needed to help lost souls who are tracing code through the codebase.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  The Search Advantage of gRPC
&lt;/h3&gt;

&lt;p&gt;Here's where things get interesting for APIs. Compared to RESTful APIs with their predefined resource structures, gRPC (and other RPC-based APIs) offer greater flexibility in naming methods. This freedom allows you to leverage greppable naming conventions to their full potential.&lt;/p&gt;

&lt;p&gt;For instance, in a RESTful API, an endpoint for updating a user profile might be named &lt;code&gt;/users/:id&lt;/code&gt;. While functional, this doesn't explicitly convey the action performed. In a gRPC API, however, you could have a method named &lt;code&gt;UpdateUserProfile(UpdateRequest request)&lt;/code&gt;, which clearly describes the purpose and is much easier to search for in the code.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The Greppability Benefit&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Meaningful names not only make code easier to understand but also significantly reduce maintenance time. When you (or another developer) revisit your codebase later, clear names can make traversing the codebase much nicer. I think the term "greppability" has a clear meaning and can come up in many contexts: in code, data, documentation, etc. Do you think this a good word for a developer to have in their vocabulary?&lt;/p&gt;

</description>
    </item>
    <item>
      <title>Building APIs with Contracts</title>
      <dc:creator>Kevin McDonald</dc:creator>
      <pubDate>Tue, 09 Apr 2024 00:00:00 +0000</pubDate>
      <link>https://dev.to/sudorandom/building-apis-with-contracts-1cel</link>
      <guid>https://dev.to/sudorandom/building-apis-with-contracts-1cel</guid>
      <description>&lt;p&gt;In today's interconnected world, APIs (Application Programming Interfaces) are the glue that connects computers. They allow different applications to talk to each other, share data, and perform actions. However, traditional methods of creating APIs can lead to challenges, especially when dealing with versioning changes and integrating complex systems. This is where &lt;strong&gt;contract-based APIs&lt;/strong&gt; come in, offering a more robust and reliable approach and taming some of the wildness that exists on the web.&lt;/p&gt;

&lt;h3&gt;
  
  
  The Power of Pre-defined API Contracts
&lt;/h3&gt;

&lt;p&gt;Imagine building a house without a blueprint. It would be chaotic and prone to errors. A contract-based API is like a detailed blueprint for communication between applications. It defines exactly what data can be exchanged, in what format, and what actions can be performed. This pre-defined agreement unlocks several advantages for developers and applications:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Improved Developer Experience:&lt;/strong&gt;

&lt;ul&gt;
&lt;li&gt;Developers on both sides (client and server) have a clear understanding of what's expected, making integration smoother.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Automated Documentation:&lt;/strong&gt; Contracts serve as self-documenting artifacts, reducing the need for manual documentation creation and maintenance. This saves development time and ensures documentation stays in sync with the actual API implementation.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;

&lt;strong&gt;Reduced Errors:&lt;/strong&gt;

&lt;ul&gt;
&lt;li&gt;Mismatched data formats or API changes become less likely, leading to fewer bugs and headaches. Contracts act as a validation layer, catching potential issues early in the development process.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;

&lt;strong&gt;Easier Integration:&lt;/strong&gt;

&lt;ul&gt;
&lt;li&gt;Contracts act as a single source of truth, simplifying the process of connecting different services.  Developers can quickly understand how to interact with the API without extensive back-and-forth communication.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;

&lt;strong&gt;Streamlined Development:&lt;/strong&gt;

&lt;ul&gt;
&lt;li&gt;Contract-based APIs often enable tools to automatically generate code for both client and server implementations based on the defined contracts. This eliminates manual coding and boilerplate, allowing developers to focus on core functionalities.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;

&lt;p&gt;By leveraging pre-defined contracts, you can build more robust, reliable, and maintainable APIs that streamline development efforts and improve overall communication within your application ecosystem. &lt;/p&gt;

&lt;h3&gt;
  
  
  Protobuf: The Language of APIs
&lt;/h3&gt;

&lt;p&gt;In my world, the foundation of many contract-based APIs lies in &lt;a href="https://protobuf.dev/" rel="noopener noreferrer"&gt;&lt;strong&gt;Protocol Buffers (protobuf)&lt;/strong&gt;&lt;/a&gt;. It's a language-neutral data format specifically designed for structured messages exchanged between applications. Protobuf offers several advantages:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Smaller Message Sizes:&lt;/strong&gt; Protobuf messages are compact and efficient, leading to faster transmission and reduced bandwidth usage.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Faster Parsing:&lt;/strong&gt; Parsing protobuf messages is significantly faster compared to traditional formats like JSON or XML.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Cross-language Compatibility:&lt;/strong&gt; Protobuf definitions are language-agnostic. Code for interacting with the API can be generated for many programming languages.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Defining service contracts in protobuf involves creating &lt;code&gt;.proto&lt;/code&gt; files. These files specify the structure of messages (data fields and types) and define the service methods (requests and responses) that an API offers.&lt;/p&gt;

&lt;p&gt;Here's a basic example of a &lt;code&gt;.proto&lt;/code&gt; file defining messages for a user and an address:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight protobuf"&gt;&lt;code&gt;&lt;span class="na"&gt;syntax&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"proto3"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;message&lt;/span&gt; &lt;span class="nc"&gt;User&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="kt"&gt;int32&lt;/span&gt; &lt;span class="na"&gt;id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="na"&gt;email&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="n"&gt;Address&lt;/span&gt; &lt;span class="na"&gt;address&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="kd"&gt;message&lt;/span&gt; &lt;span class="nc"&gt;Address&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="na"&gt;street&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="na"&gt;city&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="na"&gt;state&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="na"&gt;zip&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In this example, the &lt;code&gt;User&lt;/code&gt; message has fields for name, ID, email, and an &lt;code&gt;Address&lt;/code&gt; message. The &lt;code&gt;Address&lt;/code&gt; message itself has fields for street, city, state, and zip code. These defined message structures ensure consistent data exchange between applications.&lt;/p&gt;

&lt;h3&gt;
  
  
  gRPC: Building APIs on a Solid Foundation
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;gRPC (gRPC Remote Procedure Call)&lt;/strong&gt; is a high-performance framework that builds upon protobuf's strengths. It provides a powerful way to implement remote procedure calls, allowing applications to interact using clients generated for each language using (usually) types and semantics that make sense to the language. Here's how gRPC leverages protobuf:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Protobuf Messages for Communication:&lt;/strong&gt; gRPC uses protobuf messages to define the request and response data exchanged between client and server.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Generated Code for Seamless Interaction:&lt;/strong&gt; gRPC automatically generates server and client stubs (boilerplate code) from the &lt;code&gt;.proto&lt;/code&gt; files. This eliminates manual coding and ensures consistency between client and server implementations.&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;
  
  
  Introducing Services and Request/Response Types with gRPC
&lt;/h4&gt;

&lt;p&gt;Now let's expand on the concept of services within a &lt;code&gt;.proto&lt;/code&gt; file. We can define a service called &lt;code&gt;UserService&lt;/code&gt; with methods for user management:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight protobuf"&gt;&lt;code&gt;&lt;span class="na"&gt;syntax&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"proto3"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;service&lt;/span&gt; &lt;span class="n"&gt;UserService&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;rpc&lt;/span&gt; &lt;span class="n"&gt;CreateUser&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;CreateUserRequest&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;returns&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;User&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{}&lt;/span&gt;
  &lt;span class="k"&gt;rpc&lt;/span&gt; &lt;span class="n"&gt;GetUser&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;GetUserRequest&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;returns&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;User&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="kd"&gt;message&lt;/span&gt; &lt;span class="nc"&gt;CreateUserRequest&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="n"&gt;User&lt;/span&gt; &lt;span class="na"&gt;user&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="kd"&gt;message&lt;/span&gt; &lt;span class="nc"&gt;GetUserRequest&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kt"&gt;int32&lt;/span&gt; &lt;span class="na"&gt;id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This example defines a &lt;code&gt;UserService&lt;/code&gt; with two methods: &lt;code&gt;CreateUser&lt;/code&gt; and &lt;code&gt;GetUser&lt;/code&gt;. Each method takes a specific request message and returns a response message. The &lt;code&gt;CreateUserRequest&lt;/code&gt; message contains a &lt;code&gt;User&lt;/code&gt; object to be created, while the &lt;code&gt;GetUserRequest&lt;/code&gt; specifies the user ID to retrieve. This clear separation of request and response types further enhances the contract between client and server. You should also notice just how clear the intention is for the methods of UserServer. A reader of this spec doesn't have to do a mapping of extremely vague words like "POST" to more understandable actions like "create". Also notice how the &lt;code&gt;CreateUser&lt;/code&gt; and &lt;code&gt;GetUser&lt;/code&gt; methods are &lt;a href="https://en.wiktionary.org/wiki/greppable" rel="noopener noreferrer"&gt;greppable&lt;/a&gt;, making it trivial to locate uses of these RPCs, even across several repositories.&lt;/p&gt;

&lt;h2&gt;
  
  
  Alternatives
&lt;/h2&gt;

&lt;p&gt;While Protobuf and gRPC are a powerful duo, there are other contract-based API solutions to consider:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://www.openapis.org/" rel="noopener noreferrer"&gt;&lt;strong&gt;OpenAPI (Swagger)&lt;/strong&gt;&lt;/a&gt;: This is a popular specification for defining RESTful APIs, offering a standardized way to document and interact with web services.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://thrift.apache.org/" rel="noopener noreferrer"&gt;&lt;strong&gt;Thrift&lt;/strong&gt;&lt;/a&gt;: Similar to protobuf, Thrift is a language-neutral protocol for defining service contracts. It supports various RPC protocols beyond gRPC.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://avro.apache.org/" rel="noopener noreferrer"&gt;&lt;strong&gt;Avro&lt;/strong&gt;&lt;/a&gt;: This JSON-like data format uses schemas to ensure reliable data exchange. It's often used with Apache Kafka for streaming data pipelines.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The choice between these options depends on your specific needs. gRPC and protobuf excel for high-performance, RPC-based communication, while OpenAPI is better suited for existing RESTful APIs. Thrift is also there. Avro has some dynamic typing features and has self-describing messages but is also slower for the same reason.&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;Contract-based APIs offer a significant advantage in building robust and scalable communication between applications. protobuf and gRPC provide a powerful combination for defining clear contracts and generating efficient code. By leveraging these technologies, you can streamline API development, improve developer experience, and ensure seamless integration within.&lt;/p&gt;

</description>
    </item>
    <item>
      <title>Dropping Unknown Fields in ConnectRPC</title>
      <dc:creator>Kevin McDonald</dc:creator>
      <pubDate>Tue, 02 Apr 2024 00:00:00 +0000</pubDate>
      <link>https://dev.to/sudorandom/dropping-unknown-fields-in-connectrpc-37c0</link>
      <guid>https://dev.to/sudorandom/dropping-unknown-fields-in-connectrpc-37c0</guid>
      <description>&lt;p&gt;gRPC, with its focus on performance and language neutrality, remains a popular choice for building microservices and APIs. But when exposing your gRPC service to the internet, there are a few security considerations to account for. Protobuf, the serialization format often used with gRPC, offers various encoding options that can significantly impact your service's security posture. &lt;/p&gt;

&lt;p&gt;One crucial optimization for internet-facing gRPC services is customizing the behavior towards &lt;strong&gt;unknown fields&lt;/strong&gt;. I've talked about &lt;a href="https://dev.to/posts/protobuf-unknown-fields/"&gt;unknown fields in a previous post&lt;/a&gt;, so read that one if unknown fields are still a mystery to you and then come back here. By default, protobuf messages can contain fields that are not defined in the current version of the proto schema. While convenient for development and can help with forward compatibility, this poses a security risk in a public environment.&lt;/p&gt;

&lt;p&gt;Here's why you should consider dropping unknown fields when exposing gRPC to the internet:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Preventing Malicious Data:&lt;/strong&gt; Unknown fields can be exploited by malicious actors to inject unexpected data into your service. This could lead to potential security vulnerabilities like code injection or unexpected behavior.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Ensuring Compatibility:&lt;/strong&gt; Uncontrolled unknown fields can cause compatibility issues if your clients are using different versions of the proto schema. Dropping them enforces stricter adherence to the defined message format.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Improving Performance:&lt;/strong&gt; Skipping unknown fields during message parsing can lead to performance gains, especially when dealing with large datasets.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  How to Drop Unknown Fields
&lt;/h3&gt;

&lt;p&gt;Here is how you can drop unknown fields while using the standard &lt;code&gt;proto.UnmarshalOptions&lt;/code&gt; struct provided by the &lt;code&gt;google.golang.org/protobuf/proto&lt;/code&gt; package. Here's how to do it in your Go code:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="s"&gt;"google.golang.org/protobuf/proto"&lt;/span&gt;
    &lt;span class="o"&gt;...&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="c"&gt;// Configure unmarshalling options to discard unknown fields&lt;/span&gt;
&lt;span class="n"&gt;opts&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;proto&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;UnmarshalOptions&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;DiscardUnknown&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="no"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c"&gt;// Use the options when unmarshalling incoming messages&lt;/span&gt;
&lt;span class="n"&gt;msg&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;MyMessage&lt;/span&gt;&lt;span class="p"&gt;{}&lt;/span&gt;
&lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;proto&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Unmarshal&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;msg&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;opts&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c"&gt;// Handle error&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;By setting the &lt;code&gt;DiscardUnknown&lt;/code&gt; field to &lt;code&gt;true&lt;/code&gt; in the &lt;code&gt;proto.UnmarshalOptions&lt;/code&gt; struct before unmarshalling incoming messages, you ensure that any unknown fields are ignored. This helps mitigate the security risks associated with unknown fields while processing internet-facing gRPC requests.&lt;/p&gt;

&lt;h2&gt;
  
  
  How to Drop Unknown Fields in Connect RPC Servers
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="k"&gt;package&lt;/span&gt; &lt;span class="n"&gt;main&lt;/span&gt;

&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="s"&gt;"log"&lt;/span&gt;
    &lt;span class="s"&gt;"net/http"&lt;/span&gt;

    &lt;span class="s"&gt;"golang.org/x/net/http2"&lt;/span&gt;
    &lt;span class="s"&gt;"golang.org/x/net/http2/h2c"&lt;/span&gt;
    &lt;span class="s"&gt;"go.akshayshah.org/connectproto"&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="n"&gt;main&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;greeter&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;GreetServer&lt;/span&gt;&lt;span class="p"&gt;{}&lt;/span&gt;
    &lt;span class="n"&gt;mux&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;http&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;NewServeMux&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="n"&gt;path&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;handler&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;greetv1connect&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;NewGreetServiceHandler&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="n"&gt;greeter&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="c"&gt;// Add an option that customizes protobuf marshalling/unmarshalling behavior&lt;/span&gt;
        &lt;span class="n"&gt;connectproto&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;WithBinary&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
            &lt;span class="n"&gt;proto&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;MarshalOptions&lt;/span&gt;&lt;span class="p"&gt;{},&lt;/span&gt;
            &lt;span class="n"&gt;proto&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;UnmarshalOptions&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;DiscardUnknown&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="no"&gt;true&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;
        &lt;span class="p"&gt;),&lt;/span&gt;
        &lt;span class="c"&gt;// Add an option to customize JSON marshalling/unmachalling&lt;/span&gt;
        &lt;span class="n"&gt;connectproto&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;WithJSON&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
            &lt;span class="n"&gt;protojson&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;MarshalOptions&lt;/span&gt;&lt;span class="p"&gt;{},&lt;/span&gt;
            &lt;span class="n"&gt;protojson&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;UnmarshalOptions&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;DiscardUnknown&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="no"&gt;true&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;
        &lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;mux&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Handle&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;path&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;handler&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;log&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Fatal&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;http&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ListenAndServe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="s"&gt;"localhost:9000"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;h2c&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;NewHandler&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;mux&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;http2&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Server&lt;/span&gt;&lt;span class="p"&gt;{}),&lt;/span&gt;
    &lt;span class="p"&gt;))&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In this example, &lt;code&gt;connectproto.WithBinary&lt;/code&gt; ensures only messages with defined fields are processed, enhancing the security of your gRPC service. &lt;code&gt;connectproto.WithJSON&lt;/code&gt; does the same thing but with JSON.&lt;/p&gt;

&lt;h3&gt;
  
  
  Additional Considerations
&lt;/h3&gt;

&lt;p&gt;While dropping unknown fields is a valuable security practice, it's important to consider potential trade-offs:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Backward compatibility:&lt;/strong&gt; Clients using older versions of the proto schema will encounter errors if they rely on previously defined unknown fields. &lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Logging and Debugging:&lt;/strong&gt; Dropping unknown fields might make it harder to identify the source of unexpected behavior during development or debugging.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;In such cases, it's recommended to document these trade-offs and have a clear versioning policy for your gRPC service and client applications.&lt;/p&gt;

&lt;h3&gt;
  
  
  Conclusion
&lt;/h3&gt;

&lt;p&gt;Exposing gRPC services to the internet requires careful security considerations. By customizing protobuf encoding options, specifically by dropping unknown fields using &lt;code&gt;proto.UnmarshalOptions&lt;/code&gt;, you can significantly improve the security posture of your service. Remember to weigh the benefits against potential drawbacks and implement a solution that aligns with your specific needs.&lt;/p&gt;

</description>
    </item>
    <item>
      <title>RESTless: Web APIs After REST</title>
      <dc:creator>Kevin McDonald</dc:creator>
      <pubDate>Tue, 26 Mar 2024 00:00:00 +0000</pubDate>
      <link>https://dev.to/sudorandom/restless-web-apis-after-rest-39go</link>
      <guid>https://dev.to/sudorandom/restless-web-apis-after-rest-39go</guid>
      <description>&lt;p&gt;The "RESTful API" has been the workhorse of the web for many years. It has been an ever-changing religion with tenants that developers try their hardest to adhere to. But as web applications evolve, user demands grow and our industry experience with API design grows, it's time to re-evaluate this approach. This article explores the limitations of REST and delves into modern alternatives that can unlock a world of possibilities beyond.&lt;/p&gt;

&lt;h3&gt;
  
  
  Objects? More like "objnoxious"
&lt;/h3&gt;

&lt;p&gt;Imagine building a social media API endpoint to retrieve a user's feed. Using a single REST object to represent a feed item can get messy. This object would need to encompass:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;User information (username, profile picture)&lt;/li&gt;
&lt;li&gt;Post content (text, images, videos)&lt;/li&gt;
&lt;li&gt;User interactions (likes, comments, shares) with timestamps&lt;/li&gt;
&lt;li&gt;Additional data like post visibility or author verification status&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This "feed" object becomes bloated, especially if the feed contains many posts. Fetching and updating this complex object for every feed interaction can be inefficient. You can split the object up into many child objects but you are likely creating the need for clients to make more requests and greatly increasing the complexity of the API.&lt;/p&gt;

&lt;p&gt;This highlights a limitation of REST: forcing real-world entities (like a social media feed) into rigid object structures with a strict hierarchy can lead to cumbersome data management.&lt;/p&gt;

&lt;h3&gt;
  
  
  Versioning is weird
&lt;/h3&gt;

&lt;p&gt;Let's say you introduce a new field to your product data model in a REST API. Versioning in the path (e.g., &lt;code&gt;/api/v2/products&lt;/code&gt;) forces you to update every single endpoint URL that uses that data. Versioning each path by adding a query parameter (e.g., &lt;code&gt;/api/products?version=2&lt;/code&gt;) or header seems more targeted, but what if only a specific endpoint needs the new version? Do you maintain a list of versions per endpoint? Do you bump the version for the entire API and default to the latest version? This is open to interpretation and every solution seems awkward to me.&lt;/p&gt;

&lt;h3&gt;
  
  
  Limited Options, Clever Solution
&lt;/h3&gt;

&lt;p&gt;REST only offers a handful of methods (GET, POST, etc.) to handle data. There are &lt;strong&gt;9&lt;/strong&gt; methods in total. Let's talk about each one.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;CONNECT&lt;/li&gt;
&lt;li&gt;DELETE&lt;/li&gt;
&lt;li&gt;GET&lt;/li&gt;
&lt;li&gt;HEAD&lt;/li&gt;
&lt;li&gt;OPTIONS&lt;/li&gt;
&lt;li&gt;PATCH&lt;/li&gt;
&lt;li&gt;POST&lt;/li&gt;
&lt;li&gt;PUT&lt;/li&gt;
&lt;li&gt;TRACE&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Most of these are used in niche, hyper-specific ways. I suspect most developers don't know what &lt;code&gt;HEAD&lt;/code&gt;, &lt;code&gt;TRACE&lt;/code&gt;, &lt;code&gt;OPTIONS&lt;/code&gt;, and &lt;code&gt;CONNECT&lt;/code&gt; do. None of these are incredibly useful when developing web APIs. So let's ditch them. Let's also ditch &lt;code&gt;PATCH&lt;/code&gt; because it's just &lt;code&gt;PUT&lt;/code&gt; in a trenchcoat.&lt;/p&gt;

&lt;p&gt;Now we have: &lt;code&gt;GET&lt;/code&gt;, &lt;code&gt;POST&lt;/code&gt;, &lt;code&gt;DELETE&lt;/code&gt;, and &lt;code&gt;PUT&lt;/code&gt;. Sweet, we're left with enough methods to make a CRUD application. Roughly speaking:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;C&lt;/strong&gt;reate = &lt;code&gt;POST&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;R&lt;/strong&gt;ead = &lt;code&gt;GET&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;U&lt;/strong&gt;pdate = &lt;code&gt;PUT&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;D&lt;/strong&gt;elete = &lt;code&gt;DELETE&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Wow! Such simple. So elegant. I'm sure glad that everything web developers do boils down to these 4 simple actions... Oh wait, that's &lt;strong&gt;&lt;em&gt;totally&lt;/em&gt; wrong&lt;/strong&gt;. There are so many more actions you can do to an object. Just think of how crazy it would be if you were using a programming language where you could only have four pre-defined methods in each class. Think of all of the extra container classes and weird abstractions we'd make on top of that. This sounds like utter insanity and is exactly what REST gives us.&lt;/p&gt;

&lt;p&gt;Let's stop pretending that there are only 9 (but actually 4) things you can do to a resource.&lt;/p&gt;

&lt;h3&gt;
  
  
  Inefficiency of JSON
&lt;/h3&gt;

&lt;p&gt;JSON is slow and inefficient. It'd be insane if we built the internet around this format. Wait, we did? Really?  JSON is wasteful in many ways. It's text-based, which has an inherent cost in payload size and processing. JSON also will include key names over and over again and the length of the keys directly translates to longer payloads. This is not ideal if we're trying to save on data transfer. Protobuf, on the other hand, is a compact and efficient binary format specifically designed for data serialization. Even factoring in gzip, JSON loses out to encoded protobuf &lt;a href="https://auth0.com/blog/beating-json-performance-with-protobuf/" rel="noopener noreferrer"&gt;in pretty much every way&lt;/a&gt;: CPU usage, memory usage, message size and speed. This just shows that there are better formats than JSON.&lt;/p&gt;

&lt;h3&gt;
  
  
  OpenAPI?
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://www.openapis.org/" rel="noopener noreferrer"&gt;OpenAPI&lt;/a&gt; is a specification for describing RESTful APIs. It acts as a contract between API providers and consumers, defining the available resources, their properties, and the allowed operations (GET, POST, PUT, DELETE) for each. A common way OpenAPI is used is by generating OpenAPI specifications from the source of the backend service. Depending on the level of library integration these tools can automatically discover the HTTP method, route, request and response types, etc. This can help keep documentation up-to-date compared to manually creating OpenAPI spec, which is pretty incredible.&lt;/p&gt;

&lt;p&gt;Okay, now here's where I get philosophical. If we're going to have a declarative specification for our APIs, I believe the specification should be the source of truth rather than the output. In my mind, OpenAPI specification should be the very first thing you write and agree upon and the servers and clients should be. However, many people don't do this because the tooling isn't amazing. Developers have grown fond of specific libraries and frameworks to develop our APIs so the target for generating language/framework/library code is vast and appears to be an incredibly hard problem. OpenAPI tries to solve so many problems at once. It's coming in after we've designed our APIs and is trying to describe what's already there. It's an afterthought.&lt;/p&gt;

&lt;p&gt;I've attempted to go down the route of using OpenAPI to generate server stubs and clients. It didn't end well. There were too many issues generating clients and servers, even when the OpenAPI specification was valid. So I had to edit our OpenAPI spec to fit the limitations of the code generators just to get code generation to work. And that was just the beginning of my problems. I contend that this is a natural result of the design goals of the project. OpenAPI was not designed for generating code this way as a priority, so with many complex scenarios, it can be unclear how to map the spec to the semantics of the language/framework/library. OpenAPI wasn't designed for that, it was only designed to describe the API, not the server or client that produces or consumes it. Now consider just how many "targets" for client and server stubs you want. Now consider that target libraries and the OpenAPI spec itself are evolving. This results in a compatibility matrix from hell.&lt;/p&gt;

&lt;h2&gt;
  
  
  So what other options do we have?
&lt;/h2&gt;

&lt;p&gt;REST &lt;em&gt;has&lt;/em&gt; served us well, but the modern web demands more. Let's explore the exciting alternatives that offer a range of benefits and functionalities:&lt;/p&gt;

&lt;h3&gt;
  
  
  GraphQL
&lt;/h3&gt;

&lt;p&gt;The "Choose Your Own Adventure" API. Still with the social media app example, imagine fetching only the user's name and profile picture for the feed view, and then requesting their full profile and friend list separately when a user clicks on their profile. GraphQL allows you to specify exactly the data you need for each view, reducing unnecessary data transfer.&lt;/p&gt;

&lt;p&gt;This method also has its downsides like making the backend API extremely complex.&lt;/p&gt;

&lt;h3&gt;
  
  
  gRPC
&lt;/h3&gt;

&lt;p&gt;The "Cut the Drama" API. Consider a mobile game that communicates with a game server. gRPC allows you to define remote procedures (like &lt;code&gt;attackEnemy&lt;/code&gt; or &lt;code&gt;usePowerUp&lt;/code&gt;) that the client can call directly on the server. This removes the need for complex REST resource mapping and makes the communication intent clear. There are variants of gRPC like &lt;a href="https://dev.to/posts/connectrpc/"&gt;ConnectRPC&lt;/a&gt; that allow for leveraging of HTTP GET requests so you can fully leverage browser caching.&lt;/p&gt;

&lt;h3&gt;
  
  
  WebSockets
&lt;/h3&gt;

&lt;p&gt;Need real-time updates like a live chat or stock ticker? WebSockets offer a persistent two-way communication channel, ideal for constantly flowing data between client and server. This is different from REST's request-response cycle, allowing for a more dynamic connection.&lt;/p&gt;

&lt;h3&gt;
  
  
  Server-Sent Events (SSE)
&lt;/h3&gt;

&lt;p&gt;SSE allows the server to push updates to the client without the client needing to constantly ask. Imagine live sports scores or social media notifications. SSE is simpler to implement than WebSockets, but is one-way (server to client).&lt;/p&gt;

&lt;p&gt;Here's a conclusion that summarizes the key points and offers a final thought:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The Verdict: REST vs. the Rest&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;REST APIs have served us faithfully for years, but as our applications become more complex and data-hungry, it's worth considering the alternatives. GraphQL offers flexibility in data fetching, gRPC provides clear and efficient communication, WebSockets enable bidirectional real-time data flow and great browser support, and SSE simplifies server-to-client updates.&lt;/p&gt;

&lt;p&gt;The choice ultimately depends on your specific needs. But remember, the API landscape is ever-evolving. HTTP/2 and HTTP/3 have opened up some new functionality that we have yet to fully tap into with our API designs. So, keep an open mind, explore the options, and don't be afraid to break free from the comfy (but maybe slightly threadbare) jeans of REST when a more fitting alternative emerges. &lt;/p&gt;

</description>
    </item>
    <item>
      <title>Making gRPC more approachable with ConnectRPC</title>
      <dc:creator>Kevin McDonald</dc:creator>
      <pubDate>Tue, 05 Mar 2024 00:00:00 +0000</pubDate>
      <link>https://dev.to/sudorandom/making-grpc-more-approachable-with-connectrpc-2p8o</link>
      <guid>https://dev.to/sudorandom/making-grpc-more-approachable-with-connectrpc-2p8o</guid>
      <description>&lt;h2&gt;
  
  
  Introduction
&lt;/h2&gt;

&lt;p&gt;gRPC is an open-source framework for building high-performance applications that communicate with each other. gRPC is language-neutral, meaning clients and servers can be written in different programming languages, and it offers features like authentication, streaming, and load balancing. Also, because it is built using protocolbuffers it gives an amazing way to define contracts in a much more clear way than bolting on swagger to your API after the fact. However, gRPC has some limitations that restrict its usage. It requires special gRPC clients and support for HTTP/2 (which still lacking in some areas), and it doesn't work from Javascript.&lt;/p&gt;

&lt;p&gt;Support for Javascript has been solved in a couple of ways. Let's discuss the two approaches!&lt;/p&gt;

&lt;h2&gt;
  
  
  gRPC-Web
&lt;/h2&gt;

&lt;p&gt;gRPC-Web is a &lt;a href="https://github.com/grpc/grpc/blob/master/doc/PROTOCOL-WEB.md" rel="noopener noreferrer"&gt;variant of gRPC&lt;/a&gt; and &lt;a href="https://github.com/grpc/grpc-web" rel="noopener noreferrer"&gt;gRPC client for Javascript&lt;/a&gt; that avoids HTTP/2-specific features like &lt;a href="https://carlmastrangelo.com/blog/why-does-grpc-insist-on-trailers" rel="noopener noreferrer"&gt;HTTP trailers&lt;/a&gt;. This is an incredibly practical solution to the problem. And it does work really well, but I still have my reservations on some of the implementation details.&lt;/p&gt;

&lt;p&gt;gRPC-Web doesn't fix the "this doesn't look like any HTTP API I've ever seen" issue that I have with gRPC in general. In other words, I want to be able to send a normal cURL example to someone. gRPC-Web doesn't work for that without special gRPC-specific clients or tooling.&lt;/p&gt;

&lt;p&gt;Additionally, I don't like how gRPC-Web is typically deployed. You usually are forced into a proxy that can convert gRPC into gRPC-Web. Instead, I prefer the gRPC-Web implementation to sit alongside the actual gRPC server. The protocol isn't so different from the normal gRPC version so it shouldn't be too much work to add support to existing gRPC server implementations. I know that the popular gRPC library &lt;a href="https://docs.rs/tonic/latest/tonic/" rel="noopener noreferrer"&gt;tonic&lt;/a&gt; supports &lt;a href="https://docs.rs/tonic-web/latest/tonic_web/" rel="noopener noreferrer"&gt;gRPC-Web&lt;/a&gt; out of the box.&lt;/p&gt;

&lt;h2&gt;
  
  
  gRPC Transcoding
&lt;/h2&gt;

&lt;p&gt;The idea with transcoding is to annotate your protobuf service methods with HTTP verbs and path patterns that can map a more REST-like API to gRPC. Many solutions allow you to provide a separate config file so you aren't required to have the HTTP annotations making a mess of your protobuf files. &lt;a href="https://cloud.google.com/endpoints/docs/grpc/transcoding" rel="noopener noreferrer"&gt;Google has a service&lt;/a&gt; that can use this mapping and provide a REST-like API on top of your gRPC service. and several gRPC proxies can do this kind of transcoding as well like &lt;a href="https://github.com/grpc-ecosystem/grpc-gateway" rel="noopener noreferrer"&gt;gRPC-Gateway&lt;/a&gt; and &lt;a href="https://www.envoyproxy.io" rel="noopener noreferrer"&gt;envoy&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Here's a simple version of what the annotations can look like:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight protobuf"&gt;&lt;code&gt;&lt;span class="na"&gt;syntax&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"proto3"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kn"&gt;package&lt;/span&gt; &lt;span class="nn"&gt;status&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;v1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="s"&gt;"google/protobuf/empty.proto"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="s"&gt;"google/api/annotations.proto"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="s"&gt;"google/rpc/status.proto"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;service&lt;/span&gt; &lt;span class="n"&gt;Status&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;rpc&lt;/span&gt; &lt;span class="n"&gt;GetStatus&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;google.protobuf.Empty&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;returns&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;google.rpc.Status&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;option&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;google.api.http&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;get&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="s"&gt;"/v1/status"&lt;/span&gt;
    &lt;span class="p"&gt;};&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You can see how &lt;code&gt;status.v1.Status.GetStatus&lt;/code&gt; maps to &lt;code&gt;GET /v1/status&lt;/code&gt;. Thanks, protobuf options!&lt;/p&gt;

&lt;p&gt;However, most ways of deploying this in Go weirdly use proxies, creating a new network hop and a decoding/encoding step. Additionally, transcoding ruins a lot of the benefits you have from a contract-based interface that gRPC provides. Generated clients generally can't interpret the &lt;code&gt;google.api.http&lt;/code&gt; options so if you want to keep to a contract-based model you have to rely on converting your protobuf file to OpenAPI and generating clients based on that. I don't generally prefer this method because it adds extra complexity. However, it can be a really good way to support existing APIs by "swapping out" traditional HTTP handles with gRPC.&lt;/p&gt;

&lt;h2&gt;
  
  
  ConnectRPC
&lt;/h2&gt;

&lt;p&gt;Let me introduce &lt;a href="https://connectrpc.com/" rel="noopener noreferrer"&gt;ConnectRPC&lt;/a&gt;. I believe it elegantly solves all of the issues I have with the gRPC ecosystem. ConnectRPC is a series of libraries for building browser and gRPC-compatible APIs. With ConnectRPC as the server, you get support for three protocols: gRPC, gRPC-Web and &lt;a href="https://connectrpc.com/docs/protocol/" rel="noopener noreferrer"&gt;the so-called "Connect" protocol&lt;/a&gt;. These three protocols are &lt;a href="https://connectrpc.com/docs/multi-protocol/" rel="noopener noreferrer"&gt;all served from a single ConnectRPC server&lt;/a&gt; by simply using the HTTP content-type header, which gRPC and gRPC-Web clients already send. Let me break down where you might use each protocol:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Microservice communication: Connect or gRPC&lt;/li&gt;
&lt;li&gt;Publically exposed API: Connect, gRPC or gRPC-Web depending on client language support&lt;/li&gt;
&lt;li&gt;Clients running in environments without HTTP/2 support: Connect or gRPC-Web&lt;/li&gt;
&lt;li&gt;Sending simple API examples to colleagues: Connect&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Hopefully, I convinced you to at least give ConnectRPC a try. Now, get started with it! Here are the &lt;a href="https://connectrpc.com/docs/go/getting-started" rel="noopener noreferrer"&gt;getting started docs for Go&lt;/a&gt;. It does a great job at detailing how you make a Go server and client implemented with ConnectRPC.&lt;/p&gt;

&lt;h2&gt;
  
  
  Multiple Protocol Support
&lt;/h2&gt;

&lt;p&gt;So, ConnectRPC is running &lt;em&gt;three&lt;/em&gt; different protocols? Isn't that overkill? You can make everything work with the Connect protocol, but there are benefits to supporting all three.&lt;/p&gt;

&lt;p&gt;We can use the gRPC protocol to call into this ConnectRPC service using normal gRPC tooling like &lt;a href="https://github.com/fullstorydev/grpcurl" rel="noopener noreferrer"&gt;grpcurl&lt;/a&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;grpcurl &lt;span class="nt"&gt;-plaintext&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
          &lt;span class="nt"&gt;-proto&lt;/span&gt; greet/v1/greet.proto &lt;span class="se"&gt;\&lt;/span&gt;
          &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="s1"&gt;'{"name": "Jane"}'&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
          127.0.0.1:8080 &lt;span class="se"&gt;\&lt;/span&gt;
          greet.v1.GreetService.Greet
&lt;span class="o"&gt;{&lt;/span&gt;
  &lt;span class="s2"&gt;"greeting"&lt;/span&gt;: &lt;span class="s2"&gt;"Hello, Jane!"&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If you have the &lt;a href="https://github.com/connectrpc/grpcreflect-go" rel="noopener noreferrer"&gt;reflection API enabled&lt;/a&gt;, you can omit the -proto option.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;grpcurl &lt;span class="nt"&gt;-plaintext&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
          &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="s1"&gt;'{"name": "Jane"}'&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
          127.0.0.1:8080 &lt;span class="se"&gt;\&lt;/span&gt;
          greet.v1.GreetService.Greet
&lt;span class="o"&gt;{&lt;/span&gt;
  &lt;span class="s2"&gt;"greeting"&lt;/span&gt;: &lt;span class="s2"&gt;"Hello, Jane!"&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now here's where the magic is. In addition to gRPC-specific tooling, I can also use generic HTTP tools with ConnectRPC servers, like &lt;a href="https://curl.se/" rel="noopener noreferrer"&gt;curl&lt;/a&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;curl &lt;span class="nt"&gt;-XPOST&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
       &lt;span class="nt"&gt;-H&lt;/span&gt;&lt;span class="s2"&gt;"Content-Type: application/json"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
       &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="s1"&gt;'{"name": "Jane"}'&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
       &lt;span class="s2"&gt;"http://127.0.0.1:8080/greet.v1.GreetService/Greet"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This shows how the "I want to be able to send a normal cURL example to someone" desire from above is completely fulfilled.&lt;/p&gt;

&lt;p&gt;I also want to point out that there is also the &lt;code&gt;buf curl&lt;/code&gt; command which is a CLI tool that allows you to call ConnectRPC services using all three protocols (gRPC, gRPC-Web, Connect).&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;buf curl &lt;span class="nt"&gt;--http2-prior-knowledge&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
           &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="s1"&gt;'{"name": "Jane"}'&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
           http://127.0.0.1:8080/greet.v1.GreetService/Greet
&lt;span class="o"&gt;{&lt;/span&gt;
  &lt;span class="s2"&gt;"greeting"&lt;/span&gt;: &lt;span class="s2"&gt;"Hello, Jane!"&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This last command actually uses the Connect protocol. We can pass in &lt;code&gt;--protocol&lt;/code&gt; to use the gRPC-Web or the gRPC protocol instead:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;buf curl &lt;span class="nt"&gt;--http2-prior-knowledge&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
           &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="s1"&gt;'{"name": "Jane"}'&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
           http://127.0.0.1:8080/greet.v1.GreetService/Greet &lt;span class="se"&gt;\&lt;/span&gt;
           &lt;span class="nt"&gt;--protocol&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;grpcweb
&lt;span class="o"&gt;{&lt;/span&gt;
  &lt;span class="s2"&gt;"greeting"&lt;/span&gt;: &lt;span class="s2"&gt;"Hello, Jane!"&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;span class="nv"&gt;$ &lt;/span&gt;buf curl &lt;span class="nt"&gt;--http2-prior-knowledge&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
           &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="s1"&gt;'{"name": "Jane"}'&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
           http://127.0.0.1:8080/greet.v1.GreetService/Greet &lt;span class="se"&gt;\&lt;/span&gt;
           &lt;span class="nt"&gt;--protocol&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;grpc
&lt;span class="o"&gt;{&lt;/span&gt;
  &lt;span class="s2"&gt;"greeting"&lt;/span&gt;: &lt;span class="s2"&gt;"Hello, Jane!"&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Digging Deeper (optional)
&lt;/h3&gt;

&lt;p&gt;This is an optional section where we will dig deeper into what is happening here with this last command to show what is happening under the hood. We will see how server reflection works with gRPC and a couple uses of it. We can see more details of the previous &lt;code&gt;buf curl&lt;/code&gt; command by adding &lt;code&gt;-v&lt;/code&gt; at the end:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;buf curl &lt;span class="nt"&gt;--http2-prior-knowledge&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
           &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="s1"&gt;'{"name": "Jane"}'&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
           http://127.0.0.1:8080/greet.v1.GreetService/Greet &lt;span class="nt"&gt;-v&lt;/span&gt;
buf: &lt;span class="k"&gt;*&lt;/span&gt; Using server reflection to resolve &lt;span class="s2"&gt;"greet.v1.GreetService"&lt;/span&gt;
buf: &lt;span class="k"&gt;*&lt;/span&gt; Dialing &lt;span class="o"&gt;(&lt;/span&gt;tcp&lt;span class="o"&gt;)&lt;/span&gt; 127.0.0.1:8080...
buf: &lt;span class="k"&gt;*&lt;/span&gt; Connected to 127.0.0.1:8080
buf: &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="c"&gt;#1) POST /grpc.reflection.v1.ServerReflection/ServerReflectionInfo&lt;/span&gt;
buf: &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="c"&gt;#1) Accept-Encoding: identity&lt;/span&gt;
buf: &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="c"&gt;#1) Content-Type: application/grpc+proto&lt;/span&gt;
buf: &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="c"&gt;#1) Grpc-Accept-Encoding: gzip&lt;/span&gt;
buf: &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="c"&gt;#1) Grpc-Timeout: 119999m&lt;/span&gt;
buf: &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="c"&gt;#1) Te: trailers&lt;/span&gt;
buf: &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="c"&gt;#1) User-Agent: grpc-go-connect/1.14.0 (go1.21.6) buf/1.29.0&lt;/span&gt;
buf: &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="c"&gt;#1)&lt;/span&gt;
buf: &lt;span class="o"&gt;}&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="c"&gt;#1) [5 bytes data]&lt;/span&gt;
buf: &lt;span class="o"&gt;}&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="c"&gt;#1) [23 bytes data]&lt;/span&gt;
buf: &amp;lt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="c"&gt;#1) HTTP/2.0 200 OK&lt;/span&gt;
buf: &amp;lt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="c"&gt;#1) Content-Type: application/grpc+proto&lt;/span&gt;
buf: &amp;lt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="c"&gt;#1) Date: Sat, 02 Mar 2024 06:41:48 GMT&lt;/span&gt;
buf: &amp;lt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="c"&gt;#1) Grpc-Accept-Encoding: gzip&lt;/span&gt;
buf: &amp;lt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="c"&gt;#1) Grpc-Encoding: gzip&lt;/span&gt;
buf: &amp;lt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="c"&gt;#1)&lt;/span&gt;
buf: &lt;span class="o"&gt;{&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="c"&gt;#1) [5 bytes data]&lt;/span&gt;
buf: &lt;span class="o"&gt;{&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="c"&gt;#1) [244 bytes data]&lt;/span&gt;
buf: &lt;span class="k"&gt;*&lt;/span&gt; Server reflection has resolved file &lt;span class="s2"&gt;"greet/v1/greet.proto"&lt;/span&gt;
buf: &lt;span class="k"&gt;*&lt;/span&gt; Invoking RPC greet.v1.GreetService.Greet
buf: &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="c"&gt;#2) POST /greet.v1.GreetService/Greet&lt;/span&gt;
buf: &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="c"&gt;#2) Accept-Encoding: identity&lt;/span&gt;
buf: &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="c"&gt;#2) Content-Type: application/grpc+proto&lt;/span&gt;
buf: &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="c"&gt;#2) Grpc-Accept-Encoding: gzip&lt;/span&gt;
buf: &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="c"&gt;#2) Grpc-Timeout: 119994m&lt;/span&gt;
buf: &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="c"&gt;#2) Te: trailers&lt;/span&gt;
buf: &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="c"&gt;#2) User-Agent: grpc-go-connect/1.14.0 (go1.21.6) buf/1.29.0&lt;/span&gt;
buf: &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="c"&gt;#2)&lt;/span&gt;
buf: &lt;span class="o"&gt;}&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="c"&gt;#2) [5 bytes data]&lt;/span&gt;
buf: &lt;span class="o"&gt;}&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="c"&gt;#2) [6 bytes data]&lt;/span&gt;
buf: &lt;span class="k"&gt;*&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="c"&gt;#2) Finished upload&lt;/span&gt;
buf: &amp;lt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="c"&gt;#2) HTTP/2.0 200 OK&lt;/span&gt;
buf: &amp;lt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="c"&gt;#2) Content-Type: application/grpc+proto&lt;/span&gt;
buf: &amp;lt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="c"&gt;#2) Date: Sat, 02 Mar 2024 06:41:48 GMT&lt;/span&gt;
buf: &amp;lt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="c"&gt;#2) Greet-Version: v1&lt;/span&gt;
buf: &amp;lt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="c"&gt;#2) Grpc-Accept-Encoding: gzip&lt;/span&gt;
buf: &amp;lt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="c"&gt;#2) Grpc-Encoding: gzip&lt;/span&gt;
buf: &amp;lt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="c"&gt;#2)&lt;/span&gt;
buf: &lt;span class="o"&gt;{&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="c"&gt;#2) [5 bytes data]&lt;/span&gt;
buf: &lt;span class="o"&gt;{&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="c"&gt;#2) [38 bytes data]&lt;/span&gt;
buf: &amp;lt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="c"&gt;#2)&lt;/span&gt;
buf: &amp;lt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="c"&gt;#2) Grpc-Message:&lt;/span&gt;
buf: &amp;lt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="c"&gt;#2) Grpc-Status: 0&lt;/span&gt;
buf: &lt;span class="k"&gt;*&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="c"&gt;#2) Call complete&lt;/span&gt;
&lt;span class="o"&gt;{&lt;/span&gt;
  &lt;span class="s2"&gt;"greeting"&lt;/span&gt;: &lt;span class="s2"&gt;"Hello, Jane!"&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
buf: &amp;lt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="c"&gt;#1)&lt;/span&gt;
buf: &amp;lt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="c"&gt;#1) Grpc-Message:&lt;/span&gt;
buf: &amp;lt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="c"&gt;#1) Grpc-Status: 0&lt;/span&gt;
buf: &lt;span class="k"&gt;*&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="c"&gt;#1) Call complete&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is a little overwhelming at first so I will break down the two different calls that are being made here. Because we aren't passing the protobuf file (or &lt;a href="https://protobuf.com/docs/descriptors" rel="noopener noreferrer"&gt;descriptors&lt;/a&gt;) as the &lt;code&gt;--schema&lt;/code&gt; option we are missing some information needed to generate the protobuf messages from the given JSON string. So that's the first call that &lt;code&gt;buf curl&lt;/code&gt; will make, using the &lt;a href="https://github.com/grpc/grpc/blob/master/doc/server-reflection.md" rel="noopener noreferrer"&gt;Server Reflection&lt;/a&gt; API:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;buf: &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="c"&gt;#1) POST /grpc.reflection.v1.ServerReflection/ServerReflectionInfo&lt;/span&gt;
buf: &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="c"&gt;#1) Accept-Encoding: identity&lt;/span&gt;
buf: &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="c"&gt;#1) Connect-Accept-Encoding: gzip&lt;/span&gt;
buf: &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="c"&gt;#1) Connect-Protocol-Version: 1&lt;/span&gt;
buf: &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="c"&gt;#1) Connect-Timeout-Ms: 119999&lt;/span&gt;
buf: &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="c"&gt;#1) Content-Type: application/connect+proto&lt;/span&gt;
buf: &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="c"&gt;#1) User-Agent: connect-go/1.14.0 (go1.21.6) buf/1.29.0&lt;/span&gt;
buf: &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="c"&gt;#1)&lt;/span&gt;
buf: &lt;span class="o"&gt;}&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="c"&gt;#1) [5 bytes data]&lt;/span&gt;
buf: &lt;span class="o"&gt;}&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="c"&gt;#1) [23 bytes data]&lt;/span&gt;
buf: &amp;lt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="c"&gt;#1) HTTP/2.0 200 OK&lt;/span&gt;
buf: &amp;lt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="c"&gt;#1) Connect-Accept-Encoding: gzip&lt;/span&gt;
buf: &amp;lt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="c"&gt;#1) Connect-Content-Encoding: gzip&lt;/span&gt;
buf: &amp;lt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="c"&gt;#1) Content-Type: application/connect+proto&lt;/span&gt;
buf: &amp;lt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="c"&gt;#1) Date: Sat, 02 Mar 2024 06:33:57 GMT&lt;/span&gt;
buf: &amp;lt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="c"&gt;#1)&lt;/span&gt;
buf: &lt;span class="o"&gt;{&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="c"&gt;#1) [5 bytes data]&lt;/span&gt;
buf: &lt;span class="o"&gt;{&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="c"&gt;#1) [244 bytes data]&lt;/span&gt;
buf: &lt;span class="k"&gt;*&lt;/span&gt; Server reflection has resolved file &lt;span class="s2"&gt;"greet/v1/greet.proto"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It acquired a version of the &lt;code&gt;greet.proto&lt;/code&gt; file from the server. Now our client has everything it needs to make the actual request where it calls the actual service.&lt;/p&gt;

&lt;p&gt;With HTTP, that service lives at &lt;code&gt;POST /greet.v1.GreetService/Greet&lt;/code&gt;. Here's what the call looks like:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;buf: &lt;span class="k"&gt;*&lt;/span&gt; Invoking RPC greet.v1.GreetService.Greet
buf: &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="c"&gt;#2) POST /greet.v1.GreetService/Greet&lt;/span&gt;
buf: &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="c"&gt;#2) Accept-Encoding: identity&lt;/span&gt;
buf: &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="c"&gt;#2) Content-Type: application/grpc+proto&lt;/span&gt;
buf: &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="c"&gt;#2) Grpc-Accept-Encoding: gzip&lt;/span&gt;
buf: &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="c"&gt;#2) Grpc-Timeout: 119994m&lt;/span&gt;
buf: &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="c"&gt;#2) Te: trailers&lt;/span&gt;
buf: &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="c"&gt;#2) User-Agent: grpc-go-connect/1.14.0 (go1.21.6) buf/1.29.0&lt;/span&gt;
buf: &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="c"&gt;#2)&lt;/span&gt;
buf: &lt;span class="o"&gt;}&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="c"&gt;#2) [5 bytes data]&lt;/span&gt;
buf: &lt;span class="o"&gt;}&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="c"&gt;#2) [6 bytes data]&lt;/span&gt;
buf: &lt;span class="k"&gt;*&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="c"&gt;#2) Finished upload&lt;/span&gt;
buf: &amp;lt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="c"&gt;#2) HTTP/2.0 200 OK&lt;/span&gt;
buf: &amp;lt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="c"&gt;#2) Content-Type: application/grpc+proto&lt;/span&gt;
buf: &amp;lt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="c"&gt;#2) Date: Sat, 02 Mar 2024 06:41:48 GMT&lt;/span&gt;
buf: &amp;lt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="c"&gt;#2) Greet-Version: v1&lt;/span&gt;
buf: &amp;lt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="c"&gt;#2) Grpc-Accept-Encoding: gzip&lt;/span&gt;
buf: &amp;lt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="c"&gt;#2) Grpc-Encoding: gzip&lt;/span&gt;
buf: &amp;lt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="c"&gt;#2)&lt;/span&gt;
buf: &lt;span class="o"&gt;{&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="c"&gt;#2) [5 bytes data]&lt;/span&gt;
buf: &lt;span class="o"&gt;{&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="c"&gt;#2) [38 bytes data]&lt;/span&gt;
buf: &amp;lt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="c"&gt;#2)&lt;/span&gt;
buf: &amp;lt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="c"&gt;#2) Grpc-Message:&lt;/span&gt;
buf: &amp;lt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="c"&gt;#2) Grpc-Status: 0&lt;/span&gt;
buf: &lt;span class="k"&gt;*&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="c"&gt;#2) Call complete&lt;/span&gt;
&lt;span class="o"&gt;{&lt;/span&gt;
  &lt;span class="s2"&gt;"greeting"&lt;/span&gt;: &lt;span class="s2"&gt;"Hello, Jane!"&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
buf: &amp;lt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="c"&gt;#1)&lt;/span&gt;
buf: &amp;lt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="c"&gt;#1) Grpc-Message:&lt;/span&gt;
buf: &amp;lt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="c"&gt;#1) Grpc-Status: 0&lt;/span&gt;
buf: &lt;span class="k"&gt;*&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="c"&gt;#1) Call complete&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I wanted to show you how the server reflection works because it is a really good strength of gRPC. You can expose an API and have it be completely discoverable. Tools can automatically use this discovery mechanism... but so can humans. Look at these commands with &lt;code&gt;grpcurl&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;grpcurl &lt;span class="nt"&gt;-plaintext&lt;/span&gt; 127.0.0.1:8080 list
greet.v1.GreetService

&lt;span class="nv"&gt;$ &lt;/span&gt;grpcurl &lt;span class="nt"&gt;-plaintext&lt;/span&gt; 127.0.0.1:8080 describe
greet.v1.GreetService is a service:
service GreetService &lt;span class="o"&gt;{&lt;/span&gt;
  rpc Greet &lt;span class="o"&gt;(&lt;/span&gt; .greet.v1.GreetRequest &lt;span class="o"&gt;)&lt;/span&gt; returns &lt;span class="o"&gt;(&lt;/span&gt; .greet.v1.GreetResponse &lt;span class="o"&gt;)&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;

&lt;span class="nv"&gt;$ &lt;/span&gt;grpcurl &lt;span class="nt"&gt;-plaintext&lt;/span&gt; 127.0.0.1:8080 describe .greet.v1.GreetRequest
greet.v1.GreetRequest is a message:
message GreetRequest &lt;span class="o"&gt;{&lt;/span&gt;
  string name &lt;span class="o"&gt;=&lt;/span&gt; 1&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We can see all the available services using the &lt;code&gt;list&lt;/code&gt; and &lt;code&gt;describe&lt;/code&gt; commands. And if you pass an object to the &lt;code&gt;describe&lt;/code&gt; command you can dig down into message definitions. Protobuf works well here as the API contract for our services.&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;ConnectRPC offers a compelling solution for building gRPC servers that support multiple protocols. It seamlessly integrates gRPC, gRPC-Web, and the Connect protocol, allowing clients written in various languages and environments to interact with your service.  &lt;/p&gt;

&lt;p&gt;Here are the key takeaways:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;ConnectRPC eliminates the limitations of traditional gRPC by supporting HTTP/1.1 and Javascript environments.&lt;/li&gt;
&lt;li&gt;It provides a familiar REST-like API through the Connect protocol while still leveraging the benefits of Protobuf for contracts.&lt;/li&gt;
&lt;li&gt;Multiple protocol support with a single server simplifies deployment and reduces complexity.&lt;/li&gt;
&lt;li&gt;Existing gRPC tooling can still be used for server reflection and making gRPC requests.&lt;/li&gt;
&lt;li&gt;The Connect protocol itself can be used with generic tools like curl, making API exploration a breeze.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If you're looking for a flexible and future-proof way to build gRPC APIs, ConnectRPC is definitely worth considering.  I highly recommend checking out the &lt;a href="https://connectrpc.com/docs/go/getting-started" rel="noopener noreferrer"&gt;getting started guide for Go&lt;/a&gt; for a hands-on approach to using ConnectRPC.&lt;/p&gt;

</description>
    </item>
  </channel>
</rss>
