<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:dc="http://purl.org/dc/elements/1.1/"><channel><title>Api on kmcd.dev</title><link>https://kmcd.dev/tags/api/</link><description>Recent content in Api on kmcd.dev</description><generator>Hugo -- gohugo.io</generator><language>en</language><copyright>All Rights Reserved</copyright><lastBuildDate>Mon, 21 Sep 2026 10:00:00 +0000</lastBuildDate><atom:link href="https://kmcd.dev/tags/api/index.xml" rel="self" type="application/rss+xml"/><item><title>gRPC vs REST Is the Wrong Question</title><link>https://kmcd.dev/posts/grpc-vs-rest/</link><pubDate>Mon, 21 Sep 2026 10:00:00 +0000</pubDate><guid>https://kmcd.dev/posts/grpc-vs-rest/</guid><description> 
                &lt;p> &lt;img hspace="5" src="https://kmcd.dev/posts/grpc-vs-rest/cover.svg" /> &lt;/p>
                
                The traditional advice is REST for public APIs and gRPC for microservices. But is this a false dichotomy?
                </description><content:encoded><![CDATA[<p>I&rsquo;ve seen the same argument over and over on Twitter, LinkedIn, Reddit, etc. Where should you use JSON/HTTP APIs and where should you use gRPC? The same exact advice is almost always given: use HTTP/JSON for public or browser-facing APIs, and gRPC for microservices and infrastructure such as etcd and containerd.</p>
<p>The reasons for this advice are valid: HTTP/JSON <em>is</em> trivial to set up, inspect in DevTools, cache, proxy, and hit from a browser. gRPC and Protobuf <em>do</em> give you schemas, generated clients, compact and fast binary payloads, and streaming.</p>
<p>Why can&rsquo;t we have the best of both?</p>

    
    
        
        
            
        
        <img src="https://kmcd.dev/posts/grpc-vs-rest/rest-vs-grpc-both_hu_5c7b558c5007bed.webp"
             alt="" class="center"/>
    


<p>You <u><em>can</em></u> get the best of both worlds. With <a href="https://connectrpc.com" rel="external">ConnectRPC</a>.</p>
<p><em>(Disclosure: I work at Buf, which maintains ConnectRPC, but I absolutely had all of these opinions beforehand.)</em></p>
<hr>
<h2 id="browser-support">Browser support</h2>
<p>gRPC relies on HTTP/2 features that browser APIs refuse to expose to JavaScript, including <a href="https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Trailer" rel="external">trailers</a>. So gRPC can&rsquo;t be used with web browsers.</p>
<p><a href="https://github.com/grpc/grpc-web" rel="external">gRPC-Web</a> tried to bridge the gap by putting trailers as another frame type inside the body of the HTTP responses. gRPC-Web ultimately failed as a project. Its own roadmap says it can no longer deliver new modern solutions, will not add new features, and recommends gRPC-Gateway instead. I cover the reasons in detail in <a href="https://buf.build/blog/grpc-web-failed-the-web" rel="external">gRPC-Web Failed the Web</a>.</p>
<p>Transcoding is the other common workaround that enables you to have gRPC in the backend and an HTTP/JSON API in the frontend leveraging tools like <a href="https://github.com/grpc-ecosystem/grpc-gateway" rel="external">gRPC-Gateway</a> and <a href="https://www.envoyproxy.io/" rel="external">Envoy</a>. You annotate Protobuf files with <code>google.api.http</code> rules, and the proxy translates inbound requests to gRPC and the outgoing responses back to HTTP/JSON. This works fine, but now the browser-facing API goes through another representation. The Protobuf schema defines the gRPC service, <code>google.api.http</code> defines how that service maps onto HTTP, and tooling may then generate an OpenAPI description and another client from that.</p>
<div class="columns-container " style="--columns-gap: 1.5rem;">
<div>
<img src="/d2-diagrams/11cbdde700399d1cb185d7106ea1a1d9bd0a0e44384a1b0272489429974ca81c.svg" alt="D2 Diagram" loading="lazy" style="max-width: 100%; max-height: inherit; width: 100%; height: auto; object-fit: contain; display: block; margin: 0 auto;" /></div>
<div style="align-self: center;">
<ul>
<li>An extra required hop between the browser and the gRPC backend.</li>
<li>Another HTTP mapping to define and maintain alongside the RPC service.</li>
<li>Browser clients no longer talk directly to the RPC service described by the Protobuf schema.</li>
</ul>
</div>
</div>
<h3 id="the-connect-response">The Connect response</h3>
<p>Connect sidesteps the proxy layer entirely by having the application server handle three protocols natively: standard gRPC, gRPC-Web, and Connect&rsquo;s own <a href="https://connectrpc.com/docs/protocol/" rel="external">HTTP-based protocol</a>. Existing gRPC clients can keep talking standard gRPC, while browsers talk directly to the application server over plain HTTP with JSON or binary Protobuf.</p>
<img src="/d2-diagrams/82e21791ed88462e8c0c1284ea85d470afbdd9ce874cf4224af5feba1feb5c90.svg" alt="D2 Diagram" loading="lazy" style="max-width: 100%; max-height: inherit; width: 100%; height: auto; object-fit: contain; display: block; margin: 0 auto;" /><p>Connect-Web also supports server-streaming RPCs right in the browser using the Fetch API. Outside the browser, the protocol handles client and bidirectional streaming over HTTP/2.</p>
<h2 id="ad-hoc-requests">Ad-hoc requests</h2>
<p>A dead simple requirement for any API is the &ldquo;send a coworker a curl command&rdquo; test. This should be trivial. Standard gRPC fails this spectacularly. gRPC-Web also fails it.</p>
<p>Generated SDKs are actually pretty awesome, but they can get in the way when you want to quickly reproduce an issue or verify a change from the terminal. With gRPC, you have to install <a href="https://github.com/fullstorydev/grpcurl" rel="external">grpcurl</a>, verify reflection is enabled in that environment, or manually pass proto files before calling an API dynamically.</p>
<h3 id="the-connect-way">The Connect way</h3>
<p>A unary Connect request is just an HTTP POST. For quick debugging, you can use <a href="https://protobuf.dev/programming-guides/json/" rel="external">ProtoJSON</a>:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-http" data-lang="http"><span class="line"><span class="cl"><span class="err">Content-Type: application/json
</span></span></span></code></pre></div><p>That makes terminal testing straightforward:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">curl -X POST <span class="se">\
</span></span></span><span class="line"><span class="cl"><span class="se"></span>  https://demo.connectrpc.com/connectrpc.eliza.v1.ElizaService/Say <span class="se">\
</span></span></span><span class="line"><span class="cl"><span class="se"></span>  -H <span class="s2">&#34;Connect-Protocol-Version: 1&#34;</span> <span class="se">\
</span></span></span><span class="line"><span class="cl"><span class="se"></span>  -H <span class="s2">&#34;Content-Type: application/json&#34;</span> <span class="se">\
</span></span></span><span class="line"><span class="cl"><span class="se"></span>  -d <span class="s1">&#39;{&#34;sentence&#34;:&#34;Hello&#34;}&#39;</span>
</span></span></code></pre></div><p>No special CLIs, no need for server reflection. The server handles JSON deserialization according to the Protobuf schema, and the response prints clean, readable ProtoJSON. This example actually works, by the way. You can copy/paste it into your terminal and try.</p>
<p>Errors behave predictably too. Like many RPC systems, standard gRPC returns an HTTP <code>200 OK</code> for every response and puts the actual failures away in <code>grpc-status</code> trailers, which means edge proxies and access logs often report failed calls as perfectly healthy traffic. Connect uses standard HTTP statuses for unary calls: <code>NotFound</code> maps to <code>404</code>, <code>InvalidArgument</code> maps to <code>400</code>, and existing observability tools see unary call failures without custom parsers.</p>
<p>The same pragmatism applies to HTTP verbs. gRPC mandates POST for everything, even simple lookups or queries. Connect supports GET requests for methods flagged as <a href="https://connectrpc.com/docs/go/get-requests-and-caching/" rel="external">side-effect free</a>:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-protobuf" data-lang="protobuf"><span class="line"><span class="cl"><span class="k">rpc</span> <span class="n">Say</span><span class="p">(</span><span class="n">SayRequest</span><span class="p">)</span> <span class="k">returns</span> <span class="p">(</span><span class="n">SayResponse</span><span class="p">)</span> <span class="p">{</span><span class="err">
</span></span></span><span class="line"><span class="cl"><span class="err"></span>  <span class="k">option</span> <span class="n">idempotency_level</span> <span class="o">=</span> <span class="n">NO_SIDE_EFFECTS</span><span class="p">;</span><span class="err">
</span></span></span><span class="line"><span class="cl"><span class="err"></span><span class="p">}</span><span class="err">
</span></span></span></code></pre></div><p>Connect clients can call these endpoints over GET by placing serialized parameters into the query string:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">curl <span class="s2">&#34;https://demo.connectrpc.com/connectrpc.eliza.v1.ElizaService/Say?message=%7B%22sentence%22%3A%22Hello%22%7D&amp;encoding=json&amp;connect=v1&#34;</span>
</span></span></code></pre></div><p>The payload lives in the URL (as JSON or base64-encoded Protobuf), which means standard web caching actually works. Return a standard <a href="https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Cache-Control" rel="external"><code>Cache-Control</code></a> header, and any standard CDN or browser cache will respect it.</p>
<p>You also aren&rsquo;t locked into Protobuf on the client side. Tools like <a href="https://github.com/sudorandom/protoc-gen-connect-openapi" rel="external"><code>protoc-gen-connect-openapi</code></a>, which I created, can generate an OpenAPI spec from your proto definitions. If another team needs OpenAPI specs for their tooling, you can export them without rebuilding your backend architecture around them.</p>
<h2 id="use-connectrpc">Use ConnectRPC</h2>
<p>The usual REST vs gRPC discourse assumes you have to choose between ordinary HTTP semantics and a schema-driven RPC API. Connect doesn&rsquo;t make you choose.</p>
<p>Instead of asking whether to use REST or gRPC, why not ConnectRPC?</p>
]]></content:encoded></item><item><title>It's Time for ConnectRPC to Adopt HTTP QUERY</title><link>https://kmcd.dev/posts/connectrpc-http-query/</link><pubDate>Tue, 28 Jul 2026 10:00:00 +0000</pubDate><guid>https://kmcd.dev/posts/connectrpc-http-query/</guid><description> 
                &lt;p> &lt;img hspace="5" src="https://kmcd.dev/posts/connectrpc-http-query/cover.svg" /> &lt;/p>
                
                
                </description><content:encoded><![CDATA[<p>HTTP QUERY is now standardized in <a href="https://datatracker.ietf.org/doc/html/rfc10008" rel="external">RFC 10008</a>. For backend engineers working with schema-first APIs, this provides a clean solution for a problem we have been working around for years: cacheable requests that require complex, structured input.</p>
<p>That matters for a protocol like ConnectRPC. Connect already tries to map RPCs onto ordinary HTTP instead of fighting the grain of the web. It supports HTTP GET for side-effect-free unary RPCs, which is great for caching, but GET forces structured request payloads into query parameters.</p>
<p>ConnectRPC should support QUERY as the body-carrying counterpart to GET for those same side-effect-free unary calls. Caching support across the broader web can mature over time, but the protocol shape is already useful today: structured request data belongs in the request body, not squeezed into a URL.</p>
<h3 id="why-connect-got-it-right">Why Connect Got It Right</h3>
<p>One of the best design decisions in the Connect protocol is that it leans into standard HTTP semantics. Unlike traditional gRPC, which demands HTTP/2 and relies heavily on trailing headers, Connect maps naturally onto the HTTP infrastructure most teams already run.</p>
<p>That pays off operationally:</p>
<ul>
<li><strong>Meaningful HTTP Status Codes:</strong> Errors map directly to standard HTTP statuses, allowing metrics to work without a specialized gRPC proxy.</li>
<li><strong>Standard Compression:</strong> Traffic relies on standard <code>Content-Encoding</code> headers (like gzip or brotli) already built into your infrastructure.</li>
<li><strong>No Trailers Required:</strong> Requests pass cleanly through standard load balancers, firewalls, and HTTP/1.1 proxies without requiring end-to-end HTTP/2.</li>
<li><strong>Native Ecosystem Integration:</strong> The protocol plugs directly into Go&rsquo;s standard <code>net/http</code> stack, allowing you to reuse standard middleware, multiplexers, and observability tools.</li>
</ul>
<p>Building on this foundation, Connect allows any unary RPC marked as side-effect free (<code>NO_SIDE_EFFECTS</code> in Protobuf) to be invoked via HTTP GET, unlocking caching at the CDN or proxy layer.</p>
<h3 id="the-problem-with-get-and-query-parameters">The Problem with GET and Query Parameters</h3>
<p>To make GET work with complex schema definitions, the protocol has to perform significant gymnastics. Because GET request bodies have no defined semantics and are routinely ignored or rejected by intermediate proxies, Connect is forced to cram structured payloads into the URL.</p>
<p>For a simple JSON request, the client must serialize the payload, URL-encode it, and append it as a query parameter:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-http" data-lang="http"><span class="line"><span class="cl"><span class="nf">GET</span> <span class="nn">/connectrpc.greet.v1.GreetService/Greet?connect=v1&amp;encoding=json&amp;message=%7B%22name%22%3A%22Buf%22%7D</span> <span class="kr">HTTP</span><span class="o">/</span><span class="m">1.1</span>
</span></span><span class="line"><span class="cl"><span class="n">Host</span><span class="o">:</span> <span class="l">demo.connectrpc.com</span>
</span></span></code></pre></div><p>If you use binary Protobuf or compression, the overhead increases further, requiring base64 encoding along with additional control parameters. Shoving these complex payloads into URLs creates immediate practical problems for production systems:</p>
<ul>
<li><strong>Bloated URLs:</strong> Complex requests easily hit maximum URL length limits enforced by load balancers, reverse proxies, and older browsers.</li>
<li><strong>Leaky Logs:</strong> Query parameters show up in plain text in standard <a href="https://nginx.org/" rel="external">Nginx</a> or <a href="https://httpd.apache.org/" rel="external">Apache</a> access logs, WAF dashboards, and observability tools. If a request contains sensitive filter criteria, teams are forced to write custom masking rules to scrub their logs.</li>
<li><strong>Encoding Friction:</strong> Maintaining a separate serialization path just for GET requests introduces branching logic into the codebase. Clients and servers must implement special handling to treat this specific verb entirely differently than the rest of the API surface.</li>
</ul>
<h3 id="enter-http-query">Enter HTTP QUERY</h3>
<p>QUERY gives HTTP the method shape this use case has been missing. Semantically, it is defined as a safe, idempotent method. Mechanically, it operates like a POST, allowing a standard request body.</p>
<p>If ConnectRPC adopts QUERY, the entire query parameter encoding scheme can be dropped. A QUERY request looks exactly like a POST request on the wire, keeping the payload in the HTTP body natively encoded as <code>application/json</code> or <code>application/proto</code>.</p>
<p>Here is how a proposed JSON-based QUERY wire request looks:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-http" data-lang="http"><span class="line"><span class="cl"><span class="err">QUERY /connectrpc.greet.v1.GreetService/Greet HTTP/1.1
</span></span></span><span class="line"><span class="cl"><span class="err">Host: demo.connectrpc.com
</span></span></span><span class="line"><span class="cl"><span class="err">Content-Type: application/json
</span></span></span><span class="line"><span class="cl"><span class="err">Connect-Protocol-Version: 1
</span></span></span><span class="line"><span class="cl"><span class="err">
</span></span></span><span class="line"><span class="cl"><span class="err">{&#34;name&#34;:&#34;Buf&#34;}
</span></span></span></code></pre></div><p>And for binary Protobuf:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-http" data-lang="http"><span class="line"><span class="cl"><span class="err">QUERY /connectrpc.greet.v1.GreetService/Greet HTTP/1.1
</span></span></span><span class="line"><span class="cl"><span class="err">Host: demo.connectrpc.com
</span></span></span><span class="line"><span class="cl"><span class="err">Content-Type: application/proto
</span></span></span><span class="line"><span class="cl"><span class="err">Connect-Protocol-Version: 1
</span></span></span><span class="line"><span class="cl"><span class="err">
</span></span></span><span class="line"><span class="cl"><span class="err">&lt;binary protobuf&gt;
</span></span></span></code></pre></div><p>This is the core protocol win: <strong>QUERY lets Connect reuse the normal unary POST body format instead of maintaining a specialized GET query-encoding path.</strong></p>
<h3 id="caching-demands-query-aware-infrastructure">Caching Demands QUERY-Aware Infrastructure</h3>
<p>From a network caching perspective, QUERY is not simply &ldquo;GET with a body.&rdquo; RFC 10008 specifies that for a QUERY response to be cached, the cache key must incorporate the request body content alongside related metadata.</p>
<p>Most existing HTTP caching infrastructure is built strictly around request metadata such as the method, scheme, host, path, query string, and selected headers. Supporting QUERY requires intermediate proxies, gateways, and CDNs to inspect and hash request bodies. Until major CDNs document first-class support for body-aware cache keys, QUERY caching should be treated as experimental outside infrastructure you control directly.</p>
<h3 id="the-browser-story">The Browser Story</h3>
<p>Browser support is not a reason to avoid QUERY; it is a reason to implement it deliberately.</p>
<p>Because <code>QUERY</code> is not a CORS-safelisted method, cross-origin browser clients require the server or gateway to allow it in preflight responses. This is a standard deployment requirement for modern APIs. Many Connect-Web deployments already require custom CORS configuration for protocol headers, content types, and credentials.</p>
<p>Adopting QUERY gives web clients a clean way to express search forms, filtered list views, reporting queries, and batch reads without resorting to URL hacks.</p>
<h3 id="a-pragmatic-rollout-strategy">A Pragmatic Rollout Strategy</h3>
<p>We cannot flip a switch and expect a new HTTP verb to work across the public internet immediately. The web is built on middleboxes, load balancers, strict firewalls, and managed WAFs that are inherently suspicious of unfamiliar traffic and will likely reject QUERY requests as malformed for some time.</p>
<p>However, many modern backend servers accept arbitrary verbs without complaint. You can verify how your current stack handles unfamiliar methods right now using <a href="https://httpbin.io" rel="external">httpbin.io</a>:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">$ curl -X QUERY <span class="o">[</span>https://httpbin.io/status/200<span class="o">](</span>https://httpbin.io/status/200<span class="o">)</span> -w <span class="s2">&#34;%{http_code}&#34;</span>
</span></span><span class="line"><span class="cl"><span class="m">200</span>
</span></span></code></pre></div><p>To prove the server simply accepts the string token, you can test an arbitrary string:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">$ curl -X YEET <span class="o">[</span>https://httpbin.io/status/200<span class="o">](</span>https://httpbin.io/status/200<span class="o">)</span> -w <span class="s2">&#34;%{http_code}&#34;</span>
</span></span><span class="line"><span class="cl"><span class="m">200</span>
</span></span></code></pre></div><p>While this does not prove full RFC 10008 compliance, it confirms that the initial deployment barrier at the server application layer is low. The harder problem is teaching intermediaries, caches, and client libraries what QUERY actually means.</p>
<p>The most effective early deployments will target paths where engineering teams control the entire network hop: internal service-to-service traffic and browser-facing APIs behind configurable API gateways. Protocols like Connect are the ideal starting point because they can expose QUERY as an opt-in transport mechanism while the broader networking ecosystem catches up.</p>
<h3 id="what-query-should-not-replace-yet">What QUERY Should Not Replace Yet</h3>
<p>While QUERY is the cleaner protocol shape, a practical rollout requires clear boundaries:</p>
<ul>
<li><strong>GET remains useful for small, URL-friendly requests:</strong> For simple payloads that fit naturally in a URI, GET continues to offer mature browser integration, native CDN caching, and effortless manual debugging.</li>
<li><strong>POST remains the compatibility fallback:</strong> When requests have side effects, or when traffic must route through uncooperative legacy intermediaries, POST remains the universal standard.</li>
<li><strong>QUERY starts as opt-in:</strong> Support should initially be opt-in for unary RPCs explicitly marked <code>NO_SIDE_EFFECTS</code>. This option should be exposed to both server-side and web clients, backed by clear deployment guidance for CORS, gateways, and cache behavior.</li>
</ul>
<p>The QUERY method solves a real, persistent networking problem. It is time to put it to work.</p>
]]></content:encoded></item><item><title>Proxy, Record, and Mock gRPC APIs with FauxRPC</title><link>https://kmcd.dev/posts/fauxrpc-proxy/</link><pubDate>Tue, 30 Jun 2026 10:00:00 +0000</pubDate><guid>https://kmcd.dev/posts/fauxrpc-proxy/</guid><description> 
                
                Stop writing mock stubs by hand. How FauxRPC uses smart proxying, reflection, and CEL to automate your API testing.
                </description><content:encoded><![CDATA[<p>Mocking APIs is one of those tasks that starts simple and quickly turns into a chore. You begin with the best intentions, hand-crafting a few JSON fixtures for your frontend tests. But microservices evolve, payloads change, and soon you&rsquo;re maintaining a massive directory of stale mock files. You find yourself trying to remember if user ID <code>42</code> was the one that returns a <code>404</code>, or the one that simulates a slow response.</p>
<p>When you&rsquo;re building with gRPC or ConnectRPC, this problem gets both easier and harder. It&rsquo;s easier because you have a strict schema (the Protobuf contract) to guide you. It&rsquo;s harder because writing binary payloads or mock servers that conform to those schemas by hand is tedious.</p>
<p>In <a href="https://kmcd.dev/posts/fauxrpc/">FauxRPC</a>, I built a tool to generate fake data from Protobuf schemas dynamically. But I wanted to go further. I wanted to make mocks a natural byproduct of running your development environment. That is why I introduced <strong>Proxy Mode</strong> and <strong>Auto-Recording</strong>.</p>
<p>By placing FauxRPC in front of a real upstream service, it acts as a smart proxy: intercepting traffic, forwarding it to the upstream server, and writing out reusable mock stubs to disk. It even generates intelligent matching rules automatically.</p>
<p>Here is how it works under the hood.</p>
<img src="/d2-diagrams/93a139324c38c6c4a0d1ddc61c6e73be68b1af856093a706b924262a5f69d07a.svg" alt="D2 Diagram" loading="lazy" style="max-width: 100%; max-height: inherit; width: 100%; height: auto; object-fit: contain; display: block; margin: 0 auto;" /><h2 id="the-auto-recording-workflow">The Auto-Recording Workflow</h2>
<p>Rather than thinking about mocks as a separate coding chore, FauxRPC integrates mock generation directly into your existing development flow:</p>
<ol>
<li><strong>Start FauxRPC</strong> in proxy mode in front of a staging or local backend server.</li>
<li><strong>Interact with your application</strong> normally in the browser or via API clients.</li>
<li><strong>Capture traffic automatically</strong> as FauxRPC records every request and response into the <code>stubs/</code> directory.</li>
<li><strong>Commit the generated stubs</strong> to Git along with your code changes.</li>
<li><strong>Run your CI test suite</strong> against FauxRPC using those recorded stubs, ensuring fast, offline, and reproducible test runs.</li>
</ol>
<h2 id="zero-configuration-reflection">Zero-Configuration Reflection</h2>
<p>Normally, running a mock server requires you to supply the schema files. You have to pass paths to your <code>.proto</code> files or compiled descriptor sets (<code>.binpb</code>). That&rsquo;s fine for a CI environment, but during local development, dragging files around and keeping them in sync is a pain.</p>
<p>If you start FauxRPC in proxy mode without specifying a schema:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">fauxrpc run --proxy-to<span class="o">=</span>localhost:8080 --record-dir<span class="o">=</span>stubs/
</span></span></code></pre></div><p>FauxRPC uses <strong>gRPC Server Reflection</strong> to discover the schema dynamically from the upstream server on startup.</p>
<p>Behind the scenes, FauxRPC performs a few key steps during this schema discovery phase:</p>
<ol>
<li>It connects to the upstream and queries the reflection service using <code>ListServices</code> to list all endpoints.</li>
<li>For each service, it fetches the file descriptors using <code>FileContainingSymbol</code>.</li>
<li>It recursively collects the raw file descriptors (<code>FileDescriptorProto</code>), deduplicating common dependencies (like <code>google/protobuf/timestamp.proto</code>).</li>
<li>It compiles the gathered descriptors into a <code>FileDescriptorSet</code> and registers them in FauxRPC&rsquo;s registry.</li>
</ol>
<p>This means FauxRPC is ready to handle, translate, and mock any method defined on the upstream server with absolutely zero configuration.</p>
<p>Because the schema is discovered directly from the running service, the proxy automatically stays in sync with upstream changes. No descriptor files need to be exported, committed, or manually refreshed—as your API evolves, FauxRPC adapts instantly.</p>
<h2 id="the-multi-protocol-proxy-engine">The Multi-Protocol Proxy Engine</h2>
<p>FauxRPC is designed to work as a multi-protocol translator. A frontend might communicate using ConnectRPC (over JSON), while the upstream service is a pure gRPC service using binary Protobuf.</p>
<p>To achieve this, FauxRPC sets up a ConnectRPC client using a custom <code>dynamicProtoCodec</code> for proxying. This codec uses Go&rsquo;s protobuf reflection (<code>dynamicpb.Message</code>) to encode and decode payloads on the fly using the schemas retrieved during the reflection phase.</p>
<ul>
<li><strong>Unary calls:</strong> The proxy reads the request, forwards it using the client, intercepts the response, and writes it back.</li>
<li><strong>Streaming calls (client, server, and bidi):</strong> To handle streams, FauxRPC uses <code>FrameTracker</code> objects. The tracker forwards frames in real time so there is no added latency, but it keeps a copy of the sequence in memory for logging and recording.</li>
<li><strong>Metadata and Headers:</strong> When forwarding headers via <code>copyHeaders</code>, FauxRPC filters out protocol-level headers (like <code>content-type</code>, <code>grpc-status</code>, and Connect-specific protocol headers) to let the underlying transport layer handle them cleanly. It also automatically masks sensitive headers (e.g. <code>Authorization</code>, <code>Cookie</code>, <code>X-API-Key</code>) with <code>*****</code> to avoid leaking production keys or user credentials into logs or stubs.</li>
</ul>
<h2 id="graceful-fallbacks-for-parallel-teams">Graceful Fallbacks for Parallel Teams</h2>
<p>In a fast-moving team, schemas are often updated before the code is ready. A backend engineer might merge a <code>.proto</code> change adding a new endpoint, but the actual implementation won&rsquo;t land for days. Normally, this blocks the frontend engineers who need that endpoint to build the UI.</p>
<p>FauxRPC&rsquo;s <strong>Fallback Mode</strong> solves this problem directly.</p>
<p>If the upstream server returns an <code>Unimplemented</code> error code, the proxy doesn&rsquo;t just pass that error to the client. Instead, it enters fallback mode:</p>
<ol>
<li>It checks the local stub database to see if a mock stub matches the request.</li>
<li>If no stub is found, it automatically generates realistic fake data (leveraging <code>protovalidate</code> annotations if they are present in the schema).</li>
<li>The fake response is returned to the client.</li>
</ol>
<p>To the frontend, the endpoint looks and acts like it&rsquo;s fully implemented. The frontend team keeps coding, completely unblocked by backend delays.</p>
<h2 id="auto-recording-stubs">Auto-Recording Stubs</h2>
<p>Writing mock stubs by hand is the worst part of API mocking. In proxy mode, FauxRPC can automate this entirely when you pass the <code>--record-dir</code> flag:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-fallback" data-lang="fallback"><span class="line"><span class="cl">stubs/
</span></span><span class="line"><span class="cl">└── connectrpc.eliza.v1.ElizaService/
</span></span><span class="line"><span class="cl">    ├── Say.json
</span></span><span class="line"><span class="cl">    └── Introduce.json
</span></span></code></pre></div><p>As you interact with your upstream service, FauxRPC intercepts the requests and responses, automatically writing them to disk as structured JSON or YAML files matching your service hierarchy.</p>
<p>For example, a recorded stub might look like this:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-json" data-lang="json"><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">  <span class="nt">&#34;id&#34;</span><span class="p">:</span> <span class="s2">&#34;c85d8869-ad10-449e-ba63-2287f7401c10&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">  <span class="nt">&#34;target&#34;</span><span class="p">:</span> <span class="s2">&#34;connectrpc.eliza.v1.ElizaService/Say&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">  <span class="nt">&#34;active_if&#34;</span><span class="p">:</span> <span class="s2">&#34;req.sentence == \&#34;hello\&#34;&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">  <span class="nt">&#34;content&#34;</span><span class="p">:</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="nt">&#34;sentence&#34;</span><span class="p">:</span> <span class="s2">&#34;Hello! How can I help you today?&#34;</span>
</span></span><span class="line"><span class="cl">  <span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><p>Because these files are saved directly in your codebase, you can commit them to Git and immediately use them as your mock suite in CI/CD pipelines or integration tests. There is no extra setup: just run your application, click around your frontend, and your API mock stubs are generated for you.</p>
<h2 id="a-protobuf-native-dashboard-for-curation">A Protobuf-Native Dashboard for Curation</h2>
<p>Having an automated directory of stubs is a massive time saver, but you still need a way to easily curate and manage them. The developer dashboard in FauxRPC (served at <code>http://localhost:6660/fauxrpc</code> when running with <code>--dashboard</code>) gives you exactly that.</p>
<p>The dashboard captures your live traffic history so you can view the raw JSON payload of any logged request. From there, you can copy a pre-compiled FauxRPC YAML stub, along with its generated <code>active_if</code> matcher, directly to your clipboard. This gives you the ability to carefully curate your stub directory while still completely bypassing the need to hand-write them yourself.</p>
<figure><a href="https://kmcd.dev/posts/fauxrpc-proxy/fauxrpc-request-log-stub.png" class="spotlight" data-download="true" aria-label="Viewing the FauxRPC request log history and copying a pre-generated stub">
    
    
    
    
        
            
            
                
            
            
        
    

    
    
    

    
    
        
    

    <img src="https://kmcd.dev/posts/fauxrpc-proxy/fauxrpc-request-log-stub_hu_c351b08893238947.png"
         alt="Viewing the FauxRPC request log history and copying a pre-generated stub"/>
    </a>
</figure>

<p>This dashboard interface is powered by <strong><a href="https://protodocs.dev/" rel="external">Protodocs</a></strong>, which displays native protobuf schemas directly inside FauxRPC. Because it understands protobuf natively, you can view your packages, messages, and services, and use a built-in API explorer to run test calls (including unary and streaming APIs) directly from the browser without needing complex local Envoy or gRPC-Web proxy configurations.</p>
<h2 id="generating-intelligent-cel-matchers">Generating Intelligent CEL Matchers</h2>
<p>When you are curating these recorded stubs, they are only useful if they match the right request parameters. If you search for user ID <code>12</code>, you want the stub that returns Alice. If you search for user ID <code>42</code>, you want the stub that returns Bob.</p>
<p>Whether auto-saved to disk or copied from the dashboard, FauxRPC automatically compiles request shapes into Common Expression Language (CEL) matching rules by examining the request metadata and payload:</p>
<ol>
<li>It ranges over the fields set on the request message.</li>
<li>It ignores complex fields (nested messages, lists, and maps) to keep the generated rules readable.</li>
<li>For primitive fields (like strings, booleans, and integers), it formats the values into CEL literals.</li>
<li>It joins these field checks with <code>&amp;&amp;</code>.</li>
</ol>
<p>For example, if you send a request where <code>name</code> is &ldquo;Alice&rdquo; and <code>age</code> is 30, FauxRPC automatically generates:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-yaml" data-lang="yaml"><span class="line"><span class="cl"><span class="nt">active_if</span><span class="p">:</span><span class="w"> </span><span class="l">req.name == &#34;Alice&#34; &amp;&amp; req.age == 30</span><span class="w">
</span></span></span></code></pre></div><p>When a subsequent request comes in, FauxRPC compiles and evaluates this expression against the request payload. If it evaluates to <code>true</code>, the stub is served.</p>
<h2 id="recorded-stub-formats">Recorded Stub Formats</h2>
<p>FauxRPC formats these stubs into three distinct kinds based on the call type:</p>
<h3 id="unary--client-streaming-success">Unary / Client-Streaming Success</h3>
<p>For successful unary calls, it records the response payload:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-json" data-lang="json"><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">  <span class="nt">&#34;id&#34;</span><span class="p">:</span> <span class="s2">&#34;c85d8869-ad10-449e-ba63-2287f7401c10&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">  <span class="nt">&#34;target&#34;</span><span class="p">:</span> <span class="s2">&#34;connectrpc.eliza.v1.ElizaService/Say&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">  <span class="nt">&#34;active_if&#34;</span><span class="p">:</span> <span class="s2">&#34;req.sentence == \&#34;hello\&#34;&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">  <span class="nt">&#34;content&#34;</span><span class="p">:</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="nt">&#34;sentence&#34;</span><span class="p">:</span> <span class="s2">&#34;Hello! How can I help you today?&#34;</span>
</span></span><span class="line"><span class="cl">  <span class="p">},</span>
</span></span><span class="line"><span class="cl">  <span class="nt">&#34;priority&#34;</span><span class="p">:</span> <span class="mi">10</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><h3 id="unary--client-streaming-error">Unary / Client-Streaming Error</h3>
<p>If the upstream returned an error, FauxRPC records the status code and message so you can mock error states:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-json" data-lang="json"><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">  <span class="nt">&#34;id&#34;</span><span class="p">:</span> <span class="s2">&#34;18fd7d9e-108c-4a37-bcfc-fa8d7a12ad4f&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">  <span class="nt">&#34;target&#34;</span><span class="p">:</span> <span class="s2">&#34;connectrpc.eliza.v1.ElizaService/Say&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">  <span class="nt">&#34;active_if&#34;</span><span class="p">:</span> <span class="s2">&#34;req.sentence == \&#34;trigger error\&#34;&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">  <span class="nt">&#34;error_code&#34;</span><span class="p">:</span> <span class="mi">3</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">  <span class="nt">&#34;error_message&#34;</span><span class="p">:</span> <span class="s2">&#34;invalid sentence structure&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">  <span class="nt">&#34;priority&#34;</span><span class="p">:</span> <span class="mi">10</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><h3 id="server-streaming--bidirectional-streaming">Server-Streaming / Bidirectional Streaming</h3>
<p>For streaming APIs, it records the sequence of frames, mimicking latency with a default delay, and captures trailing errors:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-json" data-lang="json"><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">  <span class="nt">&#34;id&#34;</span><span class="p">:</span> <span class="s2">&#34;a97df11b-7a31-4b10-8b4b-6f81e3a1f810&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">  <span class="nt">&#34;target&#34;</span><span class="p">:</span> <span class="s2">&#34;connectrpc.eliza.v1.ElizaService/Introduce&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">  <span class="nt">&#34;active_if&#34;</span><span class="p">:</span> <span class="s2">&#34;req.name == \&#34;Bob\&#34;&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">  <span class="nt">&#34;stream&#34;</span><span class="p">:</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="nt">&#34;items&#34;</span><span class="p">:</span> <span class="p">[</span>
</span></span><span class="line"><span class="cl">      <span class="p">{</span> <span class="nt">&#34;content&#34;</span><span class="p">:</span> <span class="p">{</span> <span class="nt">&#34;sentence&#34;</span><span class="p">:</span> <span class="s2">&#34;Hi Bob!&#34;</span> <span class="p">},</span> <span class="nt">&#34;delay&#34;</span><span class="p">:</span> <span class="s2">&#34;100ms&#34;</span> <span class="p">},</span>
</span></span><span class="line"><span class="cl">      <span class="p">{</span> <span class="nt">&#34;content&#34;</span><span class="p">:</span> <span class="p">{</span> <span class="nt">&#34;sentence&#34;</span><span class="p">:</span> <span class="s2">&#34;I am Eliza.&#34;</span> <span class="p">},</span> <span class="nt">&#34;delay&#34;</span><span class="p">:</span> <span class="s2">&#34;100ms&#34;</span> <span class="p">},</span>
</span></span><span class="line"><span class="cl">      <span class="p">{</span> <span class="nt">&#34;error&#34;</span><span class="p">:</span> <span class="p">{</span> <span class="nt">&#34;code&#34;</span><span class="p">:</span> <span class="mi">5</span><span class="p">,</span> <span class="nt">&#34;message&#34;</span><span class="p">:</span> <span class="s2">&#34;session lost&#34;</span> <span class="p">}</span> <span class="p">}</span>
</span></span><span class="line"><span class="cl">    <span class="p">]</span>
</span></span><span class="line"><span class="cl">  <span class="p">},</span>
</span></span><span class="line"><span class="cl">  <span class="nt">&#34;priority&#34;</span><span class="p">:</span> <span class="mi">10</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><h2 id="summary">Summary</h2>
<p>FauxRPC&rsquo;s proxy and recording capabilities bridge the gap between static mock servers and real-world backend services. By discovering schemas through reflection, handling dynamic protocol translation, falling back to fake data, and generating intelligent CEL matchers automatically, it takes the busywork out of API mocking.</p>
<p>Traditional mocks drift because they are maintained separately from the systems they represent. By recording real traffic, discovering schemas automatically, and falling back to generated data when necessary, FauxRPC keeps mock environments aligned with production behavior while dramatically reducing the amount of manual work required. It turns mocks from a maintenance chore into a natural byproduct of running your development environment.</p>
]]></content:encoded></item><item><title>ConnectRPC: Where is it now?</title><link>https://kmcd.dev/posts/connectrpc-where-is-it-now/</link><pubDate>Tue, 05 May 2026 10:00:00 +0000</pubDate><guid>https://kmcd.dev/posts/connectrpc-where-is-it-now/</guid><description> 
                &lt;p> &lt;img hspace="5" src="https://kmcd.dev/posts/connectrpc-where-is-it-now/cover.svg" /> &lt;/p>
                
                Reflecting on two years of ConnectRPC: How it evolved from a gRPC alternative to a complete API ecosystem.
                </description><content:encoded><![CDATA[<p>Two years ago, I wrote <a href="https://kmcd.dev/posts/connectrpc/">Making gRPC more approachable with ConnectRPC</a>. At the time, ConnectRPC was the &ldquo;new kid on the block&rdquo;, a library promising to fix the &ldquo;gRPC tax&rdquo; by supporting HTTP/1.1 and JSON without an extra proxy.</p>
<p>Today, ConnectRPC isn&rsquo;t just a library. It is the core of a toolchain that makes traditional <code>protoc</code> workflows look completely dated. Companies like Anthropic are using it in production to power their SDKs, even maintaining <a href="https://github.com/anthropics/connect-rust" rel="external">their own ConnectRPC library in Rust</a>.</p>
<p>Let&rsquo;s look at how far things have come and how tools like Buf Remote Plugins, Protobuf SDKs, FauxRPC, and native HTTP/3 are changing API development.</p>
<h2 id="code-generation">Code Generation</h2>
<p>One of my biggest complaints in <a href="https://kmcd.dev/posts/working-with-protobuf-in-2024/">Working with Protobuf in 2024</a> was the compatibility matrix from hell. Managing local installations of <code>protoc</code>, <code>protoc-gen-go</code>, and half a dozen other plugins was a miserable onboarding experience. If one person had a slightly different version of a plugin, the generated code drifted, and the CI build would fail for reasons that took twenty minutes to track down.</p>
<p>We can finally stop doing that. Buf Remote Plugins effectively killed the &ldquo;it works on my machine&rdquo; version of <code>protoc</code>. By pointing <code>buf.gen.yaml</code> to remote plugins on the <a href="https://buf.build" rel="external">Buf Schema Registry (BSR)</a>, we get deterministic, zero-install code generation.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-yaml" data-lang="yaml"><span class="line"><span class="cl"><span class="c"># buf.gen.yaml in 2026</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="nt">version</span><span class="p">:</span><span class="w"> </span><span class="l">v2</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="nt">plugins</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span>- <span class="nt">remote</span><span class="p">:</span><span class="w"> </span><span class="l">buf.build/connectrpc/go:v1.19.1</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">out</span><span class="p">:</span><span class="w"> </span><span class="l">gen/go</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">opt</span><span class="p">:</span><span class="w"> </span><span class="l">paths=source_relative</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span>- <span class="nt">remote</span><span class="p">:</span><span class="w"> </span><span class="l">buf.build/protocolbuffers/go:v1.34.1</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">out</span><span class="p">:</span><span class="w"> </span><span class="l">gen/go</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">opt</span><span class="p">:</span><span class="w"> </span><span class="l">paths=source_relative</span><span class="w">
</span></span></span></code></pre></div><p>Your CI pipeline doesn&rsquo;t need a bloated custom Docker image packed with binaries anymore. You just need the <code>buf</code> CLI. New hires clone the repo, run one command, and they’re done. It’s the level of &ldquo;it just works&rdquo; that we should have had a decade ago.</p>
<h2 id="first-class-ide-support">First-Class IDE Support</h2>
<p>Writing Protobuf used to feel like coding in a glorified Notepad. We lacked the basic editor intelligence that almost every other major language enjoys.</p>
<p>That changed in early 2026 when Buf released a production-grade Language Server Protocol (LSP) server for Protobuf. It’s bundled directly into the <code>buf</code> CLI, which means whether you use VSCode or Neovim, you finally get go-to-definition and reference finding that actually works.</p>
<p>The LSP is workspace-aware, too. You can cmd-click an imported message from a third-party library and jump straight to the definition on the BSR without manually syncing files. It also catches syntax errors and duplicate modifiers before you even try to compile, which saves you from that annoying &ldquo;context switch to terminal, run build, see error, switch back&rdquo; loop.</p>
<h2 id="format-lint-and-breaking-changes">Format, Lint, and Breaking Changes</h2>
<p>The Buf CLI also provides the kind of guardrails that keep a team from moving into &ldquo;legacy debt&rdquo; territory too quickly.</p>
<p>If you’ve ever sat through a PR review where someone spent ten comments arguing about whether a field should be <code>camelCase</code> or <code>snake_case</code>, <code>buf fmt</code> and <code>buf lint</code> are for you. They end the debate. You run the command, the code is formatted, and the team moves on to actually solving problems.</p>
<p>The real winner is <code>buf breaking</code>. In a microservices setup, accidentally deleting a field or changing a data type in your schema is a great way to wake up the on-call engineer. By running <code>buf breaking</code> in CI, you verify the current schema against previous commits. It catches destructive changes before they hit the main branch, ensuring your contracts stay stable without requiring a human to manually audit every <code>.proto</code> change.</p>
<h2 id="data-validation">Data Validation</h2>
<p>Validation has historically been a tedious chore. Writing endless <code>if req.Age &lt; 0</code> or <code>if req.Email == &quot;&quot;</code> checks in every single handler is a waste of time and a magnet for bugs.</p>
<p><a href="https://protovalidate.com/" rel="external">protovalidate</a> (which recently hit v1.0) moves those rules directly into the Protobuf schema. Since it’s built on Google&rsquo;s Common Expression Language (CEL), you can do more than just check for nulls; you can write complex cross-field logic, like ensuring a &ldquo;start date&rdquo; is always before an &ldquo;end date.&rdquo;</p>
<p>By dropping the <code>protovalidate</code> interceptor into your server, requests are automatically validated before they touch your business logic. But the real &ldquo;aha!&rdquo; moment is the frontend. Your TypeScript client can run these same rules in the browser before the request even leaves. No more maintaining a separate Zod or Yup schema that inevitably gets out of sync with the backend. One source of truth, enforced everywhere.</p>
<h2 id="docs-and-mocks">Docs and Mocks</h2>
<p>Sharing a gRPC endpoint used to be a pain; you couldn&rsquo;t just hand someone a cURL command and expect it to work. ConnectRPC solved that fundamental issue by supporting standard HTTP/1.1 and JSON. But to truly treat these services like REST APIs, we needed the documentation tooling to match. That is why I spent part of 2024 working on <a href="https://kmcd.dev/posts/protoc-gen-connect-openapi/"><strong>protoc-gen-connect-openapi</strong></a>.</p>
<p>Now, <a href="https://kmcd.dev/posts/self-documenting-connect-services/">Self-Documenting Connect Services</a> are essentially the default for me. Because ConnectRPC skips binary framing for unary calls and uses standard HTTP status codes, we can generate an OpenAPI spec directly from the Protobuf definitions. You can spin up a Swagger UI directly from your server, let external users test with JSON, and keep your strict internal contracts intact.</p>
<p>We’ve also mostly solved the &ldquo;waiting for the backend&rdquo; bottleneck. <a href="https://kmcd.dev/posts/fauxrpc/"><strong>FauxRPC</strong></a> uses your Protobuf descriptors to spin up a mock server in seconds. When you pair it with <a href="https://kmcd.dev/posts/fauxrpc-protovalidate/"><strong>protovalidate</strong></a>, the fake data is actually realistic enough to build a frontend against. Some teams are even running <a href="https://kmcd.dev/posts/fauxrpc-testcontainers/">FauxRPC in Testcontainers</a> for integration tests, which is much cleaner than trying to manage a &ldquo;staging&rdquo; backend for every test run.</p>
<h2 id="why-grpc-web-failed">Why gRPC-Web Failed</h2>
<p>To understand why ConnectRPC won the frontend, you have to look at the history of the protocol. Native gRPC relies on HTTP/2 trailers for status codes, but browsers do not expose those trailers to JavaScript. This originally made gRPC effectively unusable on the web.</p>
<p>The official solution to this problem was gRPC-Web. Its intended goal was straightforward: allow developers to use gRPC directly from web applications. As far as that specific goal goes, it was a success. You could finally make gRPC calls from a browser.</p>
<p>But there is a big difference between a technical success and a widely adopted standard. gRPC-Web never truly took off for a number of reasons. First, it was fundamentally unfriendly to modern infrastructure. It required a separate proxy (usually Envoy) just to translate the frontend requests into something the gRPC backend could understand. This added immediate operational overhead to every project.</p>
<p>Worse, it preserved the most frustrating parts of gRPC. Every single request returned a <strong>200 OK</strong>, regardless of whether the server crashed or the resource was missing. It was a baffling design choice that broke the internet&rsquo;s existing contract for observability. You could not rely on standard load balancer metrics, standard browser dev tools, or your generic APM to see if your site was actually healthy. You were forced to use specialized, protocol-aware tooling just to perform basic debugging. If I have to open a dedicated &ldquo;gRPC-aware&rdquo; network tab just to see why a login failed, I feel like it hasn&rsquo;t actually earned the &ldquo;web&rdquo; part of gRPC-Web name.</p>
<p>ConnectRPC stepped in and completely erased the proxy requirement. It also fixed the integration issues with the traditional web. A unary JSON request in ConnectRPC acts exactly like a standard REST call. If a resource is missing, you get a real <strong>404 Not Found</strong>, and your existing monitoring stack just works. It gave frontend developers the familiar, straightforward debugging experience they actually wanted while keeping the strict schema safety that backend teams need.</p>
<h2 id="why-its-my-default-choice">Why it&rsquo;s my default choice</h2>
<p>In 2024, ConnectRPC was about making gRPC more approachable. Now, the underlying protocol is almost an implementation detail. We get the benefits of typed schemas and code generation, but the friction of the &ldquo;gRPC tax&rdquo; is gone.</p>
<p>If you’re still hand-rolling JSON/REST APIs or wrestling with legacy gRPC-go stubs and Envoy proxies, it’s time to move on. The tools are ready, the workflow is better, and your on-call engineer will thank you.</p>
]]></content:encoded></item><item><title>Building APIs with Contracts</title><link>https://kmcd.dev/posts/api-contracts/</link><pubDate>Tue, 28 Apr 2026 00:00:00 +0000</pubDate><guid>https://kmcd.dev/posts/api-contracts/</guid><description> 
                &lt;p> &lt;img hspace="5" src="https://kmcd.dev/posts/api-contracts/cover.svg" /> &lt;/p>
                
                Building for Scale: Why contract-based APIs are the future.
                </description><content:encoded><![CDATA[<div class="disclaimer">
    This article was originally published in April 2024. It was republished in April 2026 after some significant editing and modernization.
</div>

<p>In today&rsquo;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 often lead to frustrating challenges: breaking changes in JSON APIs, silent failures due to missing fields, frontend and backend drift, or schema mismatches that result in the classic &ldquo;works on my machine&rdquo; excuse.</p>
<p>Imagine a real-world scenario where the backend team renames a <code>userId</code> field to <code>user_id</code> and deploys their changes. Instantly, the frontend checkout process breaks in production because the API had no strict enforcement to catch the mismatch.</p>
<p>This is where <strong>contract-based APIs</strong> come in. A contract-based API is one where the schema is defined first in a formal specification, and both client and server are generated or validated against that contract. They reduce ambiguity and enforce consistency across services.</p>
<h2 id="the-power-of-pre-defined-api-contracts">The Power of Pre-defined API Contracts</h2>
<p>A contract-based API defines exactly what data can be exchanged, in what format, and what actions can be performed. This strict, pre-defined agreement unlocks several immediate advantages:</p>
<ul>
<li><strong>Improved Developer Experience:</strong> Developers on both sides (client and server) have a clear understanding of what is expected, making integration smoother.</li>
<li><strong>Automated Documentation:</strong> Contracts serve as self-documenting artifacts. This reduces the need for manual documentation maintenance and ensures the docs stay in sync with the actual API implementation.</li>
<li><strong>Reduced Errors:</strong> Mismatched data formats or API changes become less likely, leading to fewer bugs. Contracts act as a validation layer that catches potential issues early.</li>
<li><strong>Easier Integration:</strong> Contracts act as a single source of truth. Developers can quickly understand how to interact with the API without extensive back and forth communication.</li>
<li><strong>Streamlined Development:</strong> These APIs often enable tools to automatically generate code for both client and server implementations. This eliminates manual boilerplate so you can focus on core logic.</li>
</ul>
<h2 id="protobuf-the-language-of-apis">Protobuf: The Language of APIs</h2>
<p>In modern distributed systems, the foundation of many contract-based APIs lies in <a href="https://protobuf.dev/" rel="external"><strong>Protocol Buffers (protobuf)</strong></a>. It is a language-neutral data format specifically designed for structured messages.</p>
<p>Unlike JSON, which is a text-based format designed to be human-readable, Protobuf is a <strong>binary format</strong>. This means you trade the ability to natively read the raw data in transit for significant performance gains:</p>
<ul>
<li><strong>Smaller Message Sizes:</strong> Protobuf messages are compact and efficient, which leads to faster transmission and reduced bandwidth usage.</li>
<li><strong>Faster Parsing:</strong> Parsing binary protobuf messages is significantly faster compared to traditional formats like JSON or XML.</li>
<li><strong>Built-in Versioning:</strong> Protobuf uses field numbers (the <code>= 1</code>, <code>= 2</code> in the code below) to identify data. This allows for excellent backward and forward compatibility. You can add new fields without breaking older clients that do not know about them yet.</li>
<li><strong>Cross-language Compatibility:</strong> Protobuf definitions are language-agnostic. Code for interacting with the API can be generated for almost any modern programming language.</li>
</ul>
<p>Because the data is binary, you cannot simply open your browser&rsquo;s network tab and read the payloads by default. You will usually need to rely on modern browser extensions (like the gRPC-Web or Connect dev tools) to decode the traffic. It also requires setting up specialized tooling and build steps to compile the generated code.</p>
<p>Here is a basic example of a <code>.proto</code> file defining messages for a user and an address:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-protobuf" data-lang="protobuf"><span class="line"><span class="cl"><span class="n">syntax</span> <span class="o">=</span> <span class="s">&#34;proto3&#34;</span><span class="p">;</span><span class="err">
</span></span></span><span class="line"><span class="cl"><span class="err">
</span></span></span><span class="line"><span class="cl"><span class="err"></span><span class="kd">message</span> <span class="nc">User</span> <span class="p">{</span><span class="err">
</span></span></span><span class="line"><span class="cl"><span class="err"></span>  <span class="kt">string</span> <span class="n">name</span> <span class="o">=</span> <span class="mi">1</span><span class="p">;</span><span class="err">
</span></span></span><span class="line"><span class="cl"><span class="err"></span>  <span class="kt">int32</span> <span class="n">id</span> <span class="o">=</span> <span class="mi">2</span><span class="p">;</span><span class="err">
</span></span></span><span class="line"><span class="cl"><span class="err"></span>  <span class="kt">string</span> <span class="n">email</span> <span class="o">=</span> <span class="mi">3</span><span class="p">;</span><span class="err">
</span></span></span><span class="line"><span class="cl"><span class="err"></span>  <span class="n">Address</span> <span class="n">address</span> <span class="o">=</span> <span class="mi">4</span><span class="p">;</span><span class="err">
</span></span></span><span class="line"><span class="cl"><span class="err"></span><span class="p">}</span><span class="err">
</span></span></span><span class="line"><span class="cl"><span class="err">
</span></span></span><span class="line"><span class="cl"><span class="err"></span><span class="kd">message</span> <span class="nc">Address</span> <span class="p">{</span><span class="err">
</span></span></span><span class="line"><span class="cl"><span class="err"></span>  <span class="kt">string</span> <span class="n">street</span> <span class="o">=</span> <span class="mi">1</span><span class="p">;</span><span class="err">
</span></span></span><span class="line"><span class="cl"><span class="err"></span>  <span class="kt">string</span> <span class="n">city</span> <span class="o">=</span> <span class="mi">2</span><span class="p">;</span><span class="err">
</span></span></span><span class="line"><span class="cl"><span class="err"></span>  <span class="kt">string</span> <span class="n">state</span> <span class="o">=</span> <span class="mi">3</span><span class="p">;</span><span class="err">
</span></span></span><span class="line"><span class="cl"><span class="err"></span>  <span class="kt">string</span> <span class="n">zip</span> <span class="o">=</span> <span class="mi">4</span><span class="p">;</span><span class="err">
</span></span></span><span class="line"><span class="cl"><span class="err"></span><span class="p">}</span><span class="err">
</span></span></span></code></pre></div><p>In this example, the <code>User</code> message has fields for name, ID, email, and an <code>Address</code> message. These defined structures ensure consistent data exchange between applications.</p>
<blockquote>
<p><strong>Key idea:</strong> Protobuf relies on immutable field numbers instead of field names. This golden rule guarantees backward and forward compatibility.</p>
</blockquote>
<h2 id="grpc-building-apis-on-a-solid-foundation">gRPC: Building APIs on a Solid Foundation</h2>
<p><strong>gRPC (gRPC Remote Procedure Call)</strong> is a high-performance framework that builds upon protobuf&rsquo;s strengths. It provides a powerful way to implement remote procedure calls, allowing applications to interact using clients generated for each language.</p>
<h3 id="introducing-services-and-requestresponse-types-with-grpc">Introducing Services and Request/Response Types with gRPC</h3>
<p>We can expand the <code>.proto</code> file to define a service called <code>UserService</code> with methods for user management:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-protobuf" data-lang="protobuf"><span class="line"><span class="cl"><span class="n">syntax</span> <span class="o">=</span> <span class="s">&#34;proto3&#34;</span><span class="p">;</span><span class="err">
</span></span></span><span class="line"><span class="cl"><span class="err">
</span></span></span><span class="line"><span class="cl"><span class="err"></span><span class="kd">service</span> <span class="n">UserService</span> <span class="p">{</span><span class="err">
</span></span></span><span class="line"><span class="cl"><span class="err"></span>  <span class="k">rpc</span> <span class="n">CreateUser</span><span class="p">(</span><span class="n">CreateUserRequest</span><span class="p">)</span> <span class="k">returns</span> <span class="p">(</span><span class="n">User</span><span class="p">)</span> <span class="p">{}</span><span class="err">
</span></span></span><span class="line"><span class="cl"><span class="err"></span>  <span class="k">rpc</span> <span class="n">GetUser</span><span class="p">(</span><span class="n">GetUserRequest</span><span class="p">)</span> <span class="k">returns</span> <span class="p">(</span><span class="n">User</span><span class="p">)</span> <span class="p">{}</span><span class="err">
</span></span></span><span class="line"><span class="cl"><span class="err"></span><span class="p">}</span><span class="err">
</span></span></span><span class="line"><span class="cl"><span class="err">
</span></span></span><span class="line"><span class="cl"><span class="err"></span><span class="kd">message</span> <span class="nc">CreateUserRequest</span> <span class="p">{</span><span class="err">
</span></span></span><span class="line"><span class="cl"><span class="err"></span>  <span class="n">User</span> <span class="n">user</span> <span class="o">=</span> <span class="mi">1</span><span class="p">;</span><span class="err">
</span></span></span><span class="line"><span class="cl"><span class="err"></span><span class="p">}</span><span class="err">
</span></span></span><span class="line"><span class="cl"><span class="err">
</span></span></span><span class="line"><span class="cl"><span class="err"></span><span class="kd">message</span> <span class="nc">GetUserRequest</span> <span class="p">{</span><span class="err">
</span></span></span><span class="line"><span class="cl"><span class="err"></span>  <span class="kt">int32</span> <span class="n">id</span> <span class="o">=</span> <span class="mi">1</span><span class="p">;</span><span class="err">
</span></span></span><span class="line"><span class="cl"><span class="err"></span><span class="p">}</span><span class="err">
</span></span></span></code></pre></div><p>This example defines a <code>UserService</code> with two methods: <code>CreateUser</code> and <code>GetUser</code>. Each method takes a specific request message and returns a response.</p>
<p>Notice how clear the intention is. A helpful mental model to contrast modern APIs is:</p>
<ul>
<li><strong>REST</strong> is resource-oriented (relying on URLs and HTTP verbs).</li>
<li><strong>gRPC</strong> is action-oriented (relying on explicit methods).</li>
</ul>
<p>A reader of this spec does not have to map vague HTTP verbs like &ldquo;POST&rdquo; to actions like &ldquo;create.&rdquo; Also, these method names are <a href="https://en.wiktionary.org/wiki/greppable" rel="external">greppable</a>. It is trivial to locate every use of <code>CreateUser</code> across several repositories, making refactoring and impact analysis much easier.</p>
<h3 id="server-reflection">Server Reflection</h3>
<p>Another powerful feature of the gRPC ecosystem is <strong>Server Reflection</strong>. This allows clients or debugging tools (like Postman or grpcurl) to query the server at runtime to discover the available services and methods. This eliminates the need to distribute <code>.proto</code> files to developers just so they can explore the API structure.</p>
<h2 id="distributing-api-contracts">Distributing API Contracts</h2>
<p>Defining a contract is only half the battle. How do the frontend and backend teams actually share that <code>.proto</code> file? If the schema is not easily accessible, the contract is useless.</p>
<p>In practice, teams usually solve this distribution problem in one of three ways:</p>
<ol>
<li><strong>Monorepos:</strong> Storing the backend, frontend, and API definitions in a single repository so all code shares the same source of truth.</li>
<li><strong>Package Managers:</strong> Generating the client SDKs in a CI/CD pipeline and publishing them as internal NPM, Maven, or Go packages.</li>
<li><strong>Schema Registries:</strong> Using dedicated tools like the <a href="https://buf.build/" rel="external">Buf Schema Registry</a> to manage, version, and distribute Protobuf files securely across an organization.</li>
</ol>
<h2 id="what-about-public-apis">What about public APIs?</h2>
<p>Historically, strict RPC contracts were tough for external, public-facing APIs. If your primary consumers were third-party developers, handing them a raw Protobuf file or expecting them to set up gRPC clients caused massive friction. They just wanted to use standard REST with JSON.</p>
<p>This is where tools like <a href="https://connectrpc.com/" rel="external"><strong>ConnectRPC</strong></a> shine. ConnectRPC allows you to define your API using Protobuf, but it automatically exposes endpoints that support standard HTTP/1.1 and JSON serialization as a fallback format.</p>
<p>This hybrid approach also solves the local debugging problem. You can configure ConnectRPC to use JSON during local development specifically so you can read the network tab in plain text, and then flip it to highly efficient binary for production. In practice, you write Protobuf once, and get both gRPC and REST/JSON APIs for free.</p>
<p>Even better, because the source of truth is still Protobuf, you can use ecosystem plugins to automatically generate an OpenAPI specification directly from your <code>.proto</code> files. You get a highly maintainable, contract-driven architecture on the backend, while your external users can still <code>curl</code> standard REST endpoints, read plain JSON, and explore your API via a generated Swagger UI. It offers the best of both worlds without compromising the developer experience on either side.</p>
<blockquote>
<p><strong>Key idea:</strong> Tools like ConnectRPC allow you to maintain strict internal Protobuf contracts while exposing standard REST/JSON APIs to external consumers.</p>
</blockquote>
<h2 id="alternatives">Alternatives</h2>
<p>While Protobuf and gRPC are a powerful duo, there are other contract-based API solutions to consider depending on your architecture:</p>
<ul>
<li><a href="https://www.openapis.org/" rel="external"><strong>OpenAPI (Swagger)</strong></a>: Contracts are not exclusive to RPC. You can use OpenAPI to define strict contracts for RESTful services. However, a harsh reality of the industry is that OpenAPI specs often drift from the actual code because they are bolted on after the fact. To make OpenAPI truly safe, teams must rely on strict framework integration (like FastAPI in Python or tsoa in Node) where the code generates the spec, or vice versa.</li>
<li><a href="https://graphql.org/" rel="external"><strong>GraphQL</strong></a>: Arguably the most mainstream contract-driven API paradigm for frontend developers. Its strictly typed schema defines the exact shape of the available data. Unlike gRPC, which has fixed responses, GraphQL allows the client to dictate the exact payload it wants to receive.</li>
<li><a href="https://twitchtv.github.io/twirp/" rel="external"><strong>Twirp</strong></a>: Developed by Twitch, Twirp is a lightweight RPC framework built on top of Protobuf and HTTP/1.1. It shares similarities with ConnectRPC but focuses on absolute simplicity. It avoids the complexity of HTTP/2 and gRPC streams while still providing generated clients, making it an excellent alternative if full gRPC is overkill for your needs.</li>
<li><a href="https://thrift.apache.org/" rel="external"><strong>Thrift</strong></a>: Originally developed at Facebook, Thrift is a language-neutral protocol for defining service contracts similar to Protobuf. It is often found in large-scale data environments and supports various RPC protocols.</li>
<li><a href="https://trpc.io/" rel="external"><strong>tRPC</strong></a>: This tool defines the API schema directly in TypeScript code to be reused on both the client and the server. While it often pairs with libraries like Zod for runtime validation, it lacks true language-agnostic safety across the network boundary since it relies entirely on a TypeScript ecosystem.</li>
<li><a href="https://avro.apache.org/" rel="external"><strong>Avro</strong></a>: This format uses JSON-like schemas but stores data in a compact binary format. It is a staple in the Apache Kafka ecosystem for streaming data pipelines. It handles schema evolution differently than Protobuf (often sending the schema alongside the data), making it highly flexible for dynamic systems.</li>
</ul>
<h2 id="when-not-to-use-api-contracts">When NOT to Use API Contracts</h2>
<p>While these tools are powerful, they are not a silver bullet. You should reconsider using strict API contracts if:</p>
<ul>
<li><strong>You are building small projects or MVPs:</strong> The initial setup, code generation, and boilerplate overhead might slow down your speed of delivery when rapid iteration is the top priority.</li>
<li><strong>Simplicity for external consumers outweighs strict contracts:</strong> If you are building a straightforward public API and are not using a hybrid tool like ConnectRPC, raw JSON over REST remains the path of least resistance for third-party developers.</li>
<li><strong>Your team lacks tooling maturity:</strong> Implementing gRPC or Protobuf requires solid CI/CD pipelines and a team that is comfortable managing build steps, code generation, and backward-compatible schema evolutions.</li>
</ul>
<blockquote>
<p><strong>Key idea:</strong> Strict API contracts add overhead and may not be suitable for small MVPs, simple public APIs, or teams lacking tooling maturity.</p>
</blockquote>
<h2 id="conclusion">Conclusion</h2>
<p>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 highly efficient code.</p>
<p>As a general rule of thumb: if you are building an early-stage prototype, stick to what is fast and familiar. But if you are scaling a complex system across multiple teams and services, contract-based APIs transition from a nice-to-have to an absolute necessity. Once multiple teams depend on your API, contracts stop being optional. They are how you avoid chaos.</p>
]]></content:encoded></item><item><title>The Case for Greppable Code</title><link>https://kmcd.dev/posts/greppable/</link><pubDate>Tue, 21 Apr 2026 00:00:00 +0000</pubDate><guid>https://kmcd.dev/posts/greppable/</guid><description> 
                &lt;p> &lt;img hspace="5" src="https://kmcd.dev/posts/greppable/cover.svg" /> &lt;/p>
                
                Why naming is about navigation, not just aesthetics, especially in a multi-repo world.
                </description><content:encoded><![CDATA[<div class="disclaimer">
    This article was originally published in April 2024. It was republished in April 2026 after some significant editing and modernization.
</div>

<p>Imagine staring at a production log with a generic error in a function called <code>processData()</code>. You search the codebase, only to find forty different functions with that exact name spread across five repositories. This is the opposite of searchable code.</p>
<p><strong>Greppability</strong><sup class="citation" id="footnote-ref-3"><a href="#footnote-3">[3]</a></sup>
 is a measure of how easily a human can find specific logic using simple text search. Modern IDEs are great, but greppability is the safety net for when they fail: during code reviews, in terminal sessions, while scanning traces, or when navigating a massive mesh of microservices.</p>
<h3 id="navigation-is-the-job">Navigation is the Job</h3>
<p>Developers spend a considerable amount of effort on program comprehension, which includes reading and understanding source code. In fact, research shows that programmers spend roughly <strong>70% of their time</strong><sup class="citation" id="footnote-ref-1"><a href="#footnote-1">[1]</a></sup>
 on code comprehension.</p>
<p>When you use a generic name like <code>entity</code>, you increase the &ldquo;search cost&quot;<sup class="citation" id="footnote-ref-2"><a href="#footnote-2">[2]</a></sup>
 of the workday. Research indicates that when programmers deal with high comprehension effort, they navigate and make edits at a significantly slower rate. Choosing specific names like <code>CustomerBillingRecord</code> ensures your search result is a surgical strike rather than a list of a hundred collisions.</p>
<h3 id="spanning-across-repos">Spanning Across Repos</h3>
<p>The real power of greppability shows up in <strong>polyglot environments</strong>. When your infrastructure spans multiple repositories and languages, like Go, TypeScript, and Python, your IDE &ldquo;Jump to Definition&rdquo; often stops at the edge of the current project.</p>
<p>In these distributed systems, a unique string is the only universal bridge. A specific domain term, like <code>SubscriptionRenewalWebhook</code>, connects a frontend UI component to a backend service implementation across repo boundaries. If both use the same unique name, you can track a feature across the entire stack in seconds without needing a specialized indexer for every language you use.</p>
<blockquote>
<p><strong>Key idea:</strong> Greppability turns your codebase into a searchable database. By treating unique strings as the &ldquo;primary keys&rdquo; of your architecture, you bypass the limitations of IDEs and language boundaries, especially in multi-repo environments where traditional navigation tools often break.</p>
</blockquote>
<h3 id="how-to-write-for-grep">How to Write for Grep</h3>
<ul>
<li><strong>The Log-to-Code Pipeline:</strong> If a method handles a critical business action, name it something distinct. If a system crashes at 3:00 AM and the log says <code>Error in CalculateRegionalTax()</code>, you have found the bug before you have even opened your editor.</li>
<li><strong>Avoid Generic Names:</strong> Names like <code>data</code>, <code>info</code>, <code>manager</code>, or <code>entity</code> are search poison. <code>orderValidationLogic</code> is longer, but it is a direct hit for a search engine.</li>
<li><strong>Establish a Naming Hierarchy:</strong> Focus your &ldquo;naming budget&rdquo; where the scope is widest. Filenames, class names, and API methods must be unique. Local variables inside a five-line function, like <code>i</code> or <code>buf</code>, can stay generic because their search boundary is tiny.</li>
<li><strong>Beware of Dynamic Magic:</strong> If your language uses reflection or string interpolation to call methods, such as <code>this.call(&quot;prefix_&quot; + action)</code>, you have killed the ability to grep for the implementation.</li>
</ul>
<h3 id="the-grpc-search-advantage">The gRPC Search Advantage</h3>
<p>This is where RPC-based designs offer a distinct advantage over REST. In a REST architecture, searching for an &ldquo;update&rdquo; action usually requires a two-step mental grep: find the path (<code>/users/:id</code>) and then filter by the HTTP verb (<code>PUT</code>).</p>
<p>In gRPC, the method name is a <strong>globally unique string</strong>.</p>
<table>
  <thead>
      <tr>
          <th style="text-align: left">Architecture</th>
          <th style="text-align: left">Search Precision</th>
          <th style="text-align: left">Debugging Speed</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td style="text-align: left"><strong>REST</strong> (<code>PUT /user</code>)</td>
          <td style="text-align: left">Low (Many paths use <code>PUT</code>)</td>
          <td style="text-align: left">Slow: requires manual filtering.</td>
      </tr>
      <tr>
          <td style="text-align: left"><strong>gRPC</strong> (<code>UpdateUserBio</code>)</td>
          <td style="text-align: left"><strong>High (1-2 results)</strong></td>
          <td style="text-align: left"><strong>Fast: direct jump to logic.</strong></td>
      </tr>
  </tbody>
</table>
<p>In a gRPC environment, <code>UpdateUserBio()</code> appears identically in your <code>.proto</code> file, your server code, your client code, and your monitoring dashboard. It is the ultimate greppable identifier.</p>
<h3 id="summary">Summary</h3>
<p><strong>Greppability</strong> is an engineering-first term. It moves naming away from subjective aesthetics and toward functional operability. When you tell a teammate that a name is not very greppable, you are not critiquing their style: you are pointing out a future debugging bottleneck.</p>
<div class="citations-section">
  <h2 class="citations-title">Footnotes</h2>
  
<ol class="footnote-list">
    
<li id="footnote-1">
  <div class="footnote-item-content">Comprehension Effort and Programming Activities: Related? Or Not Related? (2018)<span class="footnote-links"><a href="https://akondrahman.github.io/files/papers/msr18_chall.pdf" target="_blank" rel="noopener noreferrer" class="footnote-link">
          <i class="fa-solid fa-arrow-up-right-from-square"></i>
        </a><a href="#footnote-ref-1" class="footnote-back-link" title="Jump back to reference">
        <i class="fa-solid fa-arrow-turn-up"></i>
      </a>
    </span>
  </div>
</li>

<li id="footnote-2">
  <div class="footnote-item-content">How Developers Search for Code: A Case Study (Google Research)<span class="footnote-links"><a href="https://research.google/pubs/how-developers-search-for-code-a-case-study/" target="_blank" rel="noopener noreferrer" class="footnote-link">
          <i class="fa-solid fa-arrow-up-right-from-square"></i>
        </a><a href="#footnote-ref-2" class="footnote-back-link" title="Jump back to reference">
        <i class="fa-solid fa-arrow-turn-up"></i>
      </a>
    </span>
  </div>
</li>

<li id="footnote-3">
  <div class="footnote-item-content">grep (global regular expression print) is a command-line utility for searching plain-text data sets for lines that match a regular expression.<span class="footnote-links"><a href="#footnote-ref-3" class="footnote-back-link" title="Jump back to reference">
        <i class="fa-solid fa-arrow-turn-up"></i>
      </a>
    </span>
  </div>
</li>


</ol>


</div>

]]></content:encoded></item><item><title>Unknown Fields in Protobuf</title><link>https://kmcd.dev/posts/protobuf-unknown-fields/</link><pubDate>Thu, 16 Apr 2026 00:00:00 +0000</pubDate><guid>https://kmcd.dev/posts/protobuf-unknown-fields/</guid><description> 
                &lt;p> &lt;img hspace="5" src="https://kmcd.dev/posts/protobuf-unknown-fields/cover.svg" /> &lt;/p>
                
                How Protobuf unknown fields enable seamless schema evolution and robust middleware.
                </description><content:encoded><![CDATA[<div class="disclaimer">
    This article was originally published in March 2024. It was republished in April 2026 after some significant editing and modernization.
</div>

<p><a href="https://protobuf.dev/programming-guides/proto3/" rel="external">Protobuf</a> includes a feature known as <a href="https://protobuf.dev/programming-guides/proto3/#unknowns" rel="external"><strong>unknown fields</strong></a>. They act as a safety net when systems encounter data they weren&rsquo;t explicitly built to handle. Here is a breakdown of what they are and why they matter.</p>
<h2 id="what-are-protobuf-unknown-fields">What are Protobuf Unknown Fields?</h2>
<p>Your <code>.proto</code> file defines the expected structure, fields, and data types. But what happens when you parse a message and it contains fields that aren&rsquo;t in your current <code>.proto</code> definition?</p>
<p>These extra pieces of data are called <strong>unknown fields</strong>.</p>
<p>At a lower level, unknown fields are <strong>field numbers and wire types that exist in the serialized message but are not defined in the current schema</strong>.</p>
<p>This mechanism is what enables <strong>forward compatibility</strong>: an older version of your software can safely read, process, and forward data produced by a newer version of the schema without crashing or losing the new data.</p>
<blockquote>
<p><strong>Key idea:</strong> Unknown fields enable forward compatibility by default.</p>
</blockquote>
<hr>
<h2 id="preserving-unknown-data">Preserving Unknown Data</h2>
<p>A key aspect of unknown fields is how they behave during message manipulation.</p>
<p>If you receive a message with unknown fields and forward it to another system, Protobuf defaults to <strong>forwarding the unknown fields alongside the known ones</strong>. This ensures the receiving system gets the complete payload.</p>
<p>If this didn&rsquo;t happen, you could accidentally clear field values set by another part of the system.</p>
<p>This forwarding capability also applies when <strong>persisting messages</strong>, as long as they remain in <strong>binary Protobuf format</strong>. If you store and later reload the binary payload, the unknown fields are preserved.</p>
<blockquote>
<p><strong>Key idea:</strong> Binary Protobuf preserves unknown fields end-to-end.</p>
</blockquote>
<blockquote>
<p><strong>Historical Note:</strong> Protobuf v3 initially tried to simplify the specification by removing several proto2 features, but real-world usage forced them to walk the biggest ones back. Early versions of proto3 dropped unknown fields entirely, but this was reversed in v3.5. Similarly, proto3 initially removed the <code>optional</code> keyword, but brought it back in v3.15 after developers struggled to distinguish between a field being unset and a field just having a zero value, which is <a href="https://en.wikipedia.org/wiki/Null_Island" rel="external">a classic programming mistake</a>.</p>
</blockquote>
<hr>
<h2 id="comparison-to-json">Comparison to JSON</h2>
<p>Consider a scenario where a new field, <code>email</code>, is added to a user object. The backend is updated, but the frontend is not.</p>
<p>The issue in JSON systems is not JSON itself, it&rsquo;s <strong>typed deserialization</strong>.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-json" data-lang="json"><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">  <span class="nt">&#34;user&#34;</span><span class="p">:</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="nt">&#34;id&#34;</span><span class="p">:</span> <span class="s2">&#34;0edc0903-9e31-47be-adad-1dfc434ca2d3&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="nt">&#34;name&#34;</span><span class="p">:</span> <span class="s2">&#34;Bob&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="nt">&#34;email&#34;</span><span class="p">:</span> <span class="s2">&#34;bob@example.com&#34;</span>
</span></span><span class="line"><span class="cl">  <span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><p>If the frontend maps this into a typed structure:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-typescript" data-lang="typescript"><span class="line"><span class="cl"><span class="kr">class</span> <span class="nx">User</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">  <span class="nx">id</span>: <span class="kt">string</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">  <span class="nx">name</span>: <span class="kt">string</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><p>The unknown field (<code>email</code>) is dropped during deserialization. When the object is sent back:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-json" data-lang="json"><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">  <span class="nt">&#34;user&#34;</span><span class="p">:</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="nt">&#34;id&#34;</span><span class="p">:</span> <span class="s2">&#34;0edc0903-9e31-47be-adad-1dfc434ca2d3&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="nt">&#34;name&#34;</span><span class="p">:</span> <span class="s2">&#34;Bob&#34;</span>
</span></span><span class="line"><span class="cl">  <span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><p>The <code>email</code> field is lost.</p>
<blockquote>
<p><strong>Key idea:</strong> Typed JSON pipelines often drop unknown fields during reserialization.</p>
</blockquote>
<hr>
<h2 id="protobuf-behavior">Protobuf Behavior</h2>
<p>With Protobuf, the same scenario behaves differently.</p>
<p>Even if the frontend does not know about the <code>email</code> field, it is preserved internally:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-typescript" data-lang="typescript"><span class="line"><span class="cl"><span class="nx">Symbol</span><span class="p">(</span><span class="kd">@bufbuild</span><span class="o">/</span><span class="nx">protobuf</span><span class="o">/</span><span class="kt">unknown</span><span class="o">-</span><span class="nx">fields</span><span class="p">)</span><span class="o">:</span> <span class="p">[</span>
</span></span><span class="line"><span class="cl">  <span class="p">{</span><span class="mi">0</span><span class="o">:</span> <span class="p">{</span><span class="nx">no</span>:<span class="kt">3</span><span class="p">,</span> <span class="nx">wire_type</span>:<span class="kt">2</span><span class="p">,</span> <span class="nx">data</span>: <span class="kt">Uint8Array</span><span class="p">(</span><span class="mi">14</span><span class="p">)}}</span>
</span></span><span class="line"><span class="cl"><span class="p">]</span>
</span></span></code></pre></div><p><em>(Note: This specific <code>Symbol</code> representation is how the <code>@bufbuild/protobuf</code> implementation manages it under the hood. Other JS/TS generators might expose this data slightly differently, but the underlying concept remains the same.)</em></p>
<ul>
<li><code>no: 3</code> → field number (email)</li>
<li><code>wire_type: 2</code> → length-delimited (used for strings)</li>
<li><code>data</code> → raw encoded value</li>
</ul>
<p>When the message is re-encoded:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-text" data-lang="text"><span class="line"><span class="cl">1:LEN {&#34;0edc0903-9e31-47be-adad-1dfc434ca2d3&#34;}
</span></span><span class="line"><span class="cl">2:LEN {&#34;Bob&#34;}
</span></span><span class="line"><span class="cl">3:LEN {&#34;bob@example.com&#34;}
</span></span></code></pre></div><p>The unknown field survives the round trip.</p>
<blockquote>
<p><strong>Key idea:</strong> Unknown fields are preserved even when not understood.</p>
</blockquote>
<hr>
<h2 id="why-we-cant-guess-the-type">Why We Can&rsquo;t &ldquo;Guess&rdquo; the Type</h2>
<p>You might wonder why we can&rsquo;t just look at the raw data in an unknown field and &ldquo;guess&rdquo; what it is. The reason is that Protobuf uses a very limited set of <strong>wire types</strong> that are shared across many different high-level data types.</p>
<table>
  <thead>
      <tr>
          <th style="text-align: left">Wire Type</th>
          <th style="text-align: left">Meaning</th>
          <th style="text-align: left">Used for&hellip;</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td style="text-align: left"><code>0</code></td>
          <td style="text-align: left">Varint</td>
          <td style="text-align: left"><code>int32</code>, <code>int64</code>, <code>uint32</code>, <code>uint64</code>, <code>sint32</code>, <code>sint64</code>, <code>bool</code>, <code>enum</code></td>
      </tr>
      <tr>
          <td style="text-align: left"><code>1</code></td>
          <td style="text-align: left">64-bit</td>
          <td style="text-align: left"><code>fixed64</code>, <code>sfixed64</code>, <code>double</code></td>
      </tr>
      <tr>
          <td style="text-align: left"><code>2</code></td>
          <td style="text-align: left">Length-delimited</td>
          <td style="text-align: left"><code>string</code>, <code>bytes</code>, embedded messages, packed repeated fields</td>
      </tr>
      <tr>
          <td style="text-align: left"><code>5</code></td>
          <td style="text-align: left">32-bit</td>
          <td style="text-align: left"><code>fixed32</code>, <code>sfixed32</code>, <code>float</code></td>
      </tr>
  </tbody>
</table>
<p>If you see an unknown field with <strong>Wire Type 0</strong>, you don&rsquo;t know if it represents the number <code>150</code>, the boolean <code>true</code>, or an enum value.</p>
<p>More critically, if you see <strong>Wire Type 2</strong>, you have no way of knowing if the data is a <code>string</code>, a <code>bytes</code> buffer, or a complex nested message. If you try to &ldquo;guess&rdquo; and parse a string as a sub-message, you will likely get gibberish or a parsing error. This is why the schema is strictly required to <em>understand</em> the data, while the binary format is sufficient to <em>preserve</em> it.</p>
<hr>
<h2 id="the-middleware-advantage">The Middleware Advantage</h2>
<p>Unknown fields shine in internal, middleware-heavy architectures.</p>
<p>Example:</p>
<ul>
<li>API Gateway reads <code>id</code> for routing</li>
<li>Logging service reads <code>trace_id</code></li>
<li>Downstream service understands full schema including new fields</li>
</ul>
<p>Intermediate services can safely:</p>
<ol>
<li>Unmarshal using an older schema</li>
<li>Read known fields</li>
<li>Forward the message unchanged</li>
</ol>
<p>No coordination is required when new fields are added upstream.</p>
<blockquote>
<p><strong>Key idea:</strong> Internal middleware can stay stable while schemas evolve.</p>
</blockquote>
<hr>
<h2 id="observability-a-signal-for-upgrades">Observability: A Signal for Upgrades</h2>
<p>Beyond just forwarding data safely, unknown fields provide a highly valuable observability metric.</p>
<p>When an API gateway or a downstream service detects unknown fields in incoming payloads, it is a clear telemetry signal: a client or upstream service is sending extra information because it is using a newer schema.</p>
<p>Instead of crashing or silently dropping the data, the service can log the presence of these unknown fields. You can use this data to trigger alerts, track the rollout progress of new features across your architecture, and pinpoint exactly which legacy services are lagging behind and due for an upgrade.</p>
<hr>
<h2 id="a-note-on-json-serialization-and-object-re-use">A Note on JSON Serialization and Object Re-use</h2>
<p>There are a couple of important exceptions where unknown fields get lost.</p>
<p>First, unknown field preservation applies <strong>only to binary Protobuf serialization</strong>. If you convert from binary to JSON (e.g., using <code>protojson</code> in Go or <code>toJson</code> in TypeScript), unknown fields are <strong>dropped</strong> during the encoding process. When unmarshaling JSON back into Protobuf, many libraries are strictly configured by default. For example, Go&rsquo;s <code>protojson.Unmarshal</code> will throw a hard error if it encounters unknown fields in the JSON payload unless you explicitly bypass it by passing <code>DiscardUnknown: true</code>. JSON simply isn&rsquo;t designed to carry this extra payload without a strict schema map.</p>
<p>Second, preserving these fields during binary serialization requires that you re-use the exact same object for re-serialization. If you read a message, pull out the known fields, and map them into a freshly created object to send downstream, the unknown fields tied to the original object will be left behind.</p>
<blockquote>
<p><strong>Key idea:</strong> Binary preserves and JSON drops. Always re-use the original object if you want to keep unknown fields intact.</p>
</blockquote>
<hr>
<h2 id="databases-and-security">Databases and Security</h2>
<p>The theoretical elegance of unknown fields often collides with the messy reality of databases and security perimeters. In practice, relying on unknown fields breaks down entirely in a few critical scenarios.</p>
<p>First, let us consider database persistence. If clients are trying to store extra data, and a backend service parses a Protobuf message to map it to standard relational database columns, those unknown fields are absolutely gone. There is no magic column for data your database schema does not know about.</p>
<p>The only way to achieve true end-to-end preservation is to store the entire serialized Protobuf message directly in the database as a BLOB. Some teams do this, but blindly storing data you haven&rsquo;t validated and don&rsquo;t even recognize is highly dangerous.</p>
<p>Allowing unknown fields to propagate unchecked from external sources is a significant security risk. While they are a powerful tool inside clearly defined, trusted internal pipelines, accepting them from the open web opens your system up to data smuggling. It allows malicious actors to sneak unvalidated payloads into unknown fields to bypass validation layers that only inspect known schema structures. If your systems blindly unmarshal, store, and forward this data, older services act as unwitting mules for malicious input.</p>
<p>Because of these exact risks, the standard security posture is to aggressively filter at the edge. API Gateways and ingress proxies should explicitly discard unknown fields before the data ever reaches internal microservices.</p>
<hr>
<h2 id="conclusion">Conclusion</h2>
<p>Unknown fields provide a powerful mechanism for <strong>forward compatibility</strong> in distributed systems. They allow internal systems to evolve independently, act as a clear signal for required upgrades, reduce coordination overhead, and simplify middleware design.</p>
<p>However, they are not a substitute for validation, schema discipline, or proper security boundaries. Use them intentionally in trusted internal pipelines, but never trust them at the edge.</p>
]]></content:encoded></item><item><title>Breaking gRPC</title><link>https://kmcd.dev/posts/breaking-grpc/</link><pubDate>Tue, 05 Aug 2025 10:00:00 +0000</pubDate><guid>https://kmcd.dev/posts/breaking-grpc/</guid><description> 
                &lt;p> &lt;img hspace="5" src="https://kmcd.dev/posts/breaking-grpc/cover_hu_72d86e0dbbafcc2.webp" /> &lt;/p>
                
                How to avoid breaking gRPC clients.
                </description><content:encoded><![CDATA[<p>When we use gRPC, we often praise its efficiency and strong contracts defined by Protocol Buffers (<code>.proto</code> files). We know that gRPC uses protobuf&rsquo;s binary format for fast, compact, and forward/backward-compatible communication. But what happens when you expose your gRPC service to clients who speak JSON, like a web frontend?</p>
<p>The encoding you use (binary protobuf or transcoded JSON) dramatically changes the rules of what constitutes a &ldquo;safe&rdquo; or &ldquo;breaking&rdquo; change to your API. A change that is perfectly harmless for a protobuf client can completely break a JSON client. Let&rsquo;s dig into this more.</p>
<h2 id="how-encodings-work">How Encodings Work</h2>
<p>First, a quick refresher on how each format represents data. Consider this simple protobuf message:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-protobuf" data-lang="protobuf"><span class="line"><span class="cl"><span class="n">syntax</span> <span class="o">=</span> <span class="s">&#34;proto3&#34;</span><span class="p">;</span><span class="err">
</span></span></span><span class="line"><span class="cl"><span class="err">
</span></span></span><span class="line"><span class="cl"><span class="err"></span><span class="kn">package</span> <span class="nn">my_service</span><span class="o">.</span><span class="n">v1</span><span class="p">;</span><span class="err">
</span></span></span><span class="line"><span class="cl"><span class="err">
</span></span></span><span class="line"><span class="cl"><span class="err"></span><span class="kd">message</span> <span class="nc">User</span> <span class="p">{</span><span class="err">
</span></span></span><span class="line"><span class="cl"><span class="err"></span>  <span class="c1">// A unique identifier for the user.
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>  <span class="kt">int64</span> <span class="n">user_id</span> <span class="o">=</span> <span class="mi">1</span><span class="p">;</span><span class="err">
</span></span></span><span class="line"><span class="cl"><span class="err"></span>  <span class="c1">// The user&#39;s full name.
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>  <span class="kt">string</span> <span class="n">name</span> <span class="o">=</span> <span class="mi">2</span><span class="p">;</span><span class="err">
</span></span></span><span class="line"><span class="cl"><span class="err"></span><span class="p">}</span><span class="err">
</span></span></span></code></pre></div><h3 id="protobuf-its-all-about-the-numbers">Protobuf: It&rsquo;s All About the Numbers</h3>
<p>On the wire, the binary protobuf encoding doesn&rsquo;t care about the field names (<code>user_id</code>, <code>name</code>). It only cares about the <strong>field numbers</strong> (<code>1</code>, <code>2</code>) and their wire types. A simplified view of the encoded data is a series of key-value pairs where the key is the field number. I dig into this further in my <a href="https://kmcd.dev/posts/grpc-from-scratch-part-3/">gRPC from Scratch</a> series, where I discuss the binary protobuf encoding.</p>
<p>Because of this, you can rename a field in your <code>.proto</code> file, and as long as the field number and type remain the same, it&rsquo;s a <strong>non-breaking change</strong> for protobuf clients.</p>
<h3 id="json-its-all-about-the-names">JSON: It&rsquo;s All About the Names</h3>
<p>When a gRPC gateway or library transcodes this message to JSON, it produces a standard JSON object. JSON is also a perfectly valid encoding to use with gRPC. By default, it uses the protobuf field names (converted to <code>lowerCamelCase</code>) as the JSON keys:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-json" data-lang="json"><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">  <span class="nt">&#34;userId&#34;</span><span class="p">:</span> <span class="mi">12345</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">  <span class="nt">&#34;name&#34;</span><span class="p">:</span> <span class="s2">&#34;Alex&#34;</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><p>Since JSON clients are coupled to these names, changing them will inevitably break the integration. <strong>JSON clients are coupled to field names, not field numbers.</strong> This fundamental difference is the source of many potential compatibility issues.</p>
<h2 id="analyzing-api-changes-breaking-vs-non-breaking">Analyzing API Changes: Breaking vs. Non-Breaking</h2>
<p>Let&rsquo;s look at common changes you might make to a <code>.proto</code> file and see their impact on each encoding.</p>
<table>
  <thead>
      <tr>
          <th style="text-align: left">Change</th>
          <th style="text-align: left">Protobuf Impact</th>
          <th style="text-align: left">JSON Impact</th>
          <th style="text-align: left">Explanation</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td style="text-align: left"><strong>Renaming a field</strong> (<code>name</code> to <code>full_name</code>)</td>
          <td style="text-align: left">✅ <strong>Non-breaking</strong></td>
          <td style="text-align: left">💥 <strong>Breaking</strong></td>
          <td style="text-align: left">Protobuf clients only see the field number (<code>2</code>), which hasn&rsquo;t changed. JSON clients expect the key <code>&quot;name&quot;</code> but will now see <code>&quot;fullName&quot;</code>.</td>
      </tr>
      <tr>
          <td style="text-align: left"><strong>Changing a field number</strong> (<code>= 2</code> to <code>= 3</code>)</td>
          <td style="text-align: left">💥 <strong>Breaking</strong></td>
          <td style="text-align: left">✅ <strong>Non-breaking</strong></td>
          <td style="text-align: left">This is a cardinal sin in the protobuf world. A client expecting field <code>2</code> will no longer find it. JSON clients, however, still see the key <code>&quot;name&quot;</code> and are unaffected.</td>
      </tr>
      <tr>
          <td style="text-align: left"><strong>Adding a new field</strong> (<code>email = 3</code>)</td>
          <td style="text-align: left">✅ <strong>Non-breaking</strong></td>
          <td style="text-align: left">✅ <strong>Non-breaking</strong></td>
          <td style="text-align: left">Well-behaved clients in both formats are designed to ignore unknown fields, making this a safe operation.</td>
      </tr>
      <tr>
          <td style="text-align: left"><strong>Removing or deprecating a field</strong></td>
          <td style="text-align: left">✅ <strong>Non-breaking</strong></td>
          <td style="text-align: left">✅ <strong>Non-breaking</strong></td>
          <td style="text-align: left">Similar to adding a field, clients should handle missing fields gracefully. It&rsquo;s best practice to <code>deprecate</code> a field before removing it.</td>
      </tr>
      <tr>
          <td style="text-align: left"><strong>Changing a compatible type</strong> (<code>int32</code> to <code>int64</code>)</td>
          <td style="text-align: left">✅ <strong>Non-breaking</strong></td>
          <td style="text-align: left">✅ <strong>Non-breaking</strong></td>
          <td style="text-align: left">These types have compatible wire formats in protobuf. For JSON, both are simply numbers, so there&rsquo;s no issue.</td>
      </tr>
      <tr>
          <td style="text-align: left"><strong>Changing an incompatible type</strong> (<code>int64</code> to <code>string</code>)</td>
          <td style="text-align: left">💥 <strong>Breaking</strong></td>
          <td style="text-align: left">💥 <strong>Breaking</strong></td>
          <td style="text-align: left">The wire format for a number and a string are different, breaking protobuf clients. The data type in JSON also changes (e.g., <code>123</code> vs. <code>&quot;123&quot;</code>), which will break any client expecting a number.</td>
      </tr>
  </tbody>
</table>
<hr>
<h2 id="the-solution-decouple-names-with-json_name">The Solution: Decouple Names with <code>json_name</code></h2>
<p>So, how do you refactor your <code>.proto</code> field names without breaking your JSON clients? The protobuf specification provides a simple and elegant solution: the <strong><code>json_name</code></strong> field option.</p>
<p>This option lets you explicitly set the JSON key for a field, decoupling it from the <code>.proto</code> field name.</p>
<p>Let&rsquo;s revise our <code>User</code> message. Suppose we want to rename <code>name</code> to <code>full_name</code> for clarity in our Go or Python code, but we can&rsquo;t break existing JSON clients that rely on the <code>&quot;name&quot;</code> key.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-protobuf" data-lang="protobuf"><span class="line"><span class="cl"><span class="n">syntax</span> <span class="o">=</span> <span class="s">&#34;proto3&#34;</span><span class="p">;</span><span class="err">
</span></span></span><span class="line"><span class="cl"><span class="err">
</span></span></span><span class="line"><span class="cl"><span class="err"></span><span class="kn">package</span> <span class="nn">my_service</span><span class="o">.</span><span class="n">v1</span><span class="p">;</span><span class="err">
</span></span></span><span class="line"><span class="cl"><span class="err">
</span></span></span><span class="line"><span class="cl"><span class="err"></span><span class="kd">message</span> <span class="nc">User</span> <span class="p">{</span><span class="err">
</span></span></span><span class="line"><span class="cl"><span class="err"></span>  <span class="kt">int64</span> <span class="n">user_id</span> <span class="o">=</span> <span class="mi">1</span> <span class="p">[</span><span class="n">json_name</span> <span class="o">=</span> <span class="s">&#34;userId&#34;</span><span class="p">];</span><span class="err">
</span></span></span><span class="line"><span class="cl"><span class="err">
</span></span></span><span class="line"><span class="cl"><span class="err"></span>  <span class="c1">// The field is now &#39;full_name&#39; in code, but will still be &#39;name&#39; in JSON.
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>  <span class="kt">string</span> <span class="n">full_name</span> <span class="o">=</span> <span class="mi">2</span> <span class="p">[</span><span class="n">json_name</span> <span class="o">=</span> <span class="s">&#34;name&#34;</span><span class="p">];</span><span class="err">
</span></span></span><span class="line"><span class="cl"><span class="err"></span><span class="p">}</span><span class="err">
</span></span></span></code></pre></div><p>With <code>json_name = &quot;name&quot;</code>, we&rsquo;ve instructed the transcoder to do the following:</p>
<ol>
<li><strong>For Protobuf:</strong> Continue using field number <code>2</code>. The field name <code>full_name</code> is used by the code generator.</li>
<li><strong>For JSON:</strong> Always use the key <code>&quot;name&quot;</code> during serialization, regardless of what the <code>.proto</code> field is called.</li>
</ol>
<p>Now, you are free to change the <code>full_name</code> field to something else (e.g., <code>user_display_name</code>) in the future, and your JSON contract remains stable.</p>
<h2 id="automating-your-safety-net-with-buf-breaking">Automating Your Safety Net with <code>buf breaking</code></h2>
<p>Remembering all these nuanced rules across different encodings is difficult and error-prone. This is where automated tooling becomes essential. The popular <a href="https://buf.build/" rel="external">Buf toolchain</a> includes a powerful command, <strong><code>buf breaking</code></strong>, designed specifically for this problem.</p>
<p>The <code>buf breaking</code> command compares your current <code>.proto</code> files against a previous state (like your main git branch) and reports any changes that would break your API consumers. Crucially, it understands that &ldquo;breaking&rdquo; means different things to different clients. You can configure it to check against multiple compatibility strategies.</p>
<p>In your <code>buf.yaml</code> configuration file, you can specify which rule sets to check against:</p>
<ul>
<li><code>FILE</code>: Checks for backward-incompatible changes at the <code>.proto</code> file level, like deleting a field or changing a field number. This protects your <strong>protobuf-based clients</strong>.</li>
<li><code>WIRE_JSON</code>: Checks for backward-incompatible changes for the JSON wire format. This catches things like renaming a field without using <code>json_name</code>. This protects your <strong>JSON-based clients</strong>.</li>
<li><code>PACKAGE</code>: Checks for source-code-level breaking changes in the generated stubs for languages like Go and Java. This protects the <strong>developers using your generated code</strong>.</li>
</ul>
<p>A typical configuration for a service with both gRPC and JSON clients might look like this:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-yaml" data-lang="yaml"><span class="line"><span class="cl"><span class="c"># buf.yaml</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="nt">version</span><span class="p">:</span><span class="w"> </span><span class="l">v2</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="nt">breaking</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">use</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span>- <span class="l">FILE</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span>- <span class="l">WIRE_JSON</span><span class="w">
</span></span></span></code></pre></div><p>By integrating <code>buf breaking</code> into your CI/CD pipeline, you can automatically prevent developers from merging changes that would break any of your consumers, whether they speak protobuf or JSON.</p>
<h2 id="conclusion">Conclusion</h2>
<p>Evolving an API for both protobuf and JSON clients is a recipe for a very specific kind of headache, the kind that pages you at 3 AM. You&rsquo;ve got protobuf, which only cares about numbers, and JSON, which only cares about names. A &ldquo;safe&rdquo; refactor for one is a production-breaking slap in the face for the other. This is where a schema-first approach, backed by powerful schema-aware tooling, isn&rsquo;t just a good idea; it&rsquo;s the only thing keeping you from questioning all your life choices.</p>
<p>Protobuf&rsquo;s semantics, like the <code>json_name</code> option, give you a powerful escape hatch. It makes certain refactors, like renaming a field for internal clarity, trivial <em>if</em> you have the right tooling in place. You can change your code without your JSON clients ever knowing you touched a thing. This decoupling is a superpower, but only if you use it correctly.</p>
<p>And that&rsquo;s the catch: don&rsquo;t rely on developers&rsquo; goldfish sized memory or manual code reviews to enforce these complex, conflicting rules. That&rsquo;s how you break production at 3 AM. Instead, let the robots do the heavy lifting. Integrating a tool like <code>buf breaking</code> into your CI pipeline is like having an unblinking, unforgiving guardian for your API. It understands the different breaking change rules for both protobuf and JSON and will stop a bad change before it ever gets merged. This is the real strength of a schema-first workflow: it makes complex refactors not just possible, but safe. You can merge with confidence and keep all your clients (binary or JSON) happy.</p>
]]></content:encoded></item><item><title>HTTP QUERY and Go</title><link>https://kmcd.dev/posts/http-query/</link><pubDate>Wed, 04 Jun 2025 10:00:00 +0000</pubDate><guid>https://kmcd.dev/posts/http-query/</guid><description><![CDATA[ 
                <p> <img hspace="5" src="https://kmcd.dev/posts/http-query/cover_hu_2439f564eb3b6556.webp" /> </p>
                
                We need another HTTP verb. I&#39;ll explain why.
                ]]></description><content:encoded><![CDATA[<p>You&rsquo;re likely familiar with the HTTP methods, <strong>GET</strong> and <strong>POST</strong>, 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 <code>GET</code>. This is where a concept like a <strong>QUERY</strong> method comes in, and it&rsquo;s not just a thought experiment; it&rsquo;s an area actively being <a href="https://httpwg.org/http-extensions/draft-ietf-httpbis-safe-method-w-body.html" rel="external">explored by the IETF HTTP Working Group</a>.</p>
<h3 id="but-why">But, why?</h3>
<p>The standard HTTP methods serve us well, except the ones that don&rsquo;t&hellip; but that&rsquo;s a topic for another day. <strong>GET</strong> 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.</p>
<p>This often leads developers to use <strong>POST</strong> for what are semantically read-only query operations. Many widely-used protocols effectively do this:</p>
<ul>
<li><strong>GraphQL</strong> typically uses POST requests with JSON bodies to send queries.</li>
<li><strong>gRPC</strong> and <strong>gRPC-Web</strong> typically rely exclusively on POST.</li>
<li>Older protocols like <strong>SOAP</strong> and <strong>XML-RPC</strong> almost exclusively use POST to encapsulate their operations, including data retrieval. This has been an issue for a long time!</li>
</ul>
<p>While using POST works, it&rsquo;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 &ldquo;query-via-POST&rdquo; requests with undue caution, forgoing caching or automatic retries that would be safe for a truly read-only operation.</p>
<p>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 <a href="https://httpwg.org/http-extensions/draft-ietf-httpbis-safe-method-w-body.html" rel="external">&ldquo;A Safe HTTP Method with a Request Content&rdquo;</a>. As the draft states:</p>
<blockquote>
<p>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.</p>
</blockquote>
<p>While the draft uses &ldquo;QUERY&rdquo; as a candidate name (among others), the core idea is what we&rsquo;re exploring: a method for safe, idempotent data retrieval that can carry a payload. For the rest of this article, we&rsquo;ll continue to use &ldquo;QUERY&rdquo; to represent this concept and show how you can implement such a custom method in Go today.</p>
<p>It&rsquo;s crucial to remember that until such a method is formally standardized and widely adopted, <strong>using custom HTTP methods can impact interoperability</strong>. However, for internal APIs, tightly controlled systems, or as a forward-looking experiment, they can be very useful.</p>
<h3 id="server-side-with-go">Server-Side with Go</h3>
<p>Go 1.22 introduced enhancements to <code>http.ServeMux</code> that allow you to register handlers for specific HTTP methods and paths more directly. Let&rsquo;s build a server that handles our custom <strong>QUERY</strong> method:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="cl"><span class="kn">package</span><span class="w"> </span><span class="nx">main</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="kn">import</span><span class="w"> </span><span class="p">(</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">	</span><span class="s">&#34;fmt&#34;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">	</span><span class="s">&#34;io&#34;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">	</span><span class="s">&#34;log&#34;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">	</span><span class="s">&#34;net/http&#34;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="p">)</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="kd">func</span><span class="w"> </span><span class="nf">queryHandler</span><span class="p">(</span><span class="nx">w</span><span class="w"> </span><span class="nx">http</span><span class="p">.</span><span class="nx">ResponseWriter</span><span class="p">,</span><span class="w"> </span><span class="nx">r</span><span class="w"> </span><span class="o">*</span><span class="nx">http</span><span class="p">.</span><span class="nx">Request</span><span class="p">)</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">	</span><span class="c1">// The new ServeMux handles method checking based on registration.</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">	</span><span class="nx">body</span><span class="p">,</span><span class="w"> </span><span class="nx">err</span><span class="w"> </span><span class="o">:=</span><span class="w"> </span><span class="nx">io</span><span class="p">.</span><span class="nf">ReadAll</span><span class="p">(</span><span class="nx">r</span><span class="p">.</span><span class="nx">Body</span><span class="p">)</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">	</span><span class="k">if</span><span class="w"> </span><span class="nx">err</span><span class="w"> </span><span class="o">!=</span><span class="w"> </span><span class="kc">nil</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">		</span><span class="nx">http</span><span class="p">.</span><span class="nf">Error</span><span class="p">(</span><span class="nx">w</span><span class="p">,</span><span class="w"> </span><span class="s">&#34;Error reading request body&#34;</span><span class="p">,</span><span class="w"> </span><span class="nx">http</span><span class="p">.</span><span class="nx">StatusInternalServerError</span><span class="p">)</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">		</span><span class="k">return</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">	</span><span class="p">}</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">	</span><span class="k">defer</span><span class="w"> </span><span class="nx">r</span><span class="p">.</span><span class="nx">Body</span><span class="p">.</span><span class="nf">Close</span><span class="p">()</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">	</span><span class="c1">// In a real application, you&#39;d parse the query from the body</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">	</span><span class="c1">// (e.g., JSON) and fetch data accordingly.</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">	</span><span class="nx">fmt</span><span class="p">.</span><span class="nf">Fprintf</span><span class="p">(</span><span class="nx">w</span><span class="p">,</span><span class="w"> </span><span class="s">&#34;Received your QUERY request with body: %s\n&#34;</span><span class="p">,</span><span class="w"> </span><span class="nb">string</span><span class="p">(</span><span class="nx">body</span><span class="p">))</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">	</span><span class="nx">log</span><span class="p">.</span><span class="nf">Printf</span><span class="p">(</span><span class="s">&#34;Handled QUERY request with body: %s&#34;</span><span class="p">,</span><span class="w"> </span><span class="nb">string</span><span class="p">(</span><span class="nx">body</span><span class="p">))</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="p">}</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="kd">func</span><span class="w"> </span><span class="nf">main</span><span class="p">()</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">	</span><span class="nx">mux</span><span class="w"> </span><span class="o">:=</span><span class="w"> </span><span class="nx">http</span><span class="p">.</span><span class="nf">NewServeMux</span><span class="p">()</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">	</span><span class="c1">// Register handler specifically for QUERY method on /data path</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">	</span><span class="nx">mux</span><span class="p">.</span><span class="nf">HandleFunc</span><span class="p">(</span><span class="s">&#34;QUERY /data&#34;</span><span class="p">,</span><span class="w"> </span><span class="nx">queryHandler</span><span class="p">)</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">	</span><span class="nx">log</span><span class="p">.</span><span class="nf">Println</span><span class="p">(</span><span class="s">&#34;Server starting on port 8080, handling custom QUERY method...&#34;</span><span class="p">)</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">	</span><span class="k">if</span><span class="w"> </span><span class="nx">err</span><span class="w"> </span><span class="o">:=</span><span class="w"> </span><span class="nx">http</span><span class="p">.</span><span class="nf">ListenAndServe</span><span class="p">(</span><span class="s">&#34;:8080&#34;</span><span class="p">,</span><span class="w"> </span><span class="nx">mux</span><span class="p">);</span><span class="w"> </span><span class="nx">err</span><span class="w"> </span><span class="o">!=</span><span class="w"> </span><span class="kc">nil</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">		</span><span class="nx">log</span><span class="p">.</span><span class="nf">Fatal</span><span class="p">(</span><span class="nx">err</span><span class="p">)</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">	</span><span class="p">}</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="p">}</span><span class="w">
</span></span></span></code></pre></div><p>In this server:</p>
<ol>
<li>We use <code>mux.HandleFunc(&quot;QUERY /data&quot;, queryHandler)</code> to directly associate the <code>queryHandler</code> with our custom <strong>QUERY</strong> HTTP method for the <code>/data</code> path.</li>
<li>The <code>queryHandler</code> reads the request body, where the complex query parameters would reside.</li>
</ol>
<p>Congratulations, we&rsquo;ve made an API that uses the QUERY method. Now let&rsquo;s write a client to pair with this server.</p>
<h3 id="client-side-also-with-go">Client-Side, also with Go</h3>
<p>Here&rsquo;s how a Go client can send a <strong>QUERY</strong> request:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="cl"><span class="kn">package</span><span class="w"> </span><span class="nx">main</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="kn">import</span><span class="w"> </span><span class="p">(</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">	</span><span class="s">&#34;fmt&#34;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">	</span><span class="s">&#34;io&#34;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">	</span><span class="s">&#34;log&#34;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">	</span><span class="s">&#34;net/http&#34;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">	</span><span class="s">&#34;strings&#34;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="p">)</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="kd">func</span><span class="w"> </span><span class="nf">main</span><span class="p">()</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">	</span><span class="nx">queryPayload</span><span class="w"> </span><span class="o">:=</span><span class="w"> </span><span class="s">`{&#34;filters&#34;: {&#34;status&#34;: &#34;active&#34;, &#34;category&#34;: &#34;electronics&#34;}, &#34;fields&#34;: [&#34;name&#34;, &#34;price&#34;]}`</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">	</span><span class="nx">client</span><span class="w"> </span><span class="o">:=</span><span class="w"> </span><span class="o">&amp;</span><span class="nx">http</span><span class="p">.</span><span class="nx">Client</span><span class="p">{}</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">	</span><span class="c1">// Create a new request with the custom &#34;QUERY&#34; method</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">	</span><span class="nx">req</span><span class="p">,</span><span class="w"> </span><span class="nx">err</span><span class="w"> </span><span class="o">:=</span><span class="w"> </span><span class="nx">http</span><span class="p">.</span><span class="nf">NewRequest</span><span class="p">(</span><span class="s">&#34;QUERY&#34;</span><span class="p">,</span><span class="w"> </span><span class="s">&#34;http://localhost:8080/data&#34;</span><span class="p">,</span><span class="w"> </span><span class="nx">strings</span><span class="p">.</span><span class="nf">NewReader</span><span class="p">(</span><span class="nx">queryPayload</span><span class="p">))</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">	</span><span class="k">if</span><span class="w"> </span><span class="nx">err</span><span class="w"> </span><span class="o">!=</span><span class="w"> </span><span class="kc">nil</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">		</span><span class="nx">log</span><span class="p">.</span><span class="nf">Fatalf</span><span class="p">(</span><span class="s">&#34;Error creating request: %v&#34;</span><span class="p">,</span><span class="w"> </span><span class="nx">err</span><span class="p">)</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">	</span><span class="p">}</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">	</span><span class="nx">req</span><span class="p">.</span><span class="nx">Header</span><span class="p">.</span><span class="nf">Set</span><span class="p">(</span><span class="s">&#34;Content-Type&#34;</span><span class="p">,</span><span class="w"> </span><span class="s">&#34;application/json&#34;</span><span class="p">)</span><span class="w"> </span><span class="c1">// Important for body processing</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">	</span><span class="nx">resp</span><span class="p">,</span><span class="w"> </span><span class="nx">err</span><span class="w"> </span><span class="o">:=</span><span class="w"> </span><span class="nx">client</span><span class="p">.</span><span class="nf">Do</span><span class="p">(</span><span class="nx">req</span><span class="p">)</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">	</span><span class="k">if</span><span class="w"> </span><span class="nx">err</span><span class="w"> </span><span class="o">!=</span><span class="w"> </span><span class="kc">nil</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">		</span><span class="nx">log</span><span class="p">.</span><span class="nf">Fatalf</span><span class="p">(</span><span class="s">&#34;Error sending request: %v&#34;</span><span class="p">,</span><span class="w"> </span><span class="nx">err</span><span class="p">)</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">	</span><span class="p">}</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">	</span><span class="k">defer</span><span class="w"> </span><span class="nx">resp</span><span class="p">.</span><span class="nx">Body</span><span class="p">.</span><span class="nf">Close</span><span class="p">()</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">	</span><span class="nx">fmt</span><span class="p">.</span><span class="nf">Println</span><span class="p">(</span><span class="s">&#34;Response Status:&#34;</span><span class="p">,</span><span class="w"> </span><span class="nx">resp</span><span class="p">.</span><span class="nx">Status</span><span class="p">)</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">	</span><span class="nx">body</span><span class="p">,</span><span class="w"> </span><span class="nx">err</span><span class="w"> </span><span class="o">:=</span><span class="w"> </span><span class="nx">io</span><span class="p">.</span><span class="nf">ReadAll</span><span class="p">(</span><span class="nx">resp</span><span class="p">.</span><span class="nx">Body</span><span class="p">)</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">	</span><span class="k">if</span><span class="w"> </span><span class="nx">err</span><span class="w"> </span><span class="o">!=</span><span class="w"> </span><span class="kc">nil</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">		</span><span class="nx">log</span><span class="p">.</span><span class="nf">Fatalf</span><span class="p">(</span><span class="s">&#34;Error reading response body: %v&#34;</span><span class="p">,</span><span class="w"> </span><span class="nx">err</span><span class="p">)</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">	</span><span class="p">}</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">	</span><span class="nx">fmt</span><span class="p">.</span><span class="nf">Println</span><span class="p">(</span><span class="s">&#34;Response Body:&#34;</span><span class="p">,</span><span class="w"> </span><span class="nb">string</span><span class="p">(</span><span class="nx">body</span><span class="p">))</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="p">}</span><span class="w">
</span></span></span></code></pre></div><p>The client uses <code>http.NewRequest(&quot;QUERY&quot;, ...)</code> to specify the custom method and sends the <code>queryPayload</code> in the request body.</p>
<h3 id="client-side-with-curl">Client-Side with cURL</h3>
<p>Although web browsers won&rsquo;t randomly make QUERY calls, many other tools can use QUERY. Many also support arbitrary HTTP verbs, although this isn&rsquo;t typically leveraged due to filters from load balancers, firewalls, etc.</p>
<p>Anyway, here&rsquo;s what that request looks like with cURL:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-shell" data-lang="shell"><span class="line"><span class="cl">$ curl -X QUERY -d <span class="s1">&#39;{&#34;filters&#34;: {&#34;status&#34;: &#34;active&#34;, &#34;category&#34;: &#34;electronics&#34;}, &#34;
</span></span></span><span class="line"><span class="cl"><span class="s1">fields&#34;: [&#34;name&#34;, &#34;price&#34;]}&#39;</span> http://localhost:8080/data -v
</span></span><span class="line"><span class="cl">* Host localhost:8080 was resolved.
</span></span><span class="line"><span class="cl">* IPv6: ::1
</span></span><span class="line"><span class="cl">* IPv4: 127.0.0.1
</span></span><span class="line"><span class="cl">*   Trying <span class="o">[</span>::1<span class="o">]</span>:8080...
</span></span><span class="line"><span class="cl">* Connected to localhost <span class="o">(</span>::1<span class="o">)</span> port <span class="m">8080</span>
</span></span><span class="line"><span class="cl">&gt; QUERY /data HTTP/1.1
</span></span><span class="line"><span class="cl">&gt; Host: localhost:8080
</span></span><span class="line"><span class="cl">&gt; User-Agent: curl/8.7.1
</span></span><span class="line"><span class="cl">&gt; Accept: */*
</span></span><span class="line"><span class="cl">&gt; Content-Length: <span class="m">89</span>
</span></span><span class="line"><span class="cl">&gt; Content-Type: application/x-www-form-urlencoded
</span></span><span class="line"><span class="cl">&gt; 
</span></span><span class="line"><span class="cl">* upload completely sent off: <span class="m">89</span> bytes
</span></span><span class="line"><span class="cl">&lt; HTTP/1.1 <span class="m">200</span> OK
</span></span><span class="line"><span class="cl">&lt; Date: Sat, <span class="m">31</span> May <span class="m">2025</span> 15:47:45 GMT
</span></span><span class="line"><span class="cl">&lt; Content-Length: <span class="m">129</span>
</span></span><span class="line"><span class="cl">&lt; Content-Type: text/plain<span class="p">;</span> <span class="nv">charset</span><span class="o">=</span>utf-8
</span></span><span class="line"><span class="cl">&lt; 
</span></span><span class="line"><span class="cl">Received your QUERY request with body: <span class="o">{</span><span class="s2">&#34;filters&#34;</span>: <span class="o">{</span><span class="s2">&#34;status&#34;</span>: <span class="s2">&#34;active&#34;</span>, <span class="s2">&#34;category&#34;</span>: <span class="s2">&#34;electronics&#34;</span><span class="o">}</span>, <span class="s2">&#34;fields&#34;</span>: <span class="o">[</span><span class="s2">&#34;name&#34;</span>, <span class="s2">&#34;price&#34;</span><span class="o">]}</span>
</span></span><span class="line"><span class="cl">* Connection <span class="c1">#0 to host localhost left intact</span>
</span></span></code></pre></div><h3 id="query-vs-get-and-post---a-clearer-separation">QUERY vs. GET and POST - A Clearer Separation</h3>
<p>Let&rsquo;s summarize the distinctions with our (potentially future-standard) QUERY method in mind:</p>
<ul>
<li><strong>GET</strong>: Used for retrieving data. Parameters are typically sent in the URL. GET requests <em>must</em> be safe and idempotent. They generally don&rsquo;t have a body.</li>
<li><strong>POST</strong>: 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.</li>
<li><strong>QUERY (custom/proposed)</strong>: Used to <em>request data</em> (like GET) but with the ability to send a <em>complex query in the request body</em> (like POST). Crucially, it is defined as <strong>safe and idempotent</strong> (like GET). This explicitly tells intermediaries and clients that the request has no side effects and can be cached or retried automatically.</li>
</ul>
<h3 id="caching-behavior-get-post-and-query">Caching Behavior: GET, POST, and QUERY</h3>
<p>HTTP caching is vital for performance. A method&rsquo;s characteristics (especially safety and idempotency) directly influence its cacheability.</p>
<h4 id="1-get">1. GET</h4>
<ul>
<li><strong>Behavior</strong>: GET requests are inherently cacheable. Caches readily store and serve responses to GET requests if caching headers (like <code>Cache-Control</code>, <code>Expires</code>, <code>ETag</code>) allow. This is a foundational aspect of HTTP performance.</li>
</ul>
<h4 id="2-post">2. POST</h4>
<ul>
<li><strong>Behavior</strong>: POST requests are generally <em>not</em> cacheable by default. Since POST can have side effects, caching responses could lead to unintended consequences or stale data if the action isn&rsquo;t repeated.</li>
</ul>
<h4 id="3-query">3. QUERY</h4>
<p>The IETF draft emphasizes that a method like QUERY is &ldquo;explicitly safe and idempotent, allowing functions like caching and automatic retries to operate.&rdquo; This is key.</p>
<ul>
<li><strong>Behavior (Current Custom Method)</strong>: Because our <strong>QUERY</strong> method is non-standard <em>today</em>, caches will <strong>not cache it by default</strong>. They typically only automatically consider standard safe methods like GET.
To make responses to a custom QUERY request cacheable:
<ul>
<li>The <strong>server must send explicit caching headers</strong> (e.g., <code>Cache-Control: public, max-age=3600</code>). These headers signal the cacheability of the response.</li>
<li>Intermediary caches (CDNs, reverse proxies) might need <strong>specific configuration</strong> to recognize and cache responses for this custom method.</li>
<li>The <code>Vary</code> HTTP header is important if the response depends on the request body content.</li>
</ul>
</li>
<li><strong>Behavior (Future Standardized Method)</strong>: If a method like QUERY becomes a recognized HTTP standard, caches would likely treat it similarly to GET for caching purposes, provided it&rsquo;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.</li>
</ul>
<h3 id="key-takeaway-for-query-caching">Key Takeaway for QUERY Caching</h3>
<p>The <em>intent</em> 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.</p>
<h2 id="whats-next">What&rsquo;s Next?</h2>
<p>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.</p>
<p>Some projects have already taken to adding support for the QUERY method. Take a look at <a href="https://github.com/nodejs/node/issues/51562" rel="external">NodeJS: Support for &lsquo;QUERY&rsquo; method</a>, which added support last year. As demonstrated earlier in this article, using <code>QUERY</code> 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.</p>
<p>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&rsquo;s actually steady changes being made to the draft document. You can tell this from the <a href="https://github.com/httpwg/http-extensions/commits/main/draft-ietf-httpbis-safe-method-w-body.xml" rel="external">git history</a> on the httpwg&rsquo;s repo. At the time of writing May 19th was when the last change was made, so I&rsquo;m certain that this hasn&rsquo;t been forgotten about.</p>
]]></content:encoded></item><item><title>FauxRPC and Protovalidate</title><link>https://kmcd.dev/posts/fauxrpc-protovalidate/</link><pubDate>Tue, 12 Nov 2024 10:00:00 +0000</pubDate><guid>https://kmcd.dev/posts/fauxrpc-protovalidate/</guid><description> 
                &lt;p> &lt;img hspace="5" src="https://kmcd.dev/posts/fauxrpc-protovalidate/cover_hu_cc063547c579daa5.webp" /> &lt;/p>
                
                
                </description><content:encoded><![CDATA[<p><a href="https://fauxrpc.com/" rel="external">FauxRPC</a>, a tool for generating fake gRPC servers, now integrates with <a href="https://github.com/bufbuild/protovalidate" rel="external">protovalidate</a>, which lets you define validation rules in your Protobuf definitions. Now every request processed by FauxRPC will be automatically validated against your protovalidate rules. Not only will you get high quality data validation in your application, but now you can have the same validation before you even write your application logic! Let&rsquo;s walk through how this new feature works.</p>
<h2 id="how-it-works">How it works</h2>
<p>First, define your validation rules using protovalidate&rsquo;s constraint annotations within your Protobuf definitions. For guidance, you should reference <a href="https://github.com/bufbuild/protovalidate/blob/main/docs/README.md" rel="external">the protovalidate documentation</a>. Fauxrpc will take care of the rest, automatically validating each request against these rules. If a request fails validation, a detailed error message will be returned, guiding you toward quickly fixing the issue.</p>
<p>For this example, we&rsquo;re going to use a simple service where you can call with a name and it will return a greeting. Here&rsquo;s what that would look like in protobuf:</p>
<p><strong>greet.proto</strong></p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-protobuf" data-lang="protobuf"><span class="line"><span class="cl"><span class="n">syntax</span> <span class="o">=</span> <span class="s">&#34;proto3&#34;</span><span class="p">;</span><span class="err">
</span></span></span><span class="line"><span class="cl"><span class="err">
</span></span></span><span class="line"><span class="cl"><span class="err"></span><span class="kn">package</span> <span class="nn">greet</span><span class="o">.</span><span class="n">v1</span><span class="p">;</span><span class="err">
</span></span></span><span class="line"><span class="cl"><span class="err">
</span></span></span><span class="line"><span class="cl"><span class="err"></span><span class="k">import</span> <span class="s">&#34;buf/validate/validate.proto&#34;</span><span class="p">;</span><span class="err">
</span></span></span><span class="line"><span class="cl"><span class="err">
</span></span></span><span class="line"><span class="cl"><span class="err"></span><span class="kd">message</span> <span class="nc">GreetRequest</span> <span class="p">{</span><span class="err">
</span></span></span><span class="line"><span class="cl"><span class="err"></span>  <span class="kt">string</span> <span class="n">name</span> <span class="o">=</span> <span class="mi">1</span> <span class="p">[(</span><span class="n">buf.validate.field</span><span class="p">)</span><span class="o">.</span><span class="kt">string</span> <span class="o">=</span> <span class="p">{</span><span class="n">min_len</span><span class="o">:</span> <span class="mi">3</span><span class="p">,</span> <span class="n">max_len</span><span class="o">:</span> <span class="mi">20</span><span class="p">}];</span><span class="err">
</span></span></span><span class="line"><span class="cl"><span class="err"></span><span class="p">}</span><span class="err">
</span></span></span><span class="line"><span class="cl"><span class="err">
</span></span></span><span class="line"><span class="cl"><span class="err"></span><span class="kd">message</span> <span class="nc">GreetResponse</span> <span class="p">{</span><span class="err">
</span></span></span><span class="line"><span class="cl"><span class="err"></span>  <span class="kt">string</span> <span class="n">greeting</span> <span class="o">=</span> <span class="mi">1</span> <span class="p">[(</span><span class="n">buf.validate.field</span><span class="p">)</span><span class="o">.</span><span class="kt">string</span><span class="o">.</span><span class="n">example</span> <span class="o">=</span> <span class="s">&#34;Hello, user!&#34;</span><span class="p">];</span><span class="err">
</span></span></span><span class="line"><span class="cl"><span class="err"></span><span class="p">}</span><span class="err">
</span></span></span><span class="line"><span class="cl"><span class="err">
</span></span></span><span class="line"><span class="cl"><span class="err"></span><span class="kd">service</span> <span class="n">GreetService</span> <span class="p">{</span><span class="err">
</span></span></span><span class="line"><span class="cl"><span class="err"></span>  <span class="k">rpc</span> <span class="n">Greet</span><span class="p">(</span><span class="n">GreetRequest</span><span class="p">)</span> <span class="k">returns</span> <span class="p">(</span><span class="n">GreetResponse</span><span class="p">)</span> <span class="p">{}</span><span class="err">
</span></span></span><span class="line"><span class="cl"><span class="err"></span><span class="p">}</span><span class="err">
</span></span></span></code></pre></div><p>The <code>buf.validate.field</code> annotations are from protovalidate. The section <code>(buf.validate.field).string = {min_len: 3, max_len: 20}</code> ensures that the <code>name</code> field is between 3 and 20 characters. <code>(buf.validate.field).string.example = &quot;Hello, user!&quot;</code> is declaring what an example response might look like. This mostly serves as documentation but it also influences other tools, including <a href="https://github.com/sudorandom/protoc-gen-connect-openapi" rel="external">protoc-gen-connect-openapi</a> and, now, FauxRPC! We&rsquo;ll see how FauxRPC uses both of these constraints in a second. First, let&rsquo;s start up the FauxRPC server.</p>
<p>To use protovalidate with FauxRPC, we need to leverage another tool that&rsquo;s extremely helpful for working with protobuf definitions. This is because we used protovalidate, a dependency. Usually dependencies are hard to deal with in protobuf because the default protobuf tooling just doesn&rsquo;t manage dependencies at all. However, the <a href="https://buf.build/product/cli" rel="external">Buf CLI</a> and the <a href="https://buf.build/product/bsr" rel="external">BSR</a> can help us here.</p>
<p>First, create a new <code>buf.yaml</code> file in the same directory as <code>greet.proto</code> with the contents:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-yaml" data-lang="yaml"><span class="line"><span class="cl"><span class="nt">version</span><span class="p">:</span><span class="w"> </span><span class="l">v2</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="nt">deps</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span>- <span class="l">buf.build/bufbuild/protovalidate</span><span class="w">
</span></span></span></code></pre></div><p>Now we&rsquo;re just a few commands away from having a mock service running:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-shell" data-lang="shell"><span class="line"><span class="cl"><span class="c1"># Get Buf CLI to pull our new dependency</span>
</span></span><span class="line"><span class="cl">$ buf dep update
</span></span><span class="line"><span class="cl"><span class="c1"># Build our protobuf file (and dependencies) into a protobuf &#34;image&#34;: https://buf.build/docs/build/overview/</span>
</span></span><span class="line"><span class="cl">$ buf build . -o greet.binpb
</span></span><span class="line"><span class="cl"><span class="c1"># Start FauxRPC with this image</span>
</span></span><span class="line"><span class="cl">$ fauxrpc run --schema<span class="o">=</span>greet.binpb
</span></span></code></pre></div><p>Now let&rsquo;s try some requests against this new service:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-shell" data-lang="shell"><span class="line"><span class="cl">$ buf curl --http2-prior-knowledge -d <span class="s1">&#39;{}&#39;</span> http://127.0.0.1:6660/greet.v1.GreetService/Greet
</span></span><span class="line"><span class="cl"><span class="o">{</span>
</span></span><span class="line"><span class="cl">   <span class="s2">&#34;code&#34;</span>: <span class="s2">&#34;invalid_argument&#34;</span>,
</span></span><span class="line"><span class="cl">   <span class="s2">&#34;message&#34;</span>: <span class="s2">&#34;validation error:\n - name: value length must be at least 3 characters [string.min_len]&#34;</span>,
</span></span><span class="line"><span class="cl">   <span class="s2">&#34;details&#34;</span>: <span class="o">[</span>
</span></span><span class="line"><span class="cl">      <span class="o">{</span>
</span></span><span class="line"><span class="cl">         <span class="s2">&#34;type&#34;</span>: <span class="s2">&#34;buf.validate.Violations&#34;</span>,
</span></span><span class="line"><span class="cl">         <span class="s2">&#34;value&#34;</span>: <span class="s2">&#34;CkIKBG5hbWUSDnN0cmluZy5taW5fbGVuGip2YWx1ZSBsZW5ndGggbXVzdCBiZSBhdCBsZWFzdCAzIGNoYXJhY3RlcnM&#34;</span>,
</span></span><span class="line"><span class="cl">         <span class="s2">&#34;debug&#34;</span>: <span class="o">{</span>
</span></span><span class="line"><span class="cl">            <span class="s2">&#34;violations&#34;</span>: <span class="o">[</span>
</span></span><span class="line"><span class="cl">               <span class="o">{</span>
</span></span><span class="line"><span class="cl">                  <span class="s2">&#34;fieldPath&#34;</span>: <span class="s2">&#34;name&#34;</span>,
</span></span><span class="line"><span class="cl">                  <span class="s2">&#34;constraintId&#34;</span>: <span class="s2">&#34;string.min_len&#34;</span>,
</span></span><span class="line"><span class="cl">                  <span class="s2">&#34;message&#34;</span>: <span class="s2">&#34;value length must be at least 3 characters&#34;</span>
</span></span><span class="line"><span class="cl">               <span class="o">}</span>
</span></span><span class="line"><span class="cl">            <span class="o">]</span>
</span></span><span class="line"><span class="cl">         <span class="o">}</span>
</span></span><span class="line"><span class="cl">      <span class="o">}</span>
</span></span><span class="line"><span class="cl">   <span class="o">]</span>
</span></span><span class="line"><span class="cl"><span class="o">}</span>
</span></span></code></pre></div><p>Oh, duh! We hit the length constraint we added for the <code>name</code> field, so our empty object <code>{}</code> isn&rsquo;t good enough anymore. We need a name and it needs to have between 3 and 20 characters. Let&rsquo;s try once more:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-shell" data-lang="shell"><span class="line"><span class="cl">$ buf curl --http2-prior-knowledge -d <span class="s1">&#39;{&#34;name&#34;: &#34;Bob&#34;}&#39;</span> http://127.0.0.1:6660/greet.v1.GreetService/Greet
</span></span><span class="line"><span class="cl"><span class="o">{</span>
</span></span><span class="line"><span class="cl">  <span class="s2">&#34;greeting&#34;</span>: <span class="s2">&#34;Hello, user!&#34;</span>
</span></span><span class="line"><span class="cl"><span class="o">}</span>
</span></span></code></pre></div><p>Great, we received a response! And the response is populated using our <code>(buf.validate.field).string.example</code> annotation. This will allow us to stand up a fake service with a few simple commands that has request validation built in and will use the constraints to make realistic fake data.</p>
<p>This shows how protovalidate uses protovalidate constraints both for input validation and fake data generation.</p>
<p>On a related note, I am personally excited to eventually have <a href="https://github.com/bufbuild/protovalidate/issues/67" rel="external">Typescript Support for protovalidate</a>. That would allow us to use protovalidate for both clean frontend UX and robust server side validation. In my eyes, this would completely solve the problem of having duplicate and inconsistent validation rules between the frontend and backend. Backend needs these rules to ensure system reliability and consistency. Frontend needs these rules for better UX.</p>
<h2 id="benefits-for-you">Benefits for you</h2>
<p>FauxRPC and protobuf synergizes well with the model-driven API design with protobuf. From a single API definition you have strongly typed definitions that has support for many programming languages, powerful validation constraints, examples of what each field looks like. And FauxRPC lets you experiment with all of that before writing a single line of application code. This means:</p>
<ul>
<li><strong>Reduced development time:</strong> Spend less time debugging data issues and more time building amazing features.</li>
<li><strong>Increased confidence:</strong> Trust that your RPCs are handling data correctly, leading to more stable and reliable applications.</li>
<li><strong>Improved collaboration:</strong> Make it easier for teams to work together by ensuring everyone adheres to the same data standards.</li>
</ul>
<h2 id="ready-to-give-it-a-try">Ready to give it a try?</h2>
<p>Ready to experience the power of FauxRPC and protovalidate?</p>
<ul>
<li>Update to the latest version of FauxRPC: <a href="https://github.com/sudorandom/fauxrpc/releases/latest" rel="external">github.com/sudorandom/fauxrpc/releases/latest</a></li>
<li>Learn more about protovalidate: <a href="https://github.com/bufbuild/protovalidate" rel="external">github.com/bufbuild/protovalidate</a></li>
<li>Explore the FauxRPC documentation: <a href="https://fauxrpc.com/" rel="external">fauxrpc.com</a></li>
</ul>
<p>For reference, all of the code in the article <a href="https://github.com/sudorandom/kmcd.dev/tree/main/content/posts/2024/fauxrpc-protovalidate/proto" rel="external">is available here</a>.</p>
]]></content:encoded></item><item><title>gRPC: The Ugly Parts</title><link>https://kmcd.dev/posts/grpc-the-ugly-parts/</link><pubDate>Tue, 03 Sep 2024 00:00:00 +0000</pubDate><guid>https://kmcd.dev/posts/grpc-the-ugly-parts/</guid><description> 
                &lt;p> &lt;img hspace="5" src="https://kmcd.dev/posts/grpc-the-ugly-parts/cover_hu_3d7170590a3ed024.webp" /> &lt;/p>
                
                The seedy underbelly of gRPC.
                </description><content:encoded><![CDATA[<p>gRPC has undeniably become a powerful tool in the world of microservices, offering efficiency and performance benefits, but gRPC also has an ugly side. As someone who&rsquo;s spent a considerable amount of time with gRPC, I&rsquo;d like to shed light on some of the uglier aspects of this technology. I&rsquo;ve already talked about the <a href="https://kmcd.dev/posts/grpc-the-good-parts">good</a> and <a href="https://kmcd.dev/posts/grpc-the-bad-parts">bad</a> parts of gRPC, now let&rsquo;s talk about the ugly.</p>
<h2 id="generated-code">Generated Code</h2>
<p>To get started, I have to talk about how ugly the code generated from protobuf definitions is. It has historically been verbose, complex, and difficult to navigate. Even though it&rsquo;s not meant to be hand-edited, this can impact code readability and maintainability, especially when integrating gRPC into larger projects. This has actually improved a lot recently in most languages but even so, there are some rough edges.</p>
<h3 id="language-specific-quirks">Language-specific Quirks</h3>
<p>Protobuf and gRPC&rsquo;s initial implementations often diverged from language-specific norms, especially in their HTTP handling. This stemmed partly from the decision to mandate HTTP/2 support, a decision that has since proven to limit gRPC&rsquo;s reach into the web frontend. We know now from gRPC-Web that trailers aren&rsquo;t a hard requirement for a protocol like gRPC. In the aftermath of this decision, we are now left with a need to evolve the language implementations of protobuf and gRPC to be more idiomatic for each language.</p>
<p>For Go, avoiding the <code>net/http</code> package is a rough decision because it makes it harder to use gRPC endpoints alongside other kinds of HTTP APIs and to re-use HTTP middleware. They eventually added a <a href="https://pkg.go.dev/google.golang.org/grpc#Server.ServeHTTP" rel="external"><code>ServeHTTP()</code></a> interface to grpc-go as an experimental way to use the HTTP server from the Go standard library but using that method results in <a href="https://kmcd.dev/posts/benchmarking-go-grpc/">a significant loss of performance</a>. Maybe they did it for performance reasons? If so, it&rsquo;s definitely a tradeoff that has split gRPC from the rest of the Go ecosystem.</p>
<p>Sometimes language quirks actually impact how you design protobuf types. If you follow the style recommendations from <a href="https://buf.build/docs/best-practices/style-guide" rel="external">Buf</a>, the names of enums are expected to be prefixed an upper-snake-case version of the enum name, like so:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-protobuf" data-lang="protobuf"><span class="line"><span class="cl"><span class="kd">enum</span> <span class="n">FooBar</span> <span class="p">{</span><span class="err">
</span></span></span><span class="line"><span class="cl"><span class="err"></span>  <span class="n">FOO_BAR_UNSPECIFIED</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span><span class="err">
</span></span></span><span class="line"><span class="cl"><span class="err"></span>  <span class="n">FOO_BAR_FIRST_VALUE</span> <span class="o">=</span> <span class="mi">1</span><span class="p">;</span><span class="err">
</span></span></span><span class="line"><span class="cl"><span class="err"></span>  <span class="n">FOO_BAR_SECOND_VALUE</span> <span class="o">=</span> <span class="mi">2</span><span class="p">;</span><span class="err">
</span></span></span><span class="line"><span class="cl"><span class="err"></span><span class="p">}</span><span class="err">
</span></span></span></code></pre></div><p>This is described better in the <a href="https://buf.build/docs/lint/rules#enum_value_prefix" rel="external">buf lint rule description</a> for <code>ENUM_VALUE_PREFIX</code> but the style guide is like this because of C++ scoping rules with enums, which makes it impossible to have two enum values in the same package with the same enum value name. While this convention originated from C++ scoping rules, it affects how you should design all protobuf files. Why would scoping inside of the enum not be enough for the C++ compiler to generate unique names? Why is this flaw something that impacts the style guide and, in effect, all target languages? To me, this is kind of ugly, because quirks of some language implementations are bubbling up in unintuitive ways.</p>
<h3 id="the-generated-code-isnt-even-that-fast">The generated code isn&rsquo;t even that fast</h3>
<p>One benefit of generated code is that you can generate code that no sane human would write in order to get some performance optimizations. However, if you look at some of the code generated from protobuf you&rsquo;ll see runtime reflection used a lot. Why? In a way, I am saying the generated code <em>isn&rsquo;t ugly enough</em>. Let&rsquo;s look at a concrete example. Be warned that this will be a very Go-specific section because most of my experience with protobufs is in Go. However, the same strategy has been applied in most languages.</p>
<p>Let&rsquo;s take a look at super a simple example in Go. Here&rsquo;s the protobuf:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-protobuf" data-lang="protobuf"><span class="line"><span class="cl"><span class="kd">message</span> <span class="nc">Hello</span> <span class="p">{</span><span class="err">
</span></span></span><span class="line"><span class="cl"><span class="err"></span>  <span class="kt">string</span> <span class="n">name</span> <span class="o">=</span> <span class="mi">1</span><span class="p">;</span><span class="err">
</span></span></span><span class="line"><span class="cl"><span class="err"></span><span class="p">}</span><span class="err">
</span></span></span></code></pre></div><p>Here&rsquo;s the type generated by protoc:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="cl"><span class="kd">type</span><span class="w"> </span><span class="nx">Hello</span><span class="w"> </span><span class="kd">struct</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">	</span><span class="nx">state</span><span class="w">         </span><span class="nx">protoimpl</span><span class="p">.</span><span class="nx">MessageState</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">	</span><span class="nx">sizeCache</span><span class="w">     </span><span class="nx">protoimpl</span><span class="p">.</span><span class="nx">SizeCache</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">	</span><span class="nx">unknownFields</span><span class="w"> </span><span class="nx">protoimpl</span><span class="p">.</span><span class="nx">UnknownFields</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">	</span><span class="nx">Name</span><span class="w"> </span><span class="kt">string</span><span class="w"> </span><span class="s">`protobuf:&#34;bytes,1,opt,name=name,proto3&#34; json:&#34;name,omitempty&#34;`</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="p">}</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="c1">// With these methods, contents are stripped</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="kd">func</span><span class="w"> </span><span class="p">(</span><span class="o">*</span><span class="nx">Hello</span><span class="p">)</span><span class="w"> </span><span class="nf">Reset</span><span class="p">()</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="kd">func</span><span class="w"> </span><span class="p">(</span><span class="o">*</span><span class="nx">Hello</span><span class="p">)</span><span class="w"> </span><span class="nf">String</span><span class="p">()</span><span class="w"> </span><span class="kt">string</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="kd">func</span><span class="w"> </span><span class="p">(</span><span class="o">*</span><span class="nx">Hello</span><span class="p">)</span><span class="w"> </span><span class="nf">ProtoMessage</span><span class="p">()</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="kd">func</span><span class="w"> </span><span class="p">(</span><span class="o">*</span><span class="nx">Hello</span><span class="p">)</span><span class="w"> </span><span class="nf">ProtoReflect</span><span class="p">()</span><span class="w"> </span><span class="nx">protoreflect</span><span class="p">.</span><span class="nx">Message</span><span class="w">
</span></span></span></code></pre></div><p>There&rsquo;s actually no <code>Marshal()</code> or <code>Unmarshal()</code> functions defined specifically for this type. This means that runtime reflection is used to make serialization work. Reflection is generally seen as slower, because it <em>is</em> slower. I find it strange that optimized, type-specific serialization code isn&rsquo;t being generated for Go. That said, you can actually get this by using a separate protoc plugin called <a href="https://github.com/planetscale/vtprotobuf" rel="external">vtprotobuf</a> that will generate specialized marshal and unmarshal functions for each protobuf type. It also allows for using type-specific memory pools, which can also help reduce allocations and improve performance. From my <a href="https://kmcd.dev/posts/benchmarking-go-grpc/">own testing</a> just adding <code>vtprotobuf</code> with zero code changes can improve performance by 2-4%. This is essentially a &ldquo;free&rdquo; 2-4%, so it&rsquo;s super strange to me that this wouldn&rsquo;t be part of the standard compiler. <a href="https://github.com/sudorandom/go-grpc-bench/blob/v0.0.1/gen/flex_vtproto.pb.go#L573" rel="external">You may not like it, but this is what peak performance looks like</a>. Anyway, this project needs more love and support.</p>
<p>Note that there are <a href="https://medium.com/@octopus.dev/gremlin-77af6fee4193" rel="external">other efforts which claim outrageous improvements</a> over what the standard protobuf library does. They do make tradeoffs to achieve these performance gains, but many times the extra complexity is worth it.</p>
<p>You might have read this section and thought &ldquo;well, this would increase the amount of code being generated and increase binary or package sizes and in some environments, you might not want that. That&rsquo;s true amd that&rsquo;s why protobuf has an <code>optimize_for</code> option, so you can annotate one of the following:</p>
<ul>
<li><code>option optimize_for = SPEED;</code> - more verbose, faster code</li>
<li><code>option optimize_for = CODE_SIZE;</code> - smaller code</li>
<li><code>option optimize_for = LITE_RUNTIME;</code> - intended to run on a smaller runtime that omits features like descriptors and reflection.</li>
</ul>
<p>See the full description for optimize_for on the <a href="https://protobuf.dev/programming-guides/proto3/" rel="external">official protobuf documentation</a>. While these options exist, they aren&rsquo;t actually used for most target languages. In the future I would totally like to see most of <code>vtprotobuf</code> be rolled into the standard protobuf compiler for Go and be used if <code>optimize_for = SPEED</code>. Integrating <code>vtprotobuf</code>-like optimizations into the standard protobuf compiler could offer significant performance gains for Go and there are potentially similar opportunities in other languages as well.</p>
<h2 id="required-fields">Required Fields</h2>
<p>The maintainers of protobuf learned some hard lessons with required fields. They felt like they misstepped so badly, that they made a new version of protobuf, proto3, just to remove required fields from the spec. Why? The author of the &ldquo;Required considered harmful&rdquo; manifesto talks about this in a <a href="https://news.ycombinator.com/item?id=18190005" rel="external">lengthy hacker news comment</a>, but the important bit is:</p>
<blockquote>
<p>Real-world practice has also shown that quite often, fields that originally seemed to be &ldquo;required&rdquo; turn out to be optional over time, hence the &ldquo;required considered harmful&rdquo; manifesto. In practice, you want to declare all fields optional to give yourself maximum flexibility for change.</p>
</blockquote>
<p>This is <a href="https://protobuf.dev/programming-guides/dos-donts/#add-required" rel="external">echoed by the official style guide of protobufs</a>, where they recommend adding a comment indicating that a field is required. If we&rsquo;re talking about getting a message from A to B, I totally agree with this line of thinking. However, just because the fields that are considered &ldquo;required&rdquo; change over time doesn&rsquo;t mean required fields don&rsquo;t exist. There still needs to be code that enforces this requirement and I&rsquo;d rather not write this code, to be honest. Therefore, I think the best way of handling required fields without writing a bunch of null checks everywhere is by using <a href="https://github.com/bufbuild/protovalidate" rel="external">protovalidate</a> or a similar library that has protobuf options that allow you to annotate which fields are required. Then there is code on the server and/or client that can enforce these requirements using a library. In my opinion, this has the best of both worlds: you can still declare required fields in a way that doesn&rsquo;t completely break message integrity.</p>
<p>I don&rsquo;t like this:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-protobuf" data-lang="protobuf"><span class="line"><span class="cl"><span class="kd">message</span> <span class="nc">User</span> <span class="p">{</span><span class="err">
</span></span></span><span class="line"><span class="cl"><span class="err"></span>  <span class="kt">int32</span> <span class="n">age</span> <span class="o">=</span> <span class="mi">1</span><span class="p">;</span> <span class="c1">// required.
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="p">}</span><span class="err">
</span></span></span></code></pre></div><p>I do like this:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-protobuf" data-lang="protobuf"><span class="line"><span class="cl"><span class="kd">message</span> <span class="nc">User</span> <span class="p">{</span><span class="err">
</span></span></span><span class="line"><span class="cl"><span class="err"></span>  <span class="kt">int32</span> <span class="n">age</span> <span class="o">=</span> <span class="mi">1</span> <span class="p">[(</span><span class="n">buf.validate.field</span><span class="p">)</span><span class="o">.</span><span class="k">required</span> <span class="o">=</span> <span class="kc">true</span><span class="p">];</span><span class="err">
</span></span></span><span class="line"><span class="cl"><span class="err"></span><span class="p">}</span><span class="err">
</span></span></span></code></pre></div><p>I&rsquo;m a big fan of <a href="https://github.com/bufbuild/protovalidate" rel="external">protovalidate</a> and I&rsquo;ve used it a good amount and have contributed to it. Generally, I think <a href="https://protobuf.dev/programming-guides/proto3/#customoptions" rel="external">custom options</a> for protobuf fields is an untapped superpower of protobufs.</p>
<h2 id="failure-to-launch">Failure to Launch</h2>
<p>While gRPC has undeniable advantages, its learning curve can be steep. Getting started with protobuf, understanding the tooling, and setting up the necessary infrastructure can be intimidating for newcomers, making the initial adoption hurdle higher than with simpler JSON-based APIs. Why is it steep? Well, it introduces non-idiomatic tooling to most languages. There are some examples of language support that make protobuf generation seamless. <a href="https://learn.microsoft.com/en-us/aspnet/core/grpc/basics" rel="external">Grpc.Tools</a> for .NET is one shining example, showing how protobuf tooling can be more integrated into standard language tooling. We need more of this.</p>

    
    
        
        
            
        
        <img src="https://kmcd.dev/posts/grpc-the-ugly-parts/learning-curve_hu_c461c8a49bfd8bff.webp"
             alt="" class="center" width="600px"/>
    


<p>The steep learning curve doesn&rsquo;t help when many people who use and rely on protobuf and gRPC actively don&rsquo;t want gRPC to extend to the frontend and think that pushing in this direction will lead to uninformed people encroaching on the domain of the backend, where only they are smart enough to work. This is elitist gate-keeping and is unfortunately prevalent in this industry. I believe gRPC has as much of a place in web frontends as much as it does in microservices.</p>
<p>I&rsquo;ve learned a lot by helping others work with protobuf. You may see me on <a href="https://buf.build/links/slack" rel="external">Buf&rsquo;s slack channel</a> or on related discussions because I truly have gotten a lot out of it. Many article ideas have come directly from answering questions there. If I see a problem often enough, I may end up writing an article about it. I believe the protobuf and gRPC community needs more of this attitude.</p>
<p>I believe the steep learning curve (which can be helped with tooling), coupled with some resistance from backend developers (which can be helped by&hellip; having empathy?), has slowed its broader adoption in web development.</p>
<h2 id="grpc-has-a-history">gRPC Has a History</h2>
<p>gRPC&rsquo;s initial focus on microservices and its close ties to HTTP/2 hindered its widespread adoption in web development. Even with the <a href="https://grpc.io/blog/state-of-grpc-web/" rel="external">advent of gRPC-Web</a>, there&rsquo;s still a perception that it&rsquo;s not a first-class citizen in the frontend ecosystem. The lack of robust integration with popular frontend libraries like <a href="https://tanstack.com/query/latest" rel="external">TanStack Query</a> further solidifies this notion to me.</p>

    
    
        
        
            
        
        <img src="https://kmcd.dev/posts/grpc-the-ugly-parts/bad-blood_hu_692216ba0338ffdf.webp"
             alt="" class="center" width="800px"/>
    


<p>I think there&rsquo;s a real chance to get more frontend developers excited about gRPC with improved tooling. There&rsquo;s a giant industry-wide conversation happening right now around where the line between &ldquo;frontend&rdquo; and &ldquo;backend&rdquo; meet and I think no matter the outcome, we&rsquo;re going to see more typescript code using gRPC.</p>
<h2 id="the-g-in-grpc">The &ldquo;g&rdquo; in gRPC</h2>
<p>While <a href="https://grpc.io/docs/what-is-grpc/faq/#what-does-grpc-stand-for" rel="external">the gRPC project claims</a> that the &ldquo;g&rdquo; in gRPC is a <a href="https://en.wikipedia.org/wiki/Backronym" rel="external">backronym</a> that stands for &ldquo;gRPC&rdquo;, it originally stood for Google, because it was Google who developed and released both protobuf and gRPC.</p>

    
    
        
        
            
        
        <img src="https://kmcd.dev/posts/grpc-the-ugly-parts/google_hu_7e5494754266e479.webp"
             alt="" class="center" width="600px"/>
    


<p>There&rsquo;s always a lingering question about Google&rsquo;s long-term commitment to gRPC and protobuf. Will they continue to invest in these open-source projects, or could they pull the plug if priorities shift? Remember that Google has <a href="https://techcrunch.com/2024/05/01/google-lays-off-staff-from-flutter-dart-python-weeks-before-its-developer-conference/" rel="external">recently layed off much of the Flutter, Dart and Python teams</a>. The protobuf community is growing, but would it be self-sustaining enough to survive such a scenario?</p>
<h2 id="its-not-finished">It&rsquo;s Not Finished</h2>
<p>Others have said that gRPC is immature, not because of its age but by how developed the ecosystem is. I tend to agree, because it&rsquo;s missing features and tools that I would have expected from a mature ecosystem.</p>
<h3 id="the-missing-package-manager">The missing package manager</h3>
<p>Sharing protobuf definitions across multiple projects or repositories is a constant struggle without specialized tools. While solutions like <a href="https://bazel.build/reference/be/protocol-buffer" rel="external">Bazel</a>, <a href="https://www.pantsbuild.org/2.21/docs/go/integrations/protobuf" rel="external">Pants</a>, and <a href="https://buf.build/product/bsr" rel="external">Buf&rsquo;s BSR</a> exist, my experience with protobuf &ldquo;in the real world&rdquo; is&hellip; mixed. There are prominent open source projects, some by Google, that have bash scripts scrapped together to download dependencies before evoking <code>protoc</code> manually. Just imagine a programming language with no solution for managing dependencies. That&rsquo;s insane. I think both <a href="https://grpc.io/blog/bazel-rules-protobuf/" rel="external">Bazel</a> and <a href="https://buf.build/docs/ecosystem/cli-overview" rel="external">Buf tooling</a> solve this problem pretty well but I&rsquo;m just frustrated that every repo I come across that uses protobuf solves the problem in the most bespoke way possible. The community needs to come together to improve this. There is an open-source repo called <a href="https://github.com/helsing-ai/buffrs" rel="external">Buffrs</a> that appears to be tackling this problem. I haven&rsquo;t used it personally but it looks decent so far.</p>

    
    
        
        
            
        
        <img src="https://kmcd.dev/posts/grpc-the-ugly-parts/build_hu_48d6d71ba4fbbe87.webp"
             alt="" class="center" width="600px"/>
    


<p>Related to dependencies, I do want to call out that <a href="https://protobuf.dev/reference/protobuf/google.protobuf/" rel="external">Google&rsquo;s &ldquo;well-known&rdquo; protobuf types</a> get special privilege of being built into protoc. While these types are incredibly useful and invaluable, their privilege makes it hard for other libraries of useful protobuf types to exist and thrive. Just building these protobuf definitions into protoc (and other tooling) is a cop out for not having a real and consistent story for dependency management.</p>
<h3 id="editor-support">Editor Support</h3>
<p>Editor integration for protobuf code generation leaves a lot to be desired. It would be immensely helpful if editors could intelligently link generated code back to its protobuf source. This would provide a more seamless experience, but the tooling just isn&rsquo;t smart enough yet. Also, I think everyone needs to run with <a href="https://buf.build/docs/editor-integration" rel="external">Buf&rsquo;s editor support</a>. Having a linter and autoformatter built into your editor is the expected from developers nowadays. And with protobuf, there are <a href="https://buf.build/docs/lint/rules" rel="external">extremely real reasons</a> to follow the advice of the linter.</p>
<p>Projects like <a href="https://trpc.io/" rel="external">tRPC</a> showcase the benefits of tight integration and opinionated design choices—something that protobuf, by its nature, can&rsquo;t fully replicate. However, I remain hopeful that the protobuf ecosystem can evolve to offer a similarly streamlined developer experience.</p>
<h3 id="ugly-documentation">Ugly Documentation</h3>
<p>I&rsquo;ve never seen documentation generated from protobuf that wasn&rsquo;t super ugly. I think since gRPC has historically been a backend service, the backend devs never bothered to put any real effort into making pretty documentation output using a protoc plugin. I&rsquo;ve solved this problem by <a href="https://github.com/sudorandom/protoc-gen-connect-openapi" rel="external">making a protoc plugin</a> that generates OpenAPI from given protobuf files. Then I use one of the many beautiful tools for displaying the OpenAPI spec. This was, by far, much easier than getting me to make a decent design. Another side benefit for generating OpenAPI from protobuf is the ability to tap into that ecosystem since there&rsquo;s more to it than just documentation.</p>
<p>Let&rsquo;s look at a real example. This is a document generated using one of the few tools for generating documentation from protobuf, <a href="https://github.com/pseudomuto/protoc-gen-doc" rel="external">protoc-gen-doc</a>:</p>

    
    
        
        
            
        
        <img src="https://kmcd.dev/posts/grpc-the-ugly-parts/protoc-gen-doc_hu_64610573235f8641.webp"
             alt="" class="center" width="600px"/>
    


<p>Compare it to some of the OpenAPI tooling. This was generated using <a href="https://github.com/stoplightio/elements" rel="external">Elements</a>, but there are many, many other alternatives that look equally as polished:</p>

    
    
        
        
            
        
        <img src="https://kmcd.dev/posts/grpc-the-ugly-parts/elements_hu_e3b5d245aba88abe.webp"
             alt="" class="center" width="800px"/>
    


<p>It&rsquo;s kind-of not fair to point at a single plugin and say that the default template doesn&rsquo;t look as good as OpenAPI alternatives, because you actually do have more flexibility with protoc-gen-doc. It allows you to specify your own template so it could look as beautiful as you want. However, this does line up with my point: the tooling is more finished and polished in the REST world than gRPC. This is a fixable problem, but we need to get frontend devs and designers excited about gRPC or backend engineers need to start sharpening their design skills.</p>
<p>I also want to note that OpenAPI/Swagger interfaces often have a way to test endpoints directly from the documentation website. This is completely missing from equivalent tools in the gRPC world. Additionally, with most OpenAPI documentation tools you can clearly see which fields are required and will display constraints on fields that have them. So not only is it prettier, it&rsquo;s more functional as well.</p>
<h2 id="conclusion">Conclusion</h2>
<p>gRPC, while a powerful tool in many ways, still has room to grow.  The less-than-ideal aspects of generated code, coupled with the challenges of dependency management and evolving protobuf schemas, can create friction for developers. The lack of intuitive editor integration and the historical focus on backend services have also hindered its wider adoption in web development.</p>
<p>However, I think the future of gRPC is bright and can be far less ugly. The community is actively addressing these challenges, developing tools like <a href="https://buf.build/product/cli" rel="external">the buf CLI</a>, <a href="https://github.com/bufbuild/protovalidate" rel="external">protovalidate</a> and <a href="https://github.com/sudorandom/protoc-gen-connect-openapi" rel="external">protoc-gen-connect-openapi</a> to bridge the gaps and enhance the developer experience. As gRPC matures and <a href="https://trends.google.com/trends/explore?date=all&amp;q=%2Fm%2F04dzxdz,%2Fg%2F11cp5mklv8,RESTful&amp;hl=en" rel="external">its ecosystem expands</a>, we can anticipate improved tooling, better editor support, and a smoother integration into the frontend world.</p>
]]></content:encoded></item><item><title>Working with Protobuf in 2024</title><link>https://kmcd.dev/posts/working-with-protobuf-in-2024/</link><pubDate>Tue, 27 Aug 2024 10:00:00 +0000</pubDate><guid>https://kmcd.dev/posts/working-with-protobuf-in-2024/</guid><description> 
                &lt;p> &lt;img hspace="5" src="https://kmcd.dev/posts/working-with-protobuf-in-2024/cover_hu_c4edfa33fe867816.webp" /> &lt;/p>
                
                Tools and tricks for developing with protobuf.
                </description><content:encoded><![CDATA[<p>Protocol Buffers (protobuf), Google&rsquo;s brainchild for efficient data serialization, have become an indispensable tool in the modern software development landscape. They offer a compact and efficient way to structure data for storage or transmission, making them ideal for applications like gRPC services, data storage, and inter-service communication. gRPC is even branching into the frontend of the web, with gRPC-Web and ConnectRPC. This means that the traditional protobuf workflow can sometimes feel a bit dated and cumbersome with tooling that isn&rsquo;t quite made to be easy to use and, worse, can be easy to use incorrectly. In this article, we&rsquo;ll explore some modern tools that address these pain points, making protobuf development more enjoyable and productive. But first, let&rsquo;s recap how the &ldquo;traditional&rdquo; protobuf workflow typically works.</p>
<h2 id="traditional-workflow">Traditional Workflow</h2>
<p>Here&rsquo;s what a typical workflow can look like when working with protobufs:</p>
<ol>
<li><strong>Define the Protobuf File:</strong>  You create a <code>.proto</code> file, defining your message structures, enums, and services (if using gRPC).</li>
<li><strong>Compile with <code>protoc</code>:</strong> You use the <code>protoc</code> compiler to generate code in your desired languages (e.g., Go, Java, Python, Rust, etc.) from your <code>.proto</code> files.</li>
<li><strong>Implement and Use:</strong> You implement the server-side logic (if applicable) using the generated server stubs and utilize the generated client code to interact with your protobuf-based services.</li>
</ol>
<p>Let&rsquo;s visualize this traditional workflow to highlight these pain points:</p>
<div class="diagram-wrapper">
    <span class="diagram"
        style="">

    
    
        
        
        <img src="https://kmcd.dev/posts/working-with-protobuf-in-2024/workflow-legacy.svg"
             alt="" class="center" height="600px"/>
    



    </span>

    
</div>

<h3 id="whats-missing">What&rsquo;s Missing?</h3>
<p>While this workflow gets the job done, it leaves a lot of room for improvement:</p>
<ul>
<li>Error-Prone Build Steps: The process often involves manual compilation and code generation, increasing the risk of human errors. Most people introduce a <code>Makefile</code> or a bash script to handle calling <code>protoc</code>, but even this can be error-prone and difficult to maintain. Some believe this doesn&rsquo;t exist or some magical build system &ldquo;handles it for you&rdquo;, but I assure you, <a href="https://github.com/openconfig/gnmi/blob/master/compile_protos.sh" rel="external"> it exists</a> and it&rsquo;s <a href="https://github.com/search?q=protoc&#43;language%3Abash&amp;type=code" rel="external">not hard to find</a> plenty of examples of this.</li>
<li>This workflow doesn&rsquo;t include a standardized way to handle protobuf dependencies. Traditionally, this involves the Makefile or bash script that I just mentioned to download protobufs from various online repositories or manually copying them between projects. This is bad, but the Protobuf/gRPC projects don&rsquo;t really give much guidance on how to reuse protobufs, so people have just done it themselves in wildly different ways.</li>
<li>Lack of Consistency: Without proper tooling, it&rsquo;s challenging to maintain consistent formatting and styling across multiple <code>.proto</code> files.</li>
<li>Breaking Changes: Manually tracking changes to your protobufs and ensuring backward compatibility can be tedious and error-prone.</li>
<li>No Mocking or Prototyping: It takes a good amount of effort to make a gRPC service, even if you just want to use mock data.</li>
</ul>
<p>Fortunately, the Protobuf ecosystem has evolved dramatically in the last few years, and most of the advancement is from third-parties (not Google), which is exciting. So let&rsquo;s cover parts of the modern workflow that you won&rsquo;t see in a gRPC or protobuf tutorial.</p>
<h2 id="the-next-generation-of-protobuf-tooling">The next generation of protobuf tooling</h2>
<p>The Protobuf ecosystem has seen a surge of innovation in recent years, with many third-party tools emerging to address the limitations of the traditional workflow. Let&rsquo;s delve into some of these tools.</p>
<h3 id="json-to-proto">JSON to Proto</h3>
<p><a href="https://json-to-proto.github.io/" rel="external">JSON to Proto</a> is a online tool that simplifies the creation of protobuf definitions if you are brand new to protobufs. I wouldn&rsquo;t recommend using this for the long haul, but it can be a good way to get started quickly by pasting in sample JSON data, and the tool will generate a corresponding <code>.proto</code> file for you. This is particularly useful when you&rsquo;re starting with existing JSON data or want to quickly prototype a protobuf schema. Learn more from <a href="https://github.com/json-to-proto/json-to-proto.github.io" rel="external">the Github repo</a>.</p>

    
    
        
        
            
        
        <img src="https://kmcd.dev/posts/working-with-protobuf-in-2024/json-to-proto_hu_5cb681304f648615.webp"
             alt="" class="center" width="600px"/>
    


<h3 id="protobuf-pal">Protobuf Pal</h3>
<p><a href="https://www.protobufpal.com/" rel="external">Protobuf Pal</a> is a browser-based protobuf editor designed to streamline the creation and editing of <code>.proto</code> files. It offers features like syntax highlighting, error checking, and auto-completion, making it easier to write valid protobuf definitions.</p>
<h3 id="buf-cli">Buf CLI</h3>
<p><a href="https://buf.build/" rel="external">Buf</a> is a comprehensive toolkit designed to make working with protobufs a breeze.</p>

    
    
        
        
            
        
        <img src="https://kmcd.dev/posts/working-with-protobuf-in-2024/surprise_hu_8964afe9cde880a4.webp"
             alt="" class="center" width="500px"/>
    


<p>The Buf CLI offers several powerful features:</p>
<ul>
<li><strong><code>buf generate</code>:</strong>  A replacement for <code>protoc</code> that provides consistent code generation across different languages and environments. <a href="https://buf.build/docs/generate/tutorial" rel="external">Read more here.</a></li>
<li><strong><code>buf lint</code>:</strong>  A linter that helps you maintain clean and consistent protobuf definitions, catching potential issues early. <a href="https://buf.build/docs/lint/tutorial" rel="external">Read more here.</a></li>
<li><strong><code>buf format</code>:</strong> An opinionated formatter to ensure consistent styling across your <code>.proto</code> files. <a href="https://buf.build/blog/introducing-buf-format" rel="external">Read more here.</a></li>
<li><strong><code>buf curl</code>:</strong> is useful not only for development but also for testing. You can use it to send gRPC requests from your terminal, making it easy to verify the behavior of your services. It can craft requests using server reflection, local protobuf files, descriptor files or from a reference to the buf.build registry. <a href="https://buf.build/docs/curl/usage" rel="external">Read more here.</a></li>
</ul>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-shell" data-lang="shell"><span class="line"><span class="cl">$ buf curl --list-methods https://demo.connectrpc.com
</span></span><span class="line"><span class="cl">connectrpc.eliza.v1.ElizaService/Converse
</span></span><span class="line"><span class="cl">connectrpc.eliza.v1.ElizaService/Introduce
</span></span><span class="line"><span class="cl">connectrpc.eliza.v1.ElizaService/Say
</span></span><span class="line"><span class="cl">$ buf curl -d <span class="s1">&#39;{&#34;sentence&#34;:&#34;Hello! I need some help, doc&#34;}&#39;</span> https://demo.connectrpc.com/connectrpc.eliza.v1.ElizaService/Say
</span></span><span class="line"><span class="cl"><span class="o">{</span>
</span></span><span class="line"><span class="cl">  <span class="s2">&#34;sentence&#34;</span>: <span class="s2">&#34;Hello...I&#39;m glad you could drop by today.&#34;</span>
</span></span><span class="line"><span class="cl"><span class="o">}</span>
</span></span></code></pre></div><p>You can use the buf CLI directly on the CLI (which is great when you&rsquo;re playing around or adding it to a continuous integration process) or <a href="https://buf.build/docs/editor-integration" rel="external">integrated into your code editor</a>, which is where the real magic happens, anyway.</p>
<h3 id="buf-schema-registry-bsr">Buf Schema Registry (BSR)</h3>
<p><a href="https://buf.build/docs/bsr/introduction" rel="external">The Buf Schema Registry (BSR)</a> is the missing package manager for Protobufs, but it does a bit more than that. Not only does it allow you to push versioned schemas to one place, but it also has cool features like <a href="https://buf.build/docs/bsr/generated-sdks/overview" rel="external">automatically generated SDKs</a>. The idea is that you can just import the package in your favorite language and magically you have your server stubs and clients in the language of your choice (as long as it&rsquo;s Go, Typescript/Javascript, Java/Kotlin, Swift, Python or Rust).</p>
<p>With the BSR, you no longer need bespoke Makefile or bash scripts to pull down protobuf dependencies.</p>
<h2 id="testing">Testing</h2>
<p>This next set of tools are good for testing live gRPC endpoints. Obviously, <a href="https://github.com/fullstorydev/grpcurl" rel="external">gRPCurl</a> is an amazing tool for this, but let&rsquo;s discover some tools that are a bit newer to the scene.</p>
<h3 id="buf-studio">Buf Studio</h3>
<p><a href="https://buf.build/studio" rel="external">Buf Studio</a> is an interactive web UI for all your gRPC and Protobuf services stored on the Buf Schema Registry. With Buf Studio you can craft gRPC/gRPC-Web/Connect requests using images on the buf registry. Buf Studio uses those protobuf schemas to support autocompletion of these requests, which is super cool. It can also use an agent that is built into the <code>Buf CLI</code> to proxy requests from internal networks, making this web-based tool a bit more flexible.</p>

    
    
        
        
            
        
        <img src="https://kmcd.dev/posts/working-with-protobuf-in-2024/buf-studio_hu_f94709d7fdb4af06.webp"
             alt="" class="center" width="600px"/>
    


<h3 id="postman">Postman</h3>
<p><a href="https://blog.postman.com/postman-now-supports-grpc/" rel="external">Postman</a>, a popular API testing tool, now supports gRPC. You can leverage its familiar interface to construct and send gRPC requests, making it a convenient option for testing your protobuf services.</p>
<h3 id="insomnia">Insomnia</h3>
<p><a href="https://docs.insomnia.rest/insomnia/grpc" rel="external">Insomnia</a> is another API testing platform that has added gRPC support. Similar to Postman, it allows you to design and execute gRPC requests within its user-friendly environment.</p>

    
    
        
        
            
        
        <img src="https://kmcd.dev/posts/working-with-protobuf-in-2024/insomnia_hu_de0279688fd9a3cf.webp"
             alt="" class="center" width="800px"/>
    


<p>Insomnia&rsquo;s UI takes a little getting used to but once gRPC is set up, it gets easier.</p>
<h3 id="k6">k6</h3>
<p><a href="https://grafana.com/docs/k6/latest/using-k6/protocols/grpc/" rel="external">k6</a> is a powerful load testing tool that can be used to simulate heavy traffic on your gRPC services. It helps you identify performance bottlenecks and ensure your services can handle real-world loads.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ts" data-lang="ts"><span class="line"><span class="cl"><span class="kr">import</span> <span class="p">{</span> <span class="nx">Client</span><span class="p">,</span> <span class="nx">StatusOK</span> <span class="p">}</span> <span class="kr">from</span> <span class="s1">&#39;k6/net/grpc&#39;</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="kr">import</span> <span class="p">{</span> <span class="nx">check</span><span class="p">,</span> <span class="nx">sleep</span> <span class="p">}</span> <span class="kr">from</span> <span class="s1">&#39;k6&#39;</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kr">const</span> <span class="nx">client</span> <span class="o">=</span> <span class="k">new</span> <span class="nx">Client</span><span class="p">();</span>
</span></span><span class="line"><span class="cl"><span class="nx">client</span><span class="p">.</span><span class="nx">load</span><span class="p">([</span><span class="s1">&#39;definitions&#39;</span><span class="p">],</span> <span class="s1">&#39;eliza.proto&#39;</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kr">export</span> <span class="k">default</span> <span class="p">()</span> <span class="o">=&gt;</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">  <span class="nx">client</span><span class="p">.</span><span class="nx">connect</span><span class="p">(</span><span class="s1">&#39;127.0.0.1:10000&#39;</span><span class="p">,</span> <span class="p">{});</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="kr">const</span> <span class="nx">data</span> <span class="o">=</span> <span class="p">{</span> <span class="nx">sentence</span><span class="o">:</span> <span class="s1">&#39;Hello, doc!&#39;</span> <span class="p">};</span>
</span></span><span class="line"><span class="cl">  <span class="kr">const</span> <span class="nx">response</span> <span class="o">=</span> <span class="nx">client</span><span class="p">.</span><span class="nx">invoke</span><span class="p">(</span><span class="s1">&#39;connectrpc.eliza.v1.ElizaService/Say&#39;</span><span class="p">,</span> <span class="nx">data</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="nx">check</span><span class="p">(</span><span class="nx">response</span><span class="p">,</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="s1">&#39;status is OK&#39;</span><span class="o">:</span> <span class="p">(</span><span class="nx">r</span><span class="p">)</span> <span class="o">=&gt;</span> <span class="nx">r</span> <span class="o">&amp;&amp;</span> <span class="nx">r</span><span class="p">.</span><span class="nx">status</span> <span class="o">===</span> <span class="nx">StatusOK</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">  <span class="p">});</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="nx">JSON</span><span class="p">.</span><span class="nx">stringify</span><span class="p">(</span><span class="nx">response</span><span class="p">.</span><span class="nx">message</span><span class="p">));</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="nx">client</span><span class="p">.</span><span class="nx">close</span><span class="p">();</span>
</span></span><span class="line"><span class="cl">  <span class="nx">sleep</span><span class="p">(</span><span class="mi">1</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="p">};</span>
</span></span></code></pre></div><h3 id="fauxrpc">FauxRPC</h3>
<p>I couldn&rsquo;t write this article without promoting my own tool, <a href="https://fauxrpc.com" rel="external">FauxRPC</a>. FauxRPC is a tool that enables developers to quickly generate mock gRPC servers from protobuf definitions, facilitating early API development and testing. By incorporating FauxRPC into your workflow, you can easily create realistic mock services that simulate real gRPC server behavior, allowing you to test your client implementations and identify potential issues without the need for a fully implemented backend. This streamlined prototyping and testing process ultimately fosters faster iteration and more robust API development.</p>
<p>With a single command, you can have a server running with fake data!</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-shell" data-lang="shell"><span class="line"><span class="cl">$ buf build buf.build/connectrpc/eliza -o eliza.binpb
</span></span><span class="line"><span class="cl">$ fauxrpc run --schema<span class="o">=</span>eliza.binpb
</span></span><span class="line"><span class="cl">FauxRPC <span class="o">(</span>0.0.16 <span class="o">(</span>97e4c8caf9d3c22387a393180e00bce40b2834c6<span class="o">)</span> @ 2024-08-22T18:42:56Z<span class="p">;</span> go1.22.4<span class="o">)</span>
</span></span><span class="line"><span class="cl">Listening on http://127.0.0.1:6660
</span></span><span class="line"><span class="cl">OpenAPI documentation: http://127.0.0.1:6660/fauxrpc.openapi.html
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">Example Commands:
</span></span><span class="line"><span class="cl">$ buf curl --http2-prior-knowledge http://127.0.0.1:6660 --list-methods
</span></span><span class="line"><span class="cl">$ buf curl --http2-prior-knowledge http://127.0.0.1:6660/<span class="o">[</span>METHOD_NAME<span class="o">]</span>
</span></span></code></pre></div><p>Learn more about it in <a href="https://kmcd.dev/posts/fauxrpc/">my previous post announcing it</a> or <a href="https://fauxrpc.com/docs/intro/" rel="external">the documentation website</a>.</p>
<h2 id="new-workflow">New Workflow</h2>
<p>This enhanced workflow empowers developers to iterate faster, catch errors earlier, and ensure API stability, ultimately leading to more robust and maintainable protobuf-based applications. Let&rsquo;s explore how these tools fit into the enhanced workflow:</p>
<div class="diagram-wrapper">
    <span class="diagram"
        style="">

    
    
        
        
        <img src="https://kmcd.dev/posts/working-with-protobuf-in-2024/workflow-new.svg"
             alt="" class="center" width="900px"/>
    



    </span>

    
</div>

<p>Note that this workflow has one more &ldquo;find issues and iterate&rdquo; connections right after writing the protobuf file. That&rsquo;s because these new tools will help you find issues with your protobuf schemas earlier in a more automated way.</p>
<p>Also note that with FauxRPC, frontend developers (or whatever is using the generated clients for the service) can <strong>start working on their part before the backend developer is finished with their work</strong>. Frontend devs no longer have to come up with mock APIs (which rapidly get outdated with reality) just to get started on their frontend work. Integration work on another service which uses this protobuf can happen before the backend implementation is completed. Everyone can work in parallel and the better the schema is (with <a href="https://github.com/bufbuild/protovalidate" rel="external">protovalidate</a> constraints) the better the fake data will be.</p>
<h3 id="more-ways-to-start-making-protobuf-files">More ways to start making protobuf files</h3>
<p>Developers can still define services, messages, and enums directly in the .proto file. However, tools like JSON-to-Proto and Protobuf Pal provide visual aids and assistance in creating and editing .proto files, reducing errors and improving productivity. I wouldn&rsquo;t recommend these tools all the time, but they are probably good for quickly getting a prototype going quickly.</p>
<h3 id="automated-code-generation-and-management">Automated Code Generation and Management</h3>
<p>Buf&rsquo;s <code>buf generate</code> command streamlines the code generation process with a set of declarative configuration files, ensuring consistency across different languages and platforms. Buf&rsquo;s dependency management capabilities eliminate the need for manual scripts to handle protobuf dependencies.</p>
<h3 id="enhanced-quality-control">Enhanced Quality Control</h3>
<ul>
<li>Buf&rsquo;s <code>buf lint</code> enforces coding standards and best practices, catching potential issues early in development.</li>
<li>Buf&rsquo;s <code>buf format</code> automatically formats your .proto files, ensuring consistency and readability.</li>
<li>Buf&rsquo;s <code>buf breaking</code> change detection helps you avoid introducing changes that could disrupt existing clients.</li>
</ul>
<p>All of these put together mean that issues in your API schema are discovered automatically, as soon as possible.</p>
<h3 id="streamlined-testing-and-prototyping">Streamlined Testing and Prototyping</h3>
<p>Buf&rsquo;s <code>buf curl</code> and Buf Studio&rsquo;s built-in gRPC client enable quick testing and interaction with your services. FauxRPC facilitates rapid prototyping by allowing you to mock gRPC services without implementing the actual server-side logic.</p>
<h2 id="conclusion">Conclusion</h2>
<p>The traditional protobuf workflow, while functional, can involve manual steps and potential pitfalls. By incorporating tools like the <a href="https://buf.build/product/cli" rel="external">Buf CLI</a>, <a href="https://github.com/json-to-proto/json-to-proto.github.io" rel="external">JSON-to-Proto</a>, <a href="https://www.protobufpal.com/" rel="external">Protobuf Pal</a>, and <a href="https://fauxrpc.com" rel="external">FauxRPC</a> into your development process, you can significantly enhance your productivity and ensure the quality of your protobuf definitions. Note that I haven&rsquo;t come close to outlining all of the different tools that you can use with Protobuf. So, don&rsquo;t hesitate to explore these tools and discover how they can transform your Protobuf development experience!</p>
]]></content:encoded></item><item><title>Introducing FauxRPC</title><link>https://kmcd.dev/posts/fauxrpc/</link><pubDate>Tue, 20 Aug 2024 10:00:00 +0000</pubDate><guid>https://kmcd.dev/posts/fauxrpc/</guid><description> 
                &lt;p> &lt;img hspace="5" src="https://kmcd.dev/posts/fauxrpc/cover_hu_39af42cea62050f8.webp" /> &lt;/p>
                
                I made a server that outputs nonsense.
                </description><content:encoded><![CDATA[<p>I would like to introduce <strong><a href="https://fauxrpc.com/" rel="external">FauxRPC</a></strong>, 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 <a href="https://kmcd.dev/posts/api-contracts/">protobuf-based workflow</a>, this tool could help.</p>
<h2 id="why-fauxrpc">Why FauxRPC?</h2>
<ul>
<li><strong>Faster Development &amp; Testing:</strong> Work independently without relying on fully functional backend services.</li>
<li><strong>Isolation &amp; Control:</strong> Test frontend components in isolation with controlled fake data.</li>
<li><strong>Multi-Protocol Support:</strong> Supports multiple protocols (gRPC, gRPC-Web, Connect, and REST).</li>
<li><strong>Prototyping &amp; Demos:</strong> Create prototypes and demos quickly without building the full backend. Fake it till you make it.</li>
<li><strong>Improved Collaboration:</strong> Bridge the gap between frontend and backend teams.</li>
<li><strong>Plays well with others:</strong> Test data from FauxRPC will try to automatically follow any <a href="https://github.com/bufbuild/protovalidate" rel="external">protovalidate</a> constraints that are defined.</li>
</ul>
<h2 id="how-it-works">How it Works</h2>
<p>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 <code>*.proto</code> 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 <code>google.api.http</code> annotations defined). Descriptors contain all of the information found in a set of <code>.proto</code> files. You can generate them with <code>protoc</code> or the <code>buf build</code> command.</p>
<div class="diagram-wrapper">
    <span class="diagram"
        style="">

    
    
        
        
        <img src="https://kmcd.dev/posts/fauxrpc/diagram.svg"
             alt="" class="center" width="800px"/>
    



    </span>

    
</div>

<h2 id="get-started">Get Started</h2>
<p>FauxRPC is available as an open-source project. Check out <a href="https://fauxrpc.com/docs/intro/" rel="external">the documentation</a> and examples to get started. Here&rsquo;s a quick overview, but be sure to check the official documentation for the most up-to-date instructions:</p>
<h3 id="install-via-source">Install via source</h3>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-fallback" data-lang="fallback"><span class="line"><span class="cl">go install github.com/sudorandom/fauxrpc/cmd/fauxrpc@latest
</span></span></code></pre></div><h3 id="pre-built-binaries">Pre-built binaries</h3>
<p>Binaries are built for several platforms for each release. See the latest ones on <a href="https://github.com/sudorandom/fauxrpc/releases/latest" rel="external">the releases page</a>.</p>
<h3 id="use-descriptors">Use Descriptors</h3>
<p>Make an <code>example.proto</code> file (or use a file that already exists):</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-protobuf" data-lang="protobuf"><span class="line"><span class="cl"><span class="n">syntax</span> <span class="o">=</span> <span class="s">&#34;proto3&#34;</span><span class="p">;</span><span class="err">
</span></span></span><span class="line"><span class="cl"><span class="err">
</span></span></span><span class="line"><span class="cl"><span class="err"></span><span class="kn">package</span> <span class="nn">greet</span><span class="o">.</span><span class="n">v1</span><span class="p">;</span><span class="err">
</span></span></span><span class="line"><span class="cl"><span class="err">
</span></span></span><span class="line"><span class="cl"><span class="err"></span><span class="kd">message</span> <span class="nc">GreetRequest</span> <span class="p">{</span><span class="err">
</span></span></span><span class="line"><span class="cl"><span class="err"></span>  <span class="kt">string</span> <span class="n">name</span> <span class="o">=</span> <span class="mi">1</span><span class="p">;</span><span class="err">
</span></span></span><span class="line"><span class="cl"><span class="err"></span><span class="p">}</span><span class="err">
</span></span></span><span class="line"><span class="cl"><span class="err">
</span></span></span><span class="line"><span class="cl"><span class="err"></span><span class="kd">message</span> <span class="nc">GreetResponse</span> <span class="p">{</span><span class="err">
</span></span></span><span class="line"><span class="cl"><span class="err"></span>  <span class="kt">string</span> <span class="n">greeting</span> <span class="o">=</span> <span class="mi">1</span><span class="p">;</span><span class="err">
</span></span></span><span class="line"><span class="cl"><span class="err"></span><span class="p">}</span><span class="err">
</span></span></span><span class="line"><span class="cl"><span class="err">
</span></span></span><span class="line"><span class="cl"><span class="err"></span><span class="kd">service</span> <span class="n">GreetService</span> <span class="p">{</span><span class="err">
</span></span></span><span class="line"><span class="cl"><span class="err"></span>  <span class="k">rpc</span> <span class="n">Greet</span><span class="p">(</span><span class="n">GreetRequest</span><span class="p">)</span> <span class="k">returns</span> <span class="p">(</span><span class="n">GreetResponse</span><span class="p">)</span> <span class="p">{}</span><span class="err">
</span></span></span><span class="line"><span class="cl"><span class="err"></span><span class="p">}</span><span class="err">
</span></span></span></code></pre></div><p>Create a descriptors file and use it to start the FauxRPC server:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-shell" data-lang="shell"><span class="line"><span class="cl">$ buf build ./example.proto -o ./example.binpb
</span></span><span class="line"><span class="cl">$ fauxrpc run --schema<span class="o">=</span>./example.binpb
</span></span><span class="line"><span class="cl">2024/08/17 08:01:19 INFO Listening on http://127.0.0.1:6660
</span></span><span class="line"><span class="cl">2024/08/17 08:01:19 INFO See available methods: buf curl --http2-prior-knowledge http://127.0.0.1:6660 --list-methods
</span></span></code></pre></div><p>Done! It&rsquo;s that easy. Now you can call the service with any tooling that supports gRPC, gRPC-Web, or connect. So <a href="https://buf.build/docs/reference/cli/buf/curl" rel="external">buf curl</a>, <a href="https://github.com/fullstorydev/grpcurl" rel="external">grpcurl</a>, <a href="https://www.postman.com/" rel="external">Postman</a>, <a href="https://insomnia.rest/" rel="external">Insomnia</a> all work fine!</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-shell" data-lang="shell"><span class="line"><span class="cl">$ buf curl --http2-prior-knowledge http://127.0.0.1:6660/greet.v1.GreetService/Greet
</span></span><span class="line"><span class="cl"><span class="o">{</span>
</span></span><span class="line"><span class="cl">  <span class="s2">&#34;greeting&#34;</span>: <span class="s2">&#34;dream&#34;</span>
</span></span><span class="line"><span class="cl"><span class="o">}</span>
</span></span></code></pre></div><h3 id="server-reflection">Server Reflection</h3>
<p>If there&rsquo;s an existing gRPC service running that you want to emulate, you can use server reflection to start the FauxRPC service:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-shell" data-lang="shell"><span class="line"><span class="cl">$ fauxrpc run --schema<span class="o">=</span>https://demo.connectrpc.com
</span></span></code></pre></div><h3 id="from-bsr-buf-schema-registry">From BSR (Buf Schema Registry)</h3>
<p>Buf has a <a href="https://buf.build/product/bsr" rel="external">schema registry</a> where many schemas are hosted. Here&rsquo;s how to use FauxRPC using images from the registry.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-shell" data-lang="shell"><span class="line"><span class="cl">$ buf build buf.build/bufbuild/registry -o bufbuild.registry.json
</span></span><span class="line"><span class="cl">$ fauxrpc run --schema<span class="o">=</span>./bufbuild.registry.json
</span></span></code></pre></div><h3 id="multiple-sources">Multiple Sources</h3>
<p>You can define this <code>--schema</code> 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:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-shell" data-lang="shell"><span class="line"><span class="cl">$ fauxrpc run --schema<span class="o">=</span>https://demo.connectrpc.com --schema<span class="o">=</span>./example.binpb
</span></span></code></pre></div><h2 id="multi-protocol-support">Multi-protocol Support</h2>
<p>The multi-protocol support <a href="https://connectrpc.com/docs/multi-protocol/" rel="external">is based on ConnectRPC</a>. So with FauxRPC, you get <strong>gRPC, gRPC-Web and Connect</strong> out of the box. However, FauxRPC does one thing more. It allows you to use <a href="https://grpc-ecosystem.github.io/grpc-gateway/docs/tutorials/adding_annotations/" rel="external"><code>google.api.http</code> annotations</a> to present a JSON/HTTP API, so you can gRPC and REST together! This is normally done with <a href="https://github.com/grpc-ecosystem/grpc-gateway" rel="external">an additional service</a> 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&rsquo;s a concrete example:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-protobuf" data-lang="protobuf"><span class="line"><span class="cl"><span class="n">syntax</span> <span class="o">=</span> <span class="s">&#34;proto3&#34;</span><span class="p">;</span><span class="err">
</span></span></span><span class="line"><span class="cl"><span class="err">
</span></span></span><span class="line"><span class="cl"><span class="err"></span><span class="kn">package</span> <span class="nn">http</span><span class="o">.</span><span class="n">service</span><span class="p">;</span><span class="err">
</span></span></span><span class="line"><span class="cl"><span class="err">
</span></span></span><span class="line"><span class="cl"><span class="err"></span><span class="k">import</span> <span class="s">&#34;google/api/annotations.proto&#34;</span><span class="p">;</span><span class="err">
</span></span></span><span class="line"><span class="cl"><span class="err">
</span></span></span><span class="line"><span class="cl"><span class="err"></span><span class="kd">service</span> <span class="n">HTTPService</span> <span class="p">{</span><span class="err">
</span></span></span><span class="line"><span class="cl"><span class="err"></span>  <span class="k">rpc</span> <span class="n">GetMessage</span><span class="p">(</span><span class="n">GetMessageRequest</span><span class="p">)</span> <span class="k">returns</span> <span class="p">(</span><span class="n">Message</span><span class="p">)</span> <span class="p">{</span><span class="err">
</span></span></span><span class="line"><span class="cl"><span class="err"></span>    <span class="k">option</span> <span class="p">(</span><span class="n">google.api.http</span><span class="p">)</span> <span class="o">=</span> <span class="p">{</span><span class="n">get</span><span class="o">:</span> <span class="s">&#34;/v1/{name=messages/*}&#34;</span><span class="p">};</span><span class="err">
</span></span></span><span class="line"><span class="cl"><span class="err"></span>  <span class="p">}</span><span class="err">
</span></span></span><span class="line"><span class="cl"><span class="err"></span><span class="p">}</span><span class="err">
</span></span></span><span class="line"><span class="cl"><span class="err"></span><span class="kd">message</span> <span class="nc">GetMessageRequest</span> <span class="p">{</span><span class="err">
</span></span></span><span class="line"><span class="cl"><span class="err"></span>  <span class="kt">string</span> <span class="n">name</span> <span class="o">=</span> <span class="mi">1</span><span class="p">;</span> <span class="c1">// Mapped to URL path.
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="p">}</span><span class="err">
</span></span></span><span class="line"><span class="cl"><span class="err"></span><span class="kd">message</span> <span class="nc">Message</span> <span class="p">{</span><span class="err">
</span></span></span><span class="line"><span class="cl"><span class="err"></span>  <span class="kt">string</span> <span class="n">text</span> <span class="o">=</span> <span class="mi">1</span><span class="p">;</span> <span class="c1">// The resource content.
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="p">}</span><span class="err">
</span></span></span></code></pre></div><p>Again, we start the service by building the descriptors and using</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-fallback" data-lang="fallback"><span class="line"><span class="cl">$ buf build ./httpservice.proto -o ./httpservice.binpb
</span></span><span class="line"><span class="cl">$ fauxrpc run --schema=httpservice.binpb
</span></span></code></pre></div><p>Now that we have the server running we can test this with the &ldquo;normal&rdquo; curl:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-shell" data-lang="shell"><span class="line"><span class="cl">$ curl http://127.0.0.1:6660/v1/messages/123456
</span></span><span class="line"><span class="cl"><span class="o">{</span><span class="s2">&#34;text&#34;</span>:<span class="s2">&#34;Retro.&#34;</span><span class="o">}</span>⏎
</span></span></code></pre></div><p>Sweet. You can now easily support REST alongside gRPC. If you are wondering how to do this with &ldquo;real&rdquo; services, look into <a href="https://github.com/connectrpc/vanguard-go" rel="external">vanguard-go</a>. This library is doing the real heavy lifting.</p>
<h2 id="what-does-the-fake-data-look-like">What does the fake data look like?</h2>
<p>You might be wondering what actual responses look like. FauxRPC&rsquo;s fake data generation is continually improving so these details might change as time goes on. It uses a library called <a href="https://github.com/brianvoe/gofakeit" rel="external">fakeit</a> 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 <a href="https://github.com/bufbuild/protovalidate" rel="external">protovalidate</a> constraints, FauxRPC will try its best to generate data that matches these constraints. Let&rsquo;s look at some examples!</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-protobuf" data-lang="protobuf"><span class="line"><span class="cl"><span class="n">syntax</span> <span class="o">=</span> <span class="s">&#34;proto3&#34;</span><span class="p">;</span><span class="err">
</span></span></span><span class="line"><span class="cl"><span class="err">
</span></span></span><span class="line"><span class="cl"><span class="err"></span><span class="kn">package</span> <span class="nn">greet</span><span class="o">.</span><span class="n">v1</span><span class="p">;</span><span class="err">
</span></span></span><span class="line"><span class="cl"><span class="err">
</span></span></span><span class="line"><span class="cl"><span class="err"></span><span class="kd">message</span> <span class="nc">GreetRequest</span> <span class="p">{</span><span class="err">
</span></span></span><span class="line"><span class="cl"><span class="err"></span>  <span class="kt">string</span> <span class="n">name</span> <span class="o">=</span> <span class="mi">1</span><span class="p">;</span><span class="err">
</span></span></span><span class="line"><span class="cl"><span class="err"></span><span class="p">}</span><span class="err">
</span></span></span><span class="line"><span class="cl"><span class="err">
</span></span></span><span class="line"><span class="cl"><span class="err"></span><span class="kd">message</span> <span class="nc">GreetResponse</span> <span class="p">{</span><span class="err">
</span></span></span><span class="line"><span class="cl"><span class="err"></span>  <span class="kt">string</span> <span class="n">greeting</span> <span class="o">=</span> <span class="mi">1</span><span class="p">;</span><span class="err">
</span></span></span><span class="line"><span class="cl"><span class="err"></span><span class="p">}</span><span class="err">
</span></span></span><span class="line"><span class="cl"><span class="err">
</span></span></span><span class="line"><span class="cl"><span class="err"></span><span class="kd">service</span> <span class="n">GreetService</span> <span class="p">{</span><span class="err">
</span></span></span><span class="line"><span class="cl"><span class="err"></span>  <span class="k">rpc</span> <span class="n">Greet</span><span class="p">(</span><span class="n">GreetRequest</span><span class="p">)</span> <span class="k">returns</span> <span class="p">(</span><span class="n">GreetResponse</span><span class="p">)</span> <span class="p">{}</span><span class="err">
</span></span></span><span class="line"><span class="cl"><span class="err"></span><span class="p">}</span><span class="err">
</span></span></span></code></pre></div><p>With FauxRPC, you will get any kind of word, so it might look like this:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-json" data-lang="json"><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">  <span class="nt">&#34;greeting&#34;</span><span class="p">:</span> <span class="s2">&#34;sufficient&#34;</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><p>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 &ldquo;Hello, [name]&rdquo;. So here&rsquo;s what the same protobuf file might look like with protovalidate constraints:</p>
<p>Now let&rsquo;s see what this looks like with protovalidate constraints:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-protobuf" data-lang="protobuf"><span class="line"><span class="cl"><span class="n">syntax</span> <span class="o">=</span> <span class="s">&#34;proto3&#34;</span><span class="p">;</span><span class="err">
</span></span></span><span class="line"><span class="cl"><span class="err">
</span></span></span><span class="line"><span class="cl"><span class="err"></span><span class="k">import</span> <span class="s">&#34;buf/validate/validate.proto&#34;</span><span class="p">;</span><span class="err">
</span></span></span><span class="line"><span class="cl"><span class="err">
</span></span></span><span class="line"><span class="cl"><span class="err"></span><span class="kn">package</span> <span class="nn">greet</span><span class="o">.</span><span class="n">v1</span><span class="p">;</span><span class="err">
</span></span></span><span class="line"><span class="cl"><span class="err">
</span></span></span><span class="line"><span class="cl"><span class="err"></span><span class="kd">message</span> <span class="nc">GreetRequest</span> <span class="p">{</span><span class="err">
</span></span></span><span class="line"><span class="cl"><span class="err"></span>  <span class="kt">string</span> <span class="n">name</span> <span class="o">=</span> <span class="mi">1</span> <span class="p">[(</span><span class="n">buf.validate.field</span><span class="p">)</span><span class="o">.</span><span class="kt">string</span> <span class="o">=</span> <span class="p">{</span><span class="n">min_len</span><span class="o">:</span> <span class="mi">3</span><span class="p">,</span> <span class="n">max_len</span><span class="o">:</span> <span class="mi">100</span><span class="p">}];</span><span class="err">
</span></span></span><span class="line"><span class="cl"><span class="err"></span><span class="p">}</span><span class="err">
</span></span></span><span class="line"><span class="cl"><span class="err">
</span></span></span><span class="line"><span class="cl"><span class="err"></span><span class="kd">message</span> <span class="nc">GreetResponse</span> <span class="p">{</span><span class="err">
</span></span></span><span class="line"><span class="cl"><span class="err"></span>  <span class="kt">string</span> <span class="n">greeting</span> <span class="o">=</span> <span class="mi">1</span> <span class="p">[(</span><span class="n">buf.validate.field</span><span class="p">)</span><span class="o">.</span><span class="kt">string</span><span class="o">.</span><span class="n">pattern</span> <span class="o">=</span> <span class="s">&#34;^Hello, [a-zA-Z]+$&#34;</span><span class="p">];</span><span class="err">
</span></span></span><span class="line"><span class="cl"><span class="err"></span><span class="p">}</span><span class="err">
</span></span></span><span class="line"><span class="cl"><span class="err">
</span></span></span><span class="line"><span class="cl"><span class="err"></span><span class="kd">service</span> <span class="n">GreetService</span> <span class="p">{</span><span class="err">
</span></span></span><span class="line"><span class="cl"><span class="err"></span>  <span class="k">rpc</span> <span class="n">Greet</span><span class="p">(</span><span class="n">GreetRequest</span><span class="p">)</span> <span class="k">returns</span> <span class="p">(</span><span class="n">GreetResponse</span><span class="p">)</span> <span class="p">{}</span><span class="err">
</span></span></span><span class="line"><span class="cl"><span class="err"></span><span class="p">}</span><span class="err">
</span></span></span></code></pre></div><p>With this new protobuf file, this is what FauxRPC might output now:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-json" data-lang="json"><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">  <span class="nt">&#34;greeting&#34;</span><span class="p">:</span> <span class="s2">&#34;Hello, TWXxF&#34;</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><p>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&rsquo;s services used to manage users, <a href="https://buf.build/bufbuild/registry/docs/main:buf.registry.owner.v1#buf.registry.owner.v1.UserService" rel="external">buf.registry.owner.v1.UserService</a>. Here&rsquo;s what the <code>UserRef</code> message looks like:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-protobuf" data-lang="protobuf"><span class="line"><span class="cl"><span class="kd">message</span> <span class="nc">UserRef</span> <span class="p">{</span><span class="err">
</span></span></span><span class="line"><span class="cl"><span class="err"></span>  <span class="k">option</span> <span class="p">(</span><span class="n">buf.registry.priv.extension.v1beta1.message</span><span class="p">)</span><span class="o">.</span><span class="n">request_only</span> <span class="o">=</span> <span class="kc">true</span><span class="p">;</span><span class="err">
</span></span></span><span class="line"><span class="cl"><span class="err"></span>  <span class="k">oneof</span> <span class="n">value</span> <span class="p">{</span><span class="err">
</span></span></span><span class="line"><span class="cl"><span class="err"></span>    <span class="k">option</span> <span class="p">(</span><span class="n">buf.validate.oneof</span><span class="p">)</span><span class="o">.</span><span class="k">required</span> <span class="o">=</span> <span class="kc">true</span><span class="p">;</span><span class="err">
</span></span></span><span class="line"><span class="cl"><span class="err"></span>    <span class="c1">// The id of the User.
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>    <span class="kt">string</span> <span class="n">id</span> <span class="o">=</span> <span class="mi">1</span> <span class="p">[(</span><span class="n">buf.validate.field</span><span class="p">)</span><span class="o">.</span><span class="kt">string</span><span class="o">.</span><span class="n">tuuid</span> <span class="o">=</span> <span class="kc">true</span><span class="p">];</span><span class="err">
</span></span></span><span class="line"><span class="cl"><span class="err"></span>    <span class="c1">// The name of the User.
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>    <span class="kt">string</span> <span class="n">name</span> <span class="o">=</span> <span class="mi">2</span> <span class="p">[(</span><span class="n">buf.validate.field</span><span class="p">)</span><span class="o">.</span><span class="kt">string</span> <span class="o">=</span> <span class="p">{</span><span class="err">
</span></span></span><span class="line"><span class="cl"><span class="err"></span>      <span class="n">min_len</span><span class="o">:</span> <span class="mi">2</span><span class="err">
</span></span></span><span class="line"><span class="cl"><span class="err"></span>      <span class="n">max_len</span><span class="o">:</span> <span class="mi">32</span><span class="err">
</span></span></span><span class="line"><span class="cl"><span class="err"></span>      <span class="n">pattern</span><span class="o">:</span> <span class="s">&#34;^[a-z][a-z0-9-]*[a-z0-9]$&#34;</span><span class="err">
</span></span></span><span class="line"><span class="cl"><span class="err"></span>    <span class="p">}];</span><span class="err">
</span></span></span><span class="line"><span class="cl"><span class="err"></span>  <span class="p">}</span><span class="err">
</span></span></span><span class="line"><span class="cl"><span class="err"></span><span class="p">}</span><span class="err">
</span></span></span></code></pre></div><p>So let&rsquo;s make our descriptors for this service, start the FauxRPC server and make our example request:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-shell" data-lang="shell"><span class="line"><span class="cl">$ buf build buf.build/bufbuild/registry -o bufbuild.registry.binpb
</span></span><span class="line"><span class="cl">$ fauxrpc run --schema<span class="o">=</span>./bufbuild.registry.binpb
</span></span><span class="line"><span class="cl">$ buf curl --http2-prior-knowledge http://127.0.0.1:6660/buf.registry.owner.v1.UserService/ListUsers
</span></span><span class="line"><span class="cl"><span class="o">{</span>
</span></span><span class="line"><span class="cl">  <span class="s2">&#34;nextPageToken&#34;</span>: <span class="s2">&#34;Food truck.&#34;</span>,
</span></span><span class="line"><span class="cl">  <span class="s2">&#34;users&#34;</span>: <span class="o">[</span>
</span></span><span class="line"><span class="cl">    <span class="o">{</span>
</span></span><span class="line"><span class="cl">      <span class="s2">&#34;id&#34;</span>: <span class="s2">&#34;c4468393f926400d8880a264df9c284a&#34;</span>,
</span></span><span class="line"><span class="cl">      <span class="s2">&#34;createTime&#34;</span>: <span class="s2">&#34;2012-03-06T12:15:03.239463070Z&#34;</span>,
</span></span><span class="line"><span class="cl">      <span class="s2">&#34;updateTime&#34;</span>: <span class="s2">&#34;1990-10-29T13:12:31.224347086Z&#34;</span>,
</span></span><span class="line"><span class="cl">      <span class="s2">&#34;name&#34;</span>: <span class="s2">&#34;jexox&#34;</span>,
</span></span><span class="line"><span class="cl">      <span class="s2">&#34;type&#34;</span>: <span class="s2">&#34;USER_TYPE_STANDARD&#34;</span>,
</span></span><span class="line"><span class="cl">      <span class="s2">&#34;description&#34;</span>: <span class="s2">&#34;Tattooed taxidermy.&#34;</span>,
</span></span><span class="line"><span class="cl">      <span class="s2">&#34;url&#34;</span>: <span class="s2">&#34;http://www.productexploit.name/synergies/target&#34;</span>
</span></span><span class="line"><span class="cl">    <span class="o">}</span>,
</span></span><span class="line"><span class="cl">    <span class="o">{</span>
</span></span><span class="line"><span class="cl">      <span class="s2">&#34;id&#34;</span>: <span class="s2">&#34;0e4ca24f4ff54761b109daab0da1bea2&#34;</span>,
</span></span><span class="line"><span class="cl">      <span class="s2">&#34;createTime&#34;</span>: <span class="s2">&#34;1955-05-16T02:37:30.643378679Z&#34;</span>,
</span></span><span class="line"><span class="cl">      <span class="s2">&#34;updateTime&#34;</span>: <span class="s2">&#34;1923-08-28T04:28:43.330711919Z&#34;</span>,
</span></span><span class="line"><span class="cl">      <span class="s2">&#34;name&#34;</span>: <span class="s2">&#34;ya0&#34;</span>,
</span></span><span class="line"><span class="cl">      <span class="s2">&#34;type&#34;</span>: <span class="s2">&#34;USER_TYPE_STANDARD&#34;</span>,
</span></span><span class="line"><span class="cl">      <span class="s2">&#34;state&#34;</span>: <span class="s2">&#34;USER_STATE_INACTIVE&#34;</span>,
</span></span><span class="line"><span class="cl">      <span class="s2">&#34;description&#34;</span>: <span class="s2">&#34;Helvetica.&#34;</span>,
</span></span><span class="line"><span class="cl">      <span class="s2">&#34;url&#34;</span>: <span class="s2">&#34;https://www.centralengage.info/markets/scale/e-commerce/exploit&#34;</span>,
</span></span><span class="line"><span class="cl">      <span class="s2">&#34;verificationStatus&#34;</span>: <span class="s2">&#34;USER_VERIFICATION_STATUS_UNVERIFIED&#34;</span>
</span></span><span class="line"><span class="cl">    <span class="o">}</span>
</span></span><span class="line"><span class="cl">  <span class="o">]</span>
</span></span><span class="line"><span class="cl"><span class="o">}</span>
</span></span></code></pre></div><p>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.</p>
<h2 id="whats-left">What&rsquo;s left?</h2>
<p>FauxRPC is already great for some use cases but it&rsquo;s not &ldquo;done&rdquo; as there&rsquo;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.</p>
<h2 id="stay-tuned">Stay Tuned</h2>
<p>I made a <a href="https://fauxrpc.com/" rel="external">documentation website</a> to organize documentation. I think it looks pretty good for how quickly I threw it together. The code for FauxRPC lives on GitHub at <a href="https://github.com/sudorandom/fauxrpc" rel="external">sudorandom/fauxrpc</a>. It&rsquo;s a little thin now but there&rsquo;s a lot that I can write about in there. I&rsquo;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!</p>
<p><em>&hellip; and don&rsquo;t forget to <a href="https://github.com/sudorandom/fauxrpc" rel="external">star the repo on GitHub</a>. It helps more than you know!</em></p>
]]></content:encoded></item><item><title>gRPC Over HTTP/3</title><link>https://kmcd.dev/posts/grpc-over-http3/</link><pubDate>Tue, 09 Jul 2024 10:00:00 +0000</pubDate><guid>https://kmcd.dev/posts/grpc-over-http3/</guid><description> 
                &lt;p> &lt;img hspace="5" src="https://kmcd.dev/posts/grpc-over-http3/cover_hu_6bb864e66717cc70.webp" /> &lt;/p>
                
                Turbocharging gRPC with HTTP/3
                </description><content:encoded><![CDATA[<h2 id="introduction">Introduction</h2>
<p>At the time of writing, HTTP/3 is <a href="https://w3techs.com/technologies/details/ce-http3" rel="external">supported by 30.4% of the top 10 million websites</a>. This market penetration is astounding, but it seems like all of this progress has been possible almost exclusively by work on browsers, load balancers and CDN providers. What about the backend? How&rsquo;s HTTP/3 doing there? The answer, sadly, is not as incredible.</p>
<p>Because of this, I have been very interested in HTTP/3 in the context of gRPC. While gRPC has been instrumental in driving the adoption of HTTP/2, the same isn&rsquo;t true for HTTP/3 even though HTTP/3 promises several benefits that all seem to apply exceptionally well to gRPC services.</p>
<p>In this post, we&rsquo;ll dive into what HTTP/3 is and explore the compelling reasons why it&rsquo;s an ideal fit for gRPC applications. We&rsquo;ll uncover the technical advancements that promise to make HTTP/3 faster, more reliable, and more secure. But we won&rsquo;t stop at theory; we&rsquo;ll get our hands dirty with practical examples in Go, demonstrating how to set up and test gRPC servers and clients over HTTP/3.</p>
<p>By the end of this journey, you&rsquo;ll have a solid understanding of the benefits HTTP/3 brings to gRPC, the tools available to start using it today, and the potential it holds for the future of API development. So, fasten your seatbelts and get ready to experience the next generation of network protocols!</p>
<p>All code examples are available at <a href="https://github.com/sudorandom/example-connect-http3/blob/v0.0.1/" rel="external">sudorandom/example-connect-http3 on GitHub</a>.</p>
<h2 id="why-http3">Why HTTP/3</h2>
<p>gRPC has had a lot of success pushing the world into HTTP/2 but there are some advantages to pushing even further and adopting the new major version of HTTP, <a href="https://http3-explained.haxx.se/en" rel="external">HTTP/3</a>. Let&rsquo;s discuss these advantages before delving into code examples. So why should we use HTTP/3?</p>

    
    
        
        
            
        
        <img src="https://kmcd.dev/posts/grpc-over-http3/butwhy_hu_77b485a21172adb5.webp"
             alt="" class="center" width="400px"/>
    


<h3 id="faster-connection-establishment">Faster Connection Establishment</h3>
<p>HTTP/3 is built on <a href="https://blog.cloudflare.com/the-road-to-quic" rel="external">QUIC</a> (Quick UDP Internet Connections). You can think of it as a similar protocol to TCP but as the name suggests, it is built on top of UDP. While TCP has historically been the foundation of web communication, offering sequential packet ordering, congestion control and retransmissions, the needs of web browsers have evolved to require some different features and tradeoffs. All three main features of TCP—sequential packet ordering, congestion control, and retransmissions—can sometimes hinder the performance of modern web applications. Acknowledging these trade-offs led to the development of QUIC and HTTP/3, which both leverage UDP.</p>
<p>As the internet evolved better security practices, we&rsquo;ve layered TLS on top of TCP, so a TCP connection gets established first and then an HTTP/gRPC/etc. request can be made. This layering made it much easier to slowly adopt TLS but it has also made it slower to establish connections that need TLS because it added more round trips between the server and client before a connection can be &ldquo;established&rdquo;. Each round trip introduces latency, delaying the time it takes for a request to reach the server and receive a response. Here&rsquo;s what it looks like with HTTP/1.1 and HTTP/2:</p>
<div class="container">
  <pre class="mermaid">sequenceDiagram
    actor Client

    rect rgb(47,75,124)
        Client ->> Server: TCP SYN
        Server ->> Client: TCP SYN-ACK
        Client ->> Server: TCP ACK
    end

    rect rgb(102,81,145)
        Client ->> Server: TLS Client Hello
        Server ->> Client: TLS Server Hello
        Client ->> Server: TLS Server Finished
        Server ->> Client: TLS Client Finished
    end

    rect rgb(200,80,96)
        Client ->> Server: HTTP Request
        Server ->> Client: HTTP Response
    end
  </pre>
</div>
<p>Yes, this process involves <strong>three round trips</strong> before the client even sends a request. Viewed in this way, this seems shockingly slow to me. By combining TLS 1.3, QUIC and HTTP/3, we can do away with several of those round trips. So with HTTP/3 it typically looks like this with only a single round trip before we can issue our request:</p>
<div class="container">
  <pre class="mermaid">sequenceDiagram
	    actor Client

    rect rgb(47,75,124)
        Client ->> Server: QUIC
        Server ->> Client: QUIC
        Client ->> Server: QUIC
    end

    rect rgb(200,80,96)
        Client ->> Server: HTTP Request
        Server ->> Client: HTTP Response
    end
  </pre>
</div>
<p>But wait, we can actually go further. With a feature called <code>0-RTT</code>, the number of round trips for a new connection can actually be <strong>ZERO</strong>. The idea is that the client can presume that a connection will be established and send the request based on that presumption. 0-RTT quite literally removes the need to do a round trip before making the first request. It looks like this:</p>
<div class="container">
  <pre class="mermaid">sequenceDiagram
    actor Client

    rect rgb(47,75,124)
        Client ->> Server: QUIC
    end
    rect rgb(200,80,96)
        Client ->> Server: HTTP Request
    end

    rect rgb(47,75,124)
        Server ->> Client: QUIC
    end
    rect rgb(200,80,96)
        Server ->> Client: HTTP Response
    end
  </pre>
</div>
<p>Note that 0-RTT requires the client to have some cached information about the server so it&rsquo;s not always possible to use. Also, there are still some security concerns, mostly focused on the potential for denial of service attacks.</p>
<h3 id="head-of-line-blocking">Head-of-line Blocking</h3>
<p>HTTP/2 has allowed gRPC to be quite good at multiplexing multiple requests and streams onto a single connection. This wasn&rsquo;t possible with HTTP/1.1. However, there is an issue that can arise due to TCP&rsquo;s guarantee of delivering packets <strong>in order</strong> even if they arrive out of order. This is an issue because packets for a request could be waiting for retransmissions from another request that is using the same connection. From gRPC&rsquo;s point of view, these requests are independent things, so there&rsquo;s no need to wait but TCP does not know about these separate streams, so we end up experiencing a so-called <a href="https://en.wikipedia.org/wiki/Head-of-line_blocking" rel="external">head-of-line blocking</a> issue.</p>

    
    
        
        
            
        
        <img src="https://kmcd.dev/posts/grpc-over-http3/holb_hu_f5a1f7cbcfe023f4.webp"
             alt="" class="center" width="400px"/>
    


<p>HTTP/3 solves the head-of-line blocking issue by avoiding TCP altogether. Instead, it is built on top of a protocol called QUIC which is built on top of UDP. QUIC is aware of multiple streams so it knows when it is appropriate to deliver packets without having this head-of-line blocking behavior. This makes HTTP/3 much better when dealing with unreliable networks. With its heavy use of streams, gRPC in particular would greatly benefit from the elimination of the head-of-line blocking issue.</p>
<h3 id="encryption-isrequired">Encryption is <em>Required</em></h3>
<p>When creating HTTP/2 there was a lot of disagreement over if it should require TLS. Many have argued that this requirement would hurt the adoption of HTTP/2. Further, they argue that HTTP/2 offers a lot of benefits that can be attained without the use of TLS. As a result, we now have <a href="https://datatracker.ietf.org/doc/html/rfc7540#section-3.2" rel="external">h2c</a>, which is a way to use HTTP/2 without encryption.</p>

    
    
        
        
            
        
        <img src="https://kmcd.dev/posts/grpc-over-http3/encryption_hu_53b737659b43b13f.webp"
             alt="" class="center" width="400px"/>
    


<p>These arguments might have worked for HTTP/2 but they didn&rsquo;t hold up for HTTP/3. HTTP/3 <em>requires</em> encryption and usage of the TLS 1.3 protocol. There are several reasons for this, but the most compelling one is that TLS 1.3 working with QUIC allows for fewer (and sometimes zero) round trips to negotiate a new connection before making a request, which is even faster than HTTP/1.1 without TLS. It&rsquo;s a double win by being both more secure and, theoretically, much faster.</p>
<h2 id="experimentation">Experimentation</h2>
<p>When researching this topic, I discovered that HTTP/3 with gRPC is a bit of a complicated story. I covered this a bit in my <a href="https://kmcd.dev/posts/grpc-the-good-parts/">gRPC: The Good Parts</a> post but the gist is that no decision has been made to officially support HTTP/3 throughout the entire gRPC ecosystem. There is an <a href="https://github.com/grpc/grpc/issues/19126" rel="external">open issue</a> to discuss this and there is an <a href="https://github.com/grpc/proposal/blob/master/G2-http3-protocol.md" rel="external">official proposal</a> also discussing the idea. Some projects are closer to HTTP/3 than others, so here are the implementations that I have found where you can make gRPC over HTTP/3 work:</p>
<ul>
<li><strong>C#</strong>: <a href="https://devblogs.microsoft.com/dotnet/http-3-support-in-dotnet-6/#grpc-with-http-3" rel="external">grpc-dotnet</a> is the pioneer here, already containing an implementation of an HTTP/3 transport for gRPC.</li>
<li><strong>Rust</strong>: <a href="https://github.com/hyperium/tonic/issues/339" rel="external">Tonic with the Hyper transport</a> appears to be able to support this, although I&rsquo;m not sure if there are good examples of this in the wild yet.</li>
<li><strong>Go</strong>: <a href="https://github.com/connectrpc/connect-go" rel="external">ConnectRPC</a> for Go uses the standard library http.Handlers, so any http server implementation can be used, including the transport available in <a href="https://github.com/quic-go/quic-go" rel="external">quic-go</a>.</li>
<li><strong>Cronet</strong>: <a href="https://developer.android.com/develop/connectivity/cronet" rel="external">Cronet</a>, designed primarily for mobile clients (Android and iOS), provides a way to utilize Chrome&rsquo;s network stack, including its QUIC and HTTP/3 support. This can be particularly useful for building gRPC clients with HTTP/3 capabilities in mobile environments.</li>
</ul>
<p>If you know about other gRPC implementations that can work with HTTP/3 (client or server), let me know. These are only the ones I know about.</p>
<p>When learning something new I always like getting my hands dirty by using it. I feel like this is the best way to learn. Since Go is my current working language, I decided to explore HTTP/3 using ConnectRPC in Go. I&rsquo;m including the full examples in this article because I&rsquo;ve looked and haven&rsquo;t really been able to find good examples for this. I hope it&rsquo;s helpful for others. The full working examples are on <a href="https://github.com/sudorandom/example-connect-http3/" rel="external">a git repo</a> that I made for this post. Examples will have things like imports omitted for brevity but the full source is linked under each example.</p>
<h3 id="example-server-in-go">Example server in Go</h3>
<p>Let&rsquo;s explore how easy it is to set up a gRPC server with HTTP/3 support using Go. The key players here are:</p>
<ul>
<li><a href="https://github.com/quic-go/quic-go" rel="external">quic-go</a>: This library provides a robust implementation of the QUIC protocol in Go, enabling HTTP/3 communication.</li>
<li><a href="https://github.com/connectrpc/connect-go" rel="external">ConnectRPC</a>: connect-go, the Go implementation of ConnectRPC allows us to define gRPC services using familiar Go HTTP handlers (http.Handler), which greatly simplifies the integration process.</li>
</ul>
<p>I am using the <a href="https://buf.build/connectrpc/eliza" rel="external">Eliza service</a> as the service to implement with ConnectRPC. This is a simple service intended to help demonstrate ConnectRPC. The implementation is un-important, so I omitted it but my implementation just echos back whatever the user sends.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="cl"><span class="kd">func</span><span class="w"> </span><span class="nf">main</span><span class="p">()</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">	</span><span class="nx">mux</span><span class="w"> </span><span class="o">:=</span><span class="w"> </span><span class="nx">http</span><span class="p">.</span><span class="nf">NewServeMux</span><span class="p">()</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">	</span><span class="c1">// Implementation is only in the full source</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">	</span><span class="nx">mux</span><span class="p">.</span><span class="nf">Handle</span><span class="p">(</span><span class="nx">elizav1connect</span><span class="p">.</span><span class="nf">NewElizaServiceHandler</span><span class="p">(</span><span class="o">&amp;</span><span class="nx">server</span><span class="p">{}))</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">	</span><span class="nx">addr</span><span class="w"> </span><span class="o">:=</span><span class="w"> </span><span class="s">&#34;127.0.0.1:6660&#34;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">	</span><span class="nx">log</span><span class="p">.</span><span class="nf">Printf</span><span class="p">(</span><span class="s">&#34;Starting connectrpc on %s&#34;</span><span class="p">,</span><span class="w"> </span><span class="nx">addr</span><span class="p">)</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">	</span><span class="nx">h3srv</span><span class="w"> </span><span class="o">:=</span><span class="w"> </span><span class="nx">http3</span><span class="p">.</span><span class="nx">Server</span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">		</span><span class="nx">Addr</span><span class="p">:</span><span class="w">    </span><span class="nx">addr</span><span class="p">,</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">		</span><span class="nx">Handler</span><span class="p">:</span><span class="w"> </span><span class="nx">mux</span><span class="p">,</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">	</span><span class="p">}</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">	</span><span class="k">if</span><span class="w"> </span><span class="nx">err</span><span class="w"> </span><span class="o">:=</span><span class="w"> </span><span class="nx">h3srv</span><span class="p">.</span><span class="nf">ListenAndServeTLS</span><span class="p">(</span><span class="s">&#34;cert.crt&#34;</span><span class="p">,</span><span class="w"> </span><span class="s">&#34;cert.key&#34;</span><span class="p">);</span><span class="w"> </span><span class="nx">err</span><span class="w"> </span><span class="o">!=</span><span class="w"> </span><span class="kc">nil</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">		</span><span class="nx">log</span><span class="p">.</span><span class="nf">Fatalf</span><span class="p">(</span><span class="s">&#34;error: %s&#34;</span><span class="p">,</span><span class="w"> </span><span class="nx">err</span><span class="p">)</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">	</span><span class="p">}</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="p">}</span></span></span></code></pre></div>
<aside><a href="https://github.com/sudorandom/example-connect-http3/blob/v0.0.1/server-single/main.go" target="_blank">See the full source at GitHub.</a>
</aside>

<p>That&rsquo;s it! With this minimal setup, we now have a gRPC server that supports HTTP/3! I just used an <code>http3.Server</code> instance instead of <code>http.Server</code>. <code>http3.Server</code> does have different options related to the differences between QUIC and TCP. Note that it is possible to run an <code>http.Server</code> alongside <code>http3.Server</code> using the same address with the same port. How is this possible? Because HTTP/3 uses UDP and the ports are completely separate from TCP ports. Here&rsquo;s an example:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="cl"><span class="kd">func</span><span class="w"> </span><span class="nf">main</span><span class="p">()</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">	</span><span class="nx">mux</span><span class="w"> </span><span class="o">:=</span><span class="w"> </span><span class="nx">http</span><span class="p">.</span><span class="nf">NewServeMux</span><span class="p">()</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">	</span><span class="nx">mux</span><span class="p">.</span><span class="nf">Handle</span><span class="p">(</span><span class="nx">elizav1connect</span><span class="p">.</span><span class="nf">NewElizaServiceHandler</span><span class="p">(</span><span class="o">&amp;</span><span class="nx">server</span><span class="p">{}))</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">	</span><span class="nx">addr</span><span class="w"> </span><span class="o">:=</span><span class="w"> </span><span class="s">&#34;127.0.0.1:6660&#34;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">	</span><span class="nx">log</span><span class="p">.</span><span class="nf">Printf</span><span class="p">(</span><span class="s">&#34;Starting connectrpc on %s&#34;</span><span class="p">,</span><span class="w"> </span><span class="nx">addr</span><span class="p">)</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">	</span><span class="nx">h3srv</span><span class="w"> </span><span class="o">:=</span><span class="w"> </span><span class="nx">http3</span><span class="p">.</span><span class="nx">Server</span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">		</span><span class="nx">Addr</span><span class="p">:</span><span class="w">    </span><span class="nx">addr</span><span class="p">,</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">		</span><span class="nx">Handler</span><span class="p">:</span><span class="w"> </span><span class="nx">mux</span><span class="p">,</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">	</span><span class="p">}</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">	</span><span class="nx">srv</span><span class="w"> </span><span class="o">:=</span><span class="w"> </span><span class="nx">http</span><span class="p">.</span><span class="nx">Server</span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">		</span><span class="nx">Addr</span><span class="p">:</span><span class="w">    </span><span class="nx">addr</span><span class="p">,</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">		</span><span class="nx">Handler</span><span class="p">:</span><span class="w"> </span><span class="nx">h2c</span><span class="p">.</span><span class="nf">NewHandler</span><span class="p">(</span><span class="nx">mux</span><span class="p">,</span><span class="w"> </span><span class="o">&amp;</span><span class="nx">http2</span><span class="p">.</span><span class="nx">Server</span><span class="p">{}),</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">	</span><span class="p">}</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">	</span><span class="nx">eg</span><span class="p">,</span><span class="w"> </span><span class="nx">_</span><span class="w"> </span><span class="o">:=</span><span class="w"> </span><span class="nx">errgroup</span><span class="p">.</span><span class="nf">WithContext</span><span class="p">(</span><span class="nx">context</span><span class="p">.</span><span class="nf">Background</span><span class="p">())</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">	</span><span class="nx">eg</span><span class="p">.</span><span class="nf">Go</span><span class="p">(</span><span class="kd">func</span><span class="p">()</span><span class="w"> </span><span class="kt">error</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">		</span><span class="k">return</span><span class="w"> </span><span class="nx">h3srv</span><span class="p">.</span><span class="nf">ListenAndServeTLS</span><span class="p">(</span><span class="s">&#34;cert.crt&#34;</span><span class="p">,</span><span class="w"> </span><span class="s">&#34;cert.key&#34;</span><span class="p">)</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">	</span><span class="p">})</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">	</span><span class="nx">eg</span><span class="p">.</span><span class="nf">Go</span><span class="p">(</span><span class="kd">func</span><span class="p">()</span><span class="w"> </span><span class="kt">error</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">		</span><span class="k">return</span><span class="w"> </span><span class="nx">srv</span><span class="p">.</span><span class="nf">ListenAndServeTLS</span><span class="p">(</span><span class="s">&#34;cert.crt&#34;</span><span class="p">,</span><span class="w"> </span><span class="s">&#34;cert.key&#34;</span><span class="p">)</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">	</span><span class="p">})</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">	</span><span class="k">if</span><span class="w"> </span><span class="nx">err</span><span class="w"> </span><span class="o">:=</span><span class="w"> </span><span class="nx">eg</span><span class="p">.</span><span class="nf">Wait</span><span class="p">();</span><span class="w"> </span><span class="nx">err</span><span class="w"> </span><span class="o">!=</span><span class="w"> </span><span class="kc">nil</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">		</span><span class="nx">log</span><span class="p">.</span><span class="nf">Fatalf</span><span class="p">(</span><span class="s">&#34;error: %s&#34;</span><span class="p">,</span><span class="w"> </span><span class="nx">err</span><span class="p">)</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">	</span><span class="p">}</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="p">}</span><span class="w">
</span></span></span></code></pre></div><aside><a href="https://github.com/sudorandom/example-connect-http3/blob/v0.0.1/server-multi/main.go" target="_blank">See the full source at GitHub.</a>
</aside>

<p>This code demonstrates running multiple HTTP servers concurrently, providing support for HTTP/1.1, HTTP/2, and HTTP/3 on the same port by leveraging TCP for HTTP/1.1 and HTTP/2 and UDP for HTTP/3.</p>
<p>Oh, and note that this requires a certificate and a key because HTTP/3 <strong>requires</strong> TLS. Here&rsquo;s the command that I used to create a self-signed cert for testing:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-shell" data-lang="shell"><span class="line"><span class="cl">openssl req -new -newkey rsa:4096 -days <span class="m">365</span> -nodes -x509 <span class="se">\
</span></span></span><span class="line"><span class="cl"><span class="se"></span>    -subj <span class="s2">&#34;/C=DK/L=Copenhagen/O=kmcd/CN=local.kmcd.dev&#34;</span> <span class="se">\
</span></span></span><span class="line"><span class="cl"><span class="se"></span>    -keyout cert.key  -out cert.crt
</span></span></code></pre></div><p>That&rsquo;s it! With just a few lines of code, we now have a gRPC server that supports HTTP/3!</p>
<h4 id="testing-with-an-example-client-in-go">Testing with an example client in Go</h4>
<p>Now that we have a server running we need some way to test it, so let&rsquo;s use the standard library <code>http.Client</code> with some quic-go magic sprinkled on top:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="cl"><span class="kd">const</span><span class="w"> </span><span class="p">(</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">	</span><span class="nx">url</span><span class="w">        </span><span class="p">=</span><span class="w"> </span><span class="s">&#34;https://127.0.0.1:6660/connectrpc.eliza.v1.ElizaService/Say&#34;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">	</span><span class="nx">reqBody</span><span class="w"> </span><span class="p">=</span><span class="w"> </span><span class="s">`{&#34;sentence&#34;: &#34;Hello World!&#34;}`</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="p">)</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="kd">func</span><span class="w"> </span><span class="nf">main</span><span class="p">()</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">	</span><span class="nx">roundTripper</span><span class="w"> </span><span class="o">:=</span><span class="w"> </span><span class="o">&amp;</span><span class="nx">http3</span><span class="p">.</span><span class="nx">RoundTripper</span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">		</span><span class="nx">TLSClientConfig</span><span class="p">:</span><span class="w"> </span><span class="o">&amp;</span><span class="nx">tls</span><span class="p">.</span><span class="nx">Config</span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">			</span><span class="c1">// we need this because our certificate is self signed</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">			</span><span class="nx">InsecureSkipVerify</span><span class="p">:</span><span class="w"> </span><span class="kc">true</span><span class="p">,</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">		</span><span class="p">},</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">	</span><span class="p">}</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">	</span><span class="k">defer</span><span class="w"> </span><span class="nx">roundTripper</span><span class="p">.</span><span class="nf">Close</span><span class="p">()</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">	</span><span class="nx">client</span><span class="w"> </span><span class="o">:=</span><span class="w"> </span><span class="o">&amp;</span><span class="nx">http</span><span class="p">.</span><span class="nx">Client</span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">		</span><span class="nx">Transport</span><span class="p">:</span><span class="w"> </span><span class="nx">roundTripper</span><span class="p">,</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">	</span><span class="p">}</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">	</span><span class="nx">log</span><span class="p">.</span><span class="nf">Println</span><span class="p">(</span><span class="s">&#34;connect: &#34;</span><span class="p">,</span><span class="w"> </span><span class="nx">url</span><span class="p">)</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">	</span><span class="nx">log</span><span class="p">.</span><span class="nf">Println</span><span class="p">(</span><span class="s">&#34;send: &#34;</span><span class="p">,</span><span class="w"> </span><span class="nx">reqBody</span><span class="p">)</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">	</span><span class="nx">req</span><span class="p">,</span><span class="w"> </span><span class="nx">err</span><span class="w"> </span><span class="o">:=</span><span class="w"> </span><span class="nx">http</span><span class="p">.</span><span class="nf">NewRequest</span><span class="p">(</span><span class="s">&#34;POST&#34;</span><span class="p">,</span><span class="w"> </span><span class="nx">url</span><span class="p">,</span><span class="w"> </span><span class="nx">strings</span><span class="p">.</span><span class="nf">NewReader</span><span class="p">(</span><span class="nx">reqBody</span><span class="p">))</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">	</span><span class="k">if</span><span class="w"> </span><span class="nx">err</span><span class="w"> </span><span class="o">!=</span><span class="w"> </span><span class="kc">nil</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">		</span><span class="nx">log</span><span class="p">.</span><span class="nf">Fatalf</span><span class="p">(</span><span class="s">&#34;error: %s&#34;</span><span class="p">,</span><span class="w"> </span><span class="nx">err</span><span class="p">)</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">	</span><span class="p">}</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">	</span><span class="nx">req</span><span class="p">.</span><span class="nx">Header</span><span class="p">.</span><span class="nf">Add</span><span class="p">(</span><span class="s">&#34;Content-Type&#34;</span><span class="p">,</span><span class="w"> </span><span class="s">&#34;application/json&#34;</span><span class="p">)</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">	</span><span class="nx">resp</span><span class="p">,</span><span class="w"> </span><span class="nx">err</span><span class="w"> </span><span class="o">:=</span><span class="w"> </span><span class="nx">client</span><span class="p">.</span><span class="nf">Do</span><span class="p">(</span><span class="nx">req</span><span class="p">)</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">	</span><span class="k">if</span><span class="w"> </span><span class="nx">err</span><span class="w"> </span><span class="o">!=</span><span class="w"> </span><span class="kc">nil</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">		</span><span class="nx">log</span><span class="p">.</span><span class="nf">Fatalf</span><span class="p">(</span><span class="s">&#34;error: %s&#34;</span><span class="p">,</span><span class="w"> </span><span class="nx">err</span><span class="p">)</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">	</span><span class="p">}</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">	</span><span class="nx">body</span><span class="p">,</span><span class="w"> </span><span class="nx">err</span><span class="w"> </span><span class="o">:=</span><span class="w"> </span><span class="nx">io</span><span class="p">.</span><span class="nf">ReadAll</span><span class="p">(</span><span class="nx">resp</span><span class="p">.</span><span class="nx">Body</span><span class="p">)</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">	</span><span class="k">if</span><span class="w"> </span><span class="nx">err</span><span class="w"> </span><span class="o">!=</span><span class="w"> </span><span class="kc">nil</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">		</span><span class="nx">log</span><span class="p">.</span><span class="nf">Fatalf</span><span class="p">(</span><span class="s">&#34;error: %s&#34;</span><span class="p">,</span><span class="w"> </span><span class="nx">err</span><span class="p">)</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">	</span><span class="p">}</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">	</span><span class="k">defer</span><span class="w"> </span><span class="nx">resp</span><span class="p">.</span><span class="nx">Body</span><span class="p">.</span><span class="nf">Close</span><span class="p">()</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">	</span><span class="nx">log</span><span class="p">.</span><span class="nf">Println</span><span class="p">(</span><span class="s">&#34;recv: &#34;</span><span class="p">,</span><span class="w"> </span><span class="nb">string</span><span class="p">(</span><span class="nx">body</span><span class="p">))</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="p">}</span><span class="w">
</span></span></span></code></pre></div><aside><a href="https://github.com/sudorandom/example-connect-http3/blob/v0.0.1/client-http/main.go" target="_blank">See the full source at GitHub.</a>
</aside>

<p>In the case of the client, we only need to define a <code>http3.RoundTripper</code> instance and pass that into a completely normal <code>http.Client</code> instance. That&rsquo;s&hellip; quite literally it. Everything else should be the same.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-shell" data-lang="shell"><span class="line"><span class="cl">2024/07/06 12:57:24 connect:  https://127.0.0.1:6660/connectrpc.eliza.v1.ElizaService/Say
</span></span><span class="line"><span class="cl">2024/07/06 12:57:24 send:  <span class="o">{</span><span class="s2">&#34;sentence&#34;</span>: <span class="s2">&#34;Hello World!&#34;</span><span class="o">}</span>
</span></span><span class="line"><span class="cl">2024/07/06 12:57:24 recv:  <span class="o">{</span><span class="s2">&#34;sentence&#34;</span>:<span class="s2">&#34;Hello World!&#34;</span><span class="o">}</span>
</span></span></code></pre></div><p>This works as expected! In this next example, I&rsquo;m calling the service using a client built with ConnectRPC, demonstrating how it can work seamlessly over HTTP/3 as well, just by configuring <code>http.Client</code> a little differently.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="cl"><span class="kd">const</span><span class="w"> </span><span class="nx">url</span><span class="w"> </span><span class="p">=</span><span class="w"> </span><span class="s">&#34;https://127.0.0.1:6660&#34;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="kd">var</span><span class="w"> </span><span class="nx">reqBody</span><span class="w"> </span><span class="p">=</span><span class="w"> </span><span class="o">&amp;</span><span class="nx">elizav1</span><span class="p">.</span><span class="nx">SayRequest</span><span class="p">{</span><span class="nx">Sentence</span><span class="p">:</span><span class="w"> </span><span class="s">&#34;Hello World!&#34;</span><span class="p">}</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="kd">func</span><span class="w"> </span><span class="nf">main</span><span class="p">()</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">	</span><span class="nx">roundTripper</span><span class="w"> </span><span class="o">:=</span><span class="w"> </span><span class="o">&amp;</span><span class="nx">http3</span><span class="p">.</span><span class="nx">RoundTripper</span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">		</span><span class="nx">TLSClientConfig</span><span class="p">:</span><span class="w"> </span><span class="o">&amp;</span><span class="nx">tls</span><span class="p">.</span><span class="nx">Config</span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">			</span><span class="nx">InsecureSkipVerify</span><span class="p">:</span><span class="w"> </span><span class="kc">true</span><span class="p">,</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">		</span><span class="p">},</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">	</span><span class="p">}</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">	</span><span class="k">defer</span><span class="w"> </span><span class="nx">roundTripper</span><span class="p">.</span><span class="nf">Close</span><span class="p">()</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">	</span><span class="nx">client</span><span class="w"> </span><span class="o">:=</span><span class="w"> </span><span class="o">&amp;</span><span class="nx">http</span><span class="p">.</span><span class="nx">Client</span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">		</span><span class="nx">Transport</span><span class="p">:</span><span class="w"> </span><span class="nx">roundTripper</span><span class="p">,</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">	</span><span class="p">}</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">	</span><span class="nx">svcClient</span><span class="w"> </span><span class="o">:=</span><span class="w"> </span><span class="nx">elizav1connect</span><span class="p">.</span><span class="nf">NewElizaServiceClient</span><span class="p">(</span><span class="nx">client</span><span class="p">,</span><span class="w"> </span><span class="nx">url</span><span class="p">,</span><span class="w"> </span><span class="nx">connect</span><span class="p">.</span><span class="nf">WithGRPC</span><span class="p">())</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">	</span><span class="nx">log</span><span class="p">.</span><span class="nf">Println</span><span class="p">(</span><span class="s">&#34;connect: &#34;</span><span class="p">,</span><span class="w"> </span><span class="nx">url</span><span class="p">)</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">	</span><span class="nx">log</span><span class="p">.</span><span class="nf">Println</span><span class="p">(</span><span class="s">&#34;send: &#34;</span><span class="p">,</span><span class="w"> </span><span class="nx">reqBody</span><span class="p">)</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">	</span><span class="nx">resp</span><span class="p">,</span><span class="w"> </span><span class="nx">err</span><span class="w"> </span><span class="o">:=</span><span class="w"> </span><span class="nx">svcClient</span><span class="p">.</span><span class="nf">Say</span><span class="p">(</span><span class="nx">context</span><span class="p">.</span><span class="nf">Background</span><span class="p">(),</span><span class="w"> </span><span class="nx">connect</span><span class="p">.</span><span class="nf">NewRequest</span><span class="p">(</span><span class="nx">reqBody</span><span class="p">))</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">	</span><span class="k">if</span><span class="w"> </span><span class="nx">err</span><span class="w"> </span><span class="o">!=</span><span class="w"> </span><span class="kc">nil</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">		</span><span class="nx">log</span><span class="p">.</span><span class="nf">Fatalf</span><span class="p">(</span><span class="s">&#34;error: %s&#34;</span><span class="p">,</span><span class="w"> </span><span class="nx">err</span><span class="p">)</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">	</span><span class="p">}</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">	</span><span class="nx">log</span><span class="p">.</span><span class="nf">Println</span><span class="p">(</span><span class="s">&#34;recv: &#34;</span><span class="p">,</span><span class="w"> </span><span class="nx">resp</span><span class="p">.</span><span class="nx">Msg</span><span class="p">)</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="p">}</span><span class="w">
</span></span></span></code></pre></div><aside><a href="https://github.com/sudorandom/example-connect-http3/blob/v0.0.1/client-connect/main.go" target="_blank">See the full source at GitHub.</a>
</aside>

<p>It works the same, but the output is a bit different since protobuf types print slightly differently from JSON:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-shell" data-lang="shell"><span class="line"><span class="cl">2024/07/06 12:57:12 connect:  https://127.0.0.1:6660
</span></span><span class="line"><span class="cl">2024/07/06 12:57:12 send:  sentence:<span class="s2">&#34;Hello World!&#34;</span>
</span></span><span class="line"><span class="cl">2024/07/06 12:57:14 recv:  sentence:<span class="s2">&#34;Hello World!&#34;</span>
</span></span></code></pre></div><p>Now we have a server and a client that talks HTTP/3! Amazing!</p>
<h4 id="testing-http3-servers-with-curl">Testing HTTP/3 servers with curl</h4>
<p>I wanted to test this with a &ldquo;real&rdquo; HTTP/3 client because maybe there are some weird issues where quic-go servers only work against quic-go clients. To be extra sure, I wanted to validate this further with a well-established HTTP/3 client. So I reached for <code>curl</code>, the de-facto tool for calling HTTP APIs from the command line. So I immediately tried to just run <code>curl --http3</code>:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-shell" data-lang="shell"><span class="line"><span class="cl">$ curl <span class="se">\
</span></span></span><span class="line"><span class="cl"><span class="se"></span>  --json <span class="s1">&#39;{&#34;sentence&#34;: &#34;Hello World!&#34;}&#39;</span> <span class="se">\
</span></span></span><span class="line"><span class="cl"><span class="se"></span>  --http3 -k -v <span class="se">\
</span></span></span><span class="line"><span class="cl"><span class="se"></span>  https://127.0.0.1:6660/connectrpc.eliza.v1.ElizaService/Say
</span></span><span class="line"><span class="cl">curl: option --http3: the installed libcurl version doesn<span class="s1">&#39;t support this
</span></span></span><span class="line"><span class="cl"><span class="s1">curl: try &#39;</span>curl --help<span class="s1">&#39; or &#39;</span>curl --manual<span class="err">&#39;</span> <span class="k">for</span> more information
</span></span></code></pre></div>
    
    
        
        
            
        
        <img src="https://kmcd.dev/posts/grpc-over-http3/suprised_hu_a42315c900e59c3f.webp"
             alt="" class="center" width="400px"/>
    


<p>Wait, what? What&rsquo;s happening?? I thought curl supported HTTP/3! What gives? Well, the story <a href="https://daniel.haxx.se/blog/2024/06/10/http-3-in-curl-mid-2024/" rel="external">isn&rsquo;t that simple</a>. The curl CLI might support HTTP/3 but the libraries that it uses also need to be on the correct version to make it all work. To get around that, I installed curl using <a href="https://blog.cloudflare.com/http3-the-past-present-and-future#using-curl" rel="external">Cloudflare&rsquo;s homebrew formula</a> which will install everything needed to get curl to work with HTTP/3. So let&rsquo;s try it out:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-shell" data-lang="shell"><span class="line"><span class="cl">$ curl <span class="se">\
</span></span></span><span class="line"><span class="cl"><span class="se"></span>  --json <span class="s1">&#39;{&#34;sentence&#34;: &#34;Hello World!&#34;}&#39;</span> <span class="se">\
</span></span></span><span class="line"><span class="cl"><span class="se"></span>  --http3 -k -v <span class="se">\
</span></span></span><span class="line"><span class="cl"><span class="se"></span>  https://127.0.0.1:6660/connectrpc.eliza.v1.ElizaService/Say
</span></span><span class="line"><span class="cl">*   Trying 127.0.0.1:6660...
</span></span><span class="line"><span class="cl">* Server certificate:
</span></span><span class="line"><span class="cl">*  subject: <span class="nv">C</span><span class="o">=</span>DK<span class="p">;</span> <span class="nv">L</span><span class="o">=</span>Copenhagen<span class="p">;</span> <span class="nv">O</span><span class="o">=</span>kmcd<span class="p">;</span> <span class="nv">CN</span><span class="o">=</span>local.kmcd.dev
</span></span><span class="line"><span class="cl">*  start date: Jul  <span class="m">6</span> 06:14:48 <span class="m">2024</span> GMT
</span></span><span class="line"><span class="cl">*  expire date: Jul  <span class="m">6</span> 06:14:48 <span class="m">2025</span> GMT
</span></span><span class="line"><span class="cl">*  issuer: <span class="nv">C</span><span class="o">=</span>DK<span class="p">;</span> <span class="nv">L</span><span class="o">=</span>Copenhagen<span class="p">;</span> <span class="nv">O</span><span class="o">=</span>kmcd<span class="p">;</span> <span class="nv">CN</span><span class="o">=</span>local.kmcd.dev
</span></span><span class="line"><span class="cl">*  SSL certificate verify result: self signed certificate <span class="o">(</span>18<span class="o">)</span>, continuing anyway.
</span></span><span class="line"><span class="cl">* Connected to 127.0.0.1 <span class="o">(</span>127.0.0.1<span class="o">)</span> port <span class="m">6660</span>
</span></span><span class="line"><span class="cl">* using HTTP/3
</span></span><span class="line"><span class="cl">* <span class="o">[</span>HTTP/3<span class="o">]</span> <span class="o">[</span>0<span class="o">]</span> OPENED stream <span class="k">for</span> https://127.0.0.1:6660/connectrpc.eliza.v1.ElizaService/Say
</span></span><span class="line"><span class="cl">* <span class="o">[</span>HTTP/3<span class="o">]</span> <span class="o">[</span>0<span class="o">]</span> <span class="o">[</span>:method: POST<span class="o">]</span>
</span></span><span class="line"><span class="cl">* <span class="o">[</span>HTTP/3<span class="o">]</span> <span class="o">[</span>0<span class="o">]</span> <span class="o">[</span>:scheme: https<span class="o">]</span>
</span></span><span class="line"><span class="cl">* <span class="o">[</span>HTTP/3<span class="o">]</span> <span class="o">[</span>0<span class="o">]</span> <span class="o">[</span>:authority: 127.0.0.1:6660<span class="o">]</span>
</span></span><span class="line"><span class="cl">* <span class="o">[</span>HTTP/3<span class="o">]</span> <span class="o">[</span>0<span class="o">]</span> <span class="o">[</span>:path: /connectrpc.eliza.v1.ElizaService/Say<span class="o">]</span>
</span></span><span class="line"><span class="cl">* <span class="o">[</span>HTTP/3<span class="o">]</span> <span class="o">[</span>0<span class="o">]</span> <span class="o">[</span>user-agent: curl/8.9.0-DEV<span class="o">]</span>
</span></span><span class="line"><span class="cl">* <span class="o">[</span>HTTP/3<span class="o">]</span> <span class="o">[</span>0<span class="o">]</span> <span class="o">[</span>content-type: application/json<span class="o">]</span>
</span></span><span class="line"><span class="cl">* <span class="o">[</span>HTTP/3<span class="o">]</span> <span class="o">[</span>0<span class="o">]</span> <span class="o">[</span>accept: application/json<span class="o">]</span>
</span></span><span class="line"><span class="cl">* <span class="o">[</span>HTTP/3<span class="o">]</span> <span class="o">[</span>0<span class="o">]</span> <span class="o">[</span>content-length: 28<span class="o">]</span>
</span></span><span class="line"><span class="cl">&gt; POST /connectrpc.eliza.v1.ElizaService/Say HTTP/3
</span></span><span class="line"><span class="cl">&gt; Host: 127.0.0.1:6660
</span></span><span class="line"><span class="cl">&gt; User-Agent: curl/8.9.0-DEV
</span></span><span class="line"><span class="cl">&gt; Content-Type: application/json
</span></span><span class="line"><span class="cl">&gt; Accept: application/json
</span></span><span class="line"><span class="cl">&gt; Content-Length: <span class="m">28</span>
</span></span><span class="line"><span class="cl">&gt;
</span></span><span class="line"><span class="cl">* upload completely sent off: <span class="m">28</span> bytes
</span></span><span class="line"><span class="cl">&lt; HTTP/3 <span class="m">200</span>
</span></span><span class="line"><span class="cl">&lt; content-type: application/json
</span></span><span class="line"><span class="cl">&lt; accept-encoding: gzip
</span></span><span class="line"><span class="cl">&lt; date: Sat, <span class="m">06</span> Jul <span class="m">2024</span> 13:17:12 GMT
</span></span><span class="line"><span class="cl">&lt; content-length: <span class="m">27</span>
</span></span><span class="line"><span class="cl">&lt;
</span></span><span class="line"><span class="cl">* Connection <span class="c1">#0 to host 127.0.0.1 left intact</span>
</span></span><span class="line"><span class="cl"><span class="o">{</span><span class="s2">&#34;sentence&#34;</span>:<span class="s2">&#34;Hello World!&#34;</span><span class="o">}</span>
</span></span></code></pre></div><p>Success! You can see that HTTP/3 is being used by inspecting the verbose logging. This flexes our server with HTTP/3 but you may be wondering what this has to do with gRPC because I&rsquo;m only using basic HTTP requests with JSON and you&rsquo;re totally right. The previous examples only leverage one of the three protocols that ConnectRPC provides by default, <a href="https://connectrpc.com/docs/protocol/" rel="external">the connect protocol</a>. Thus far, I haven&rsquo;t validated if the other two protocols, gRPC or gRPC-Web, really work using this transport. To test that, we&rsquo;ll need more gRPC-centric tooling.</p>
<h4 id="adding-http3-to-the-buf-cli">Adding HTTP/3 to the Buf CLI</h4>
<p>I also wanted to test with gRPC-specific tooling to ensure gRPC and gRPC-Web worked as expected. So I added support for HTTP/3 with the buf CLI because it supports calling services using <a href="https://connectrpc.com/docs/protocol/" rel="external">Connect</a>, <a href="https://github.com/grpc/grpc/blob/master/doc/PROTOCOL-HTTP2.md" rel="external">gRPC</a> and <a href="https://github.com/grpc/grpc/blob/master/doc/PROTOCOL-WEB.md" rel="external">gRPC-Web</a>. This ended up being pretty easy and looks similar to the ConnectRPC example client above. I have an <a href="https://github.com/bufbuild/buf/pull/3127" rel="external">open PR here</a> and if you want a sneak peak you can build the Buf CLI from <a href="https://github.com/sudorandom/buf/tree/http3" rel="external">my branch</a>. Below, I show how I tested this new feature with ConnectRPC&rsquo;s demo website:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-shell" data-lang="shell"><span class="line"><span class="cl">$ buf curl <span class="se">\
</span></span></span><span class="line"><span class="cl"><span class="se"></span>    --http3 <span class="se">\
</span></span></span><span class="line"><span class="cl"><span class="se"></span>    --schema<span class="o">=</span>buf.build/connectrpc/eliza <span class="se">\
</span></span></span><span class="line"><span class="cl"><span class="se"></span>    -d <span class="s1">&#39;{&#34;sentence&#34;: &#34;Hello world!&#34;}&#39;</span> <span class="se">\
</span></span></span><span class="line"><span class="cl"><span class="se"></span>    https://demo.connectrpc.com/connectrpc.eliza.v1.ElizaService/Say
</span></span><span class="line"><span class="cl"><span class="o">{</span>
</span></span><span class="line"><span class="cl">  <span class="s2">&#34;sentence&#34;</span>: <span class="s2">&#34;Hello...I&#39;m glad you could drop by today.&#34;</span>
</span></span><span class="line"><span class="cl"><span class="o">}</span>
</span></span></code></pre></div><p>Success! HTTP/3 works now! When I tested this, I was surprised that <a href="https://demo.connectrpc.com" rel="external">demo.connectrpc.com</a> actually supported HTTP/3, but it does! By default, <code>buf curl</code> will use the connect protocol, so we didn&rsquo;t test anything new. So let&rsquo;s try gRPC-Web next:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-shell" data-lang="shell"><span class="line"><span class="cl"><span class="nv">$buf</span> curl <span class="se">\
</span></span></span><span class="line"><span class="cl"><span class="se"></span>    --http3 <span class="se">\
</span></span></span><span class="line"><span class="cl"><span class="se"></span>    --protocol<span class="o">=</span>grpcweb <span class="se">\
</span></span></span><span class="line"><span class="cl"><span class="se"></span>    --schema<span class="o">=</span>buf.build/connectrpc/eliza <span class="se">\
</span></span></span><span class="line"><span class="cl"><span class="se"></span>    -d <span class="s1">&#39;{&#34;sentence&#34;: &#34;Hello world!&#34;}&#39;</span> <span class="se">\
</span></span></span><span class="line"><span class="cl"><span class="se"></span>    https://demo.connectrpc.com/connectrpc.eliza.v1.ElizaService/Say
</span></span><span class="line"><span class="cl"><span class="o">{</span>
</span></span><span class="line"><span class="cl">  <span class="s2">&#34;sentence&#34;</span>: <span class="s2">&#34;Hello, how are you feeling today?&#34;</span>
</span></span><span class="line"><span class="cl"><span class="o">}</span>
</span></span></code></pre></div><p>And now the classic gRPC:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-shell" data-lang="shell"><span class="line"><span class="cl">$ buf curl <span class="se">\
</span></span></span><span class="line"><span class="cl"><span class="se"></span>    --http3 <span class="se">\
</span></span></span><span class="line"><span class="cl"><span class="se"></span>    --protocol<span class="o">=</span>grpc <span class="se">\
</span></span></span><span class="line"><span class="cl"><span class="se"></span>    --schema<span class="o">=</span>buf.build/connectrpc/eliza <span class="se">\
</span></span></span><span class="line"><span class="cl"><span class="se"></span>    -d <span class="s1">&#39;{&#34;sentence&#34;: &#34;Hello world!&#34;}&#39;</span> <span class="se">\
</span></span></span><span class="line"><span class="cl"><span class="se"></span>    https://demo.connectrpc.com/connectrpc.eliza.v1.ElizaService/Say
</span></span><span class="line"><span class="cl"><span class="o">{</span>
</span></span><span class="line"><span class="cl">   <span class="s2">&#34;code&#34;</span>: <span class="s2">&#34;internal&#34;</span>,
</span></span><span class="line"><span class="cl">   <span class="s2">&#34;message&#34;</span>: <span class="s2">&#34;protocol error: no Grpc-Status trailer: unexpected EOF&#34;</span>
</span></span><span class="line"><span class="cl"><span class="o">}</span>
</span></span></code></pre></div><p>Wait, <em><strong>what</strong></em>? Why is it complaining about missing HTTP trailers? What gives?</p>

    
    
        
        
            
        
        <img src="https://kmcd.dev/posts/grpc-over-http3/trailers_hu_41da2c4f990cf317.webp"
             alt="" class="center" width="400px"/>
    


<p>HTTP trailers are a recurring issue for gRPC. HTTP trailers are a special type of HTTP header that is sent at the very end of a message body after all the data has been transmitted. They are useful for sending metadata that cannot be determined until the entire message is known, such as checksums, signatures, or other end-of-message signals. gRPC relies on trailers to return status codes and error details.</p>
<p>To explain why I am not receiving the HTTP trailers, I had to dig into <a href="https://github.com/quic-go/quic-go" rel="external">quic-go&rsquo;s</a> HTTP/3 implementation.</p>
<h4 id="adding-trailer-support-to-quic-go">Adding trailer support to quic-go</h4>
<p>When I looked into quic-go, I discovered that it <em>doesn&rsquo;t support <a href="https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Trailer" rel="external">HTTP trailers</a> yet</em>. There is <a href="https://github.com/quic-go/quic-go/issues/2266" rel="external">an issue</a> and a <a href="https://github.com/quic-go/quic-go/pull/2344" rel="external">related pull request</a>. But the issue is four years old, the PR is two years old, and both are still open after all of this time. This seemed quite crazy to me at first, but, on reflection, I realized that gRPC is the most popular thing that currently uses HTTP trailers. However, <code>grpc-go</code> directly uses HTTP/2 support from <code>golang.org/x/net/http2</code> instead of using <code>net/http</code> so it would require a good amount of work to get HTTP/3 support through quic-go. Therefore, this issue probably isn&rsquo;t on the radar of anyone working on <code>grpc-go</code>. However, it is trivial (as seen above) to use quic-go with ConnectRPC, so I think this is a unique situation where progress can be made quickly.</p>
<p>So I ended up implementing trailer support for clients and I <a href="https://github.com/quic-go/quic-go/issues/2266" rel="external">submitted my own PR</a>. I don&rsquo;t think it is quite ready yet but when I use <a href="https://github.com/sudorandom/quic-go/tree/client-trailers" rel="external">my branch of quic-go</a> with <a href="https://github.com/sudorandom/buf/tree/http3" rel="external">my branch of the buf CLI</a>, gRPC actually works with HTTP/3!</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-shell" data-lang="shell"><span class="line"><span class="cl">$ buf curl <span class="se">\
</span></span></span><span class="line"><span class="cl"><span class="se"></span>    --http3 <span class="se">\
</span></span></span><span class="line"><span class="cl"><span class="se"></span>    --protocol<span class="o">=</span>grpc <span class="se">\
</span></span></span><span class="line"><span class="cl"><span class="se"></span>    --schema<span class="o">=</span>buf.build/connectrpc/eliza <span class="se">\
</span></span></span><span class="line"><span class="cl"><span class="se"></span>    -d <span class="s1">&#39;{&#34;sentence&#34;: &#34;Hello world!&#34;}&#39;</span> <span class="se">\
</span></span></span><span class="line"><span class="cl"><span class="se"></span>    https://demo.connectrpc.com/connectrpc.eliza.v1.ElizaService/Say
</span></span><span class="line"><span class="cl"><span class="o">{</span>
</span></span><span class="line"><span class="cl">  <span class="s2">&#34;sentence&#34;</span>: <span class="s2">&#34;Hello there...how are you today?&#34;</span>
</span></span><span class="line"><span class="cl"><span class="o">}</span>
</span></span></code></pre></div><p>There are a few issues that I have with my PR. I based it off of the earlier PR but many things have changed with the codebase that actually make it harder to implement this feature. However, it&rsquo;s super encouraging that this code seems to work without too much fuss.</p>
<h3 id="experiment-results">Experiment Results</h3>
<p>My experimentation shows that while Go doesn&rsquo;t yet have full, native support for gRPC over HTTP/3, there are practical workarounds available today. Both the gRPC-Web and Connect protocols function seamlessly over HTTP/3, and in fact, ConnectRPC may already be leveraging HTTP/3 in production environments where infrastructure allows. I discovered this firsthand with the <a href="https://connectrpc.com/demo/" rel="external">ConnectRPC demo website</a>, which will connect using HTTP/3 from the browser to the load balancer (after a few requests so the browser knows HTTP/3 is available).</p>

    
    
        
        
            
        
        <img src="https://kmcd.dev/posts/grpc-over-http3/demo-connectrpc_hu_ad658e51d3dd4240.webp"
             alt="" class="center" width="400px"/>
    


<aside>Note: While the connection between my browser and the demo service is HTTP/3, it is likely that HTTP/2 is being used between the load balancer and the backend service. Even without HTTP/3 through the entire stack, the reduced number of round trips between the load balancer and the end user is likely still an advantage due to the decreased connection latency.
</aside>

<h2 id="conclusion">Conclusion</h2>
<p>In this post, we&rsquo;ve explored the exciting potential of HTTP/3 to supercharge gRPC performance. We dove into the key advantages of HTTP/3, such as faster connection establishment, elimination of head-of-line blocking, and mandatory encryption. By getting our hands dirty with practical examples in Go, we&rsquo;ve seen firsthand how HTTP/3 can be seamlessly integrated into gRPC services using tools like ConnectRPC and Buf.</p>
<p>While the gRPC ecosystem&rsquo;s full adoption of HTTP/3 is in its early stages, the benefits are clear, and the tools are already available in some libraries and tools. As developers, we have the opportunity to push this technology forward and shape the future of high-performance, secure communication.</p>

    
    
        
        
            
        
        <img src="https://kmcd.dev/posts/grpc-over-http3/i-should-use-http3_hu_b58073cc7505a7e3.webp"
             alt="" class="center" width="400px"/>
    


<p>I encourage you to experiment with HTTP/3 and gRPC in your own projects. Explore different implementations, measure the performance gains, and don&rsquo;t be afraid to dive into the code if you run into issues. Your active engagement with this evolving technology can directly contribute to the ongoing development of gRPC and HTTP/3. Although widespread adoption of HTTP/3 for gRPC on the backend is still in its early stages, if you have the flexibility to control both server and client components, or are working with browser-based clients, you might find compelling use cases for it even today.</p>
<p>I&rsquo;d love to hear about your experiences with HTTP/3 and gRPC. Have you seen significant performance improvements? Or perhaps you&rsquo;ve found that QUIC is slower without the kernel-level optimizations that TCP can take advantage of? What challenges have you encountered while experimenting with this exciting technology? Let&rsquo;s share our experiences and findings because even though HTTP/3 is still finding its footing in this context, there&rsquo;s a lot we can learn from each other.</p>
]]></content:encoded></item><item><title>gRPC: The Good Parts</title><link>https://kmcd.dev/posts/grpc-the-good-parts/</link><pubDate>Tue, 02 Jul 2024 00:00:00 +0000</pubDate><guid>https://kmcd.dev/posts/grpc-the-good-parts/</guid><description> 
                &lt;p> &lt;img hspace="5" src="https://kmcd.dev/posts/grpc-the-good-parts/cover_hu_208016cb095a54b9.webp" /> &lt;/p>
                
                Not perfect, but still pretty awesome.
                </description><content:encoded><![CDATA[<p>While REST APIs remain a popular choice for building web services, gRPC is increasingly being adopted for its unique advantages in performance, efficiency, and developer experience. You may have seen my post, <a href="https://kmcd.dev/posts/grpc-the-bad-parts/">gRPC: The Bad Parts</a>, where I talk about some of my issues with gRPC. Based on the many comments about that article, I could easily write a sequel with even more complaints. However, today I&rsquo;m going to focus on the <em>good</em> parts of gRPC. It has become obvious to me that many people didn&rsquo;t read the ending of the last post which attempted to outline how many of the points that I made are no longer true. So I figured that I need to give the positive aspects of gRPC a dedicated post.</p>
<p>Let&rsquo;s dive into the key advantages that make gRPC a powerful tool for modern web development.</p>
<h2 id="performance">Performance</h2>
<p>This one might be a little controversial, but <a href="https://protobuf.dev/" rel="external">Protocol Buffers</a> is, indeed, faster than JSON and XML. This continues <a href="https://streamdal.com/blog/ptotobuf-vs-json-for-your-event-driven-architecture/" rel="external">to be demonstrated</a> over and over again. Protobuf is able to be faster for these reasons:</p>
<ul>
<li>Field names are not included in the message. Instead, protobuf uses numbers to distinguish fields. In most cases you&rsquo;ll see field numbers take one or two bytes on the wire when it can be much more than that depending on your JSON field names.</li>
<li>Protobuf&rsquo;s <code>VARINT</code> type allows for small scale integers to take up a single byte, even if it&rsquo;s an int64. Realistically we really don&rsquo;t use that many large numbers so these savings can add up. Again, it&rsquo;s much better than ASCII-encoded numbers being used for each digit.</li>
<li>There&rsquo;s no real winning with strings or byte arrays but compression is still supported with gRPC so at worst this aspect is even with HTTP/JSON.</li>
</ul>
<p>I&rsquo;ve personally seen 50% data transfer savings by switching to protobuf encoding with realistic payloads.</p>
<p>There are <a href="https://reasonablypolymorphic.com/blog/protos-are-wrong/" rel="external">some haters</a> of protobuf encoding, and that&rsquo;s perfectly fine. The only &ldquo;fatal flaw&rdquo; that I&rsquo;ve actually been annoyed with is &ldquo;map values cannot be other maps.&rdquo; It does seem like that should be possible, even when you consider the internal representation of a map:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-protobuf" data-lang="protobuf"><span class="line"><span class="cl"><span class="n">map</span><span class="p">&lt;</span><span class="n">key_type</span><span class="p">,</span> <span class="n">value_type</span><span class="p">&gt;</span> <span class="n">map_field</span> <span class="o">=</span> <span class="n">N</span><span class="p">;</span><span class="err">
</span></span></span></code></pre></div><p>transforms into:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-protobuf" data-lang="protobuf"><span class="line"><span class="cl"><span class="kd">message</span> <span class="nc">MapFieldEntry</span> <span class="p">{</span><span class="err">
</span></span></span><span class="line"><span class="cl"><span class="err"></span>  <span class="n">key_type</span> <span class="n">key</span> <span class="o">=</span> <span class="mi">1</span><span class="p">;</span><span class="err">
</span></span></span><span class="line"><span class="cl"><span class="err"></span>  <span class="n">value_type</span> <span class="n">value</span> <span class="o">=</span> <span class="mi">2</span><span class="p">;</span><span class="err">
</span></span></span><span class="line"><span class="cl"><span class="err"></span><span class="p">}</span><span class="err">
</span></span></span><span class="line"><span class="cl"><span class="err">
</span></span></span><span class="line"><span class="cl"><span class="err"></span><span class="k">repeated</span> <span class="n">MapFieldEntry</span> <span class="n">map_field</span> <span class="o">=</span> <span class="n">N</span><span class="p">;</span><span class="err">
</span></span></span></code></pre></div><p>It&rsquo;s super frustrating because I don&rsquo;t understand why <code>value_type</code> can&rsquo;t be a map. The solution to this problem is just to make your own wrapper type to use as the value that contains a map. It is kind of annoying and this does come up semi-often. Crap, this was supposed to be a positive article. Let&rsquo;s get back on track.</p>
<p>I think the protobuf encoding is better than JSON in many ways. However, I understand that sometimes you just want JSON, and with <a href="https://protobuf.dev/programming-guides/proto3/#json" rel="external">gRPC you absolutely can just use JSON</a>. gRPC still has a few binary framing bytes before each message that won&rsquo;t be human readable but if you&rsquo;re really concerned with those check out the <a href="https://kmcd.dev/posts/grpc-the-good-parts/#connectrpc">ConnectRPC</a> section below.</p>
<p>Most gRPC implementations also let you define your own encoding, so it may possible to insert your own favorite encoding if you want to push the limits.</p>
<h2 id="strongly-typed-contracts">Strongly Typed Contracts</h2>
<p>Say goodbye to the guesswork of loosely typed APIs. gRPC&rsquo;s protobuf definitions create rock-solid contracts between client and server. This translates to:</p>
<ul>
<li><strong>Fewer errors:</strong> Clear expectations for data types reduce the chance of mismatched data.</li>
<li><strong>Better code generation:</strong> Automatic generation of client and server code in various languages saves time and effort.</li>
<li><strong>Smoother development cycles:</strong> Consistent contracts make it easier to evolve your API without breaking existing clients.</li>
<li><strong>Generated Documentation:</strong> Automatic generation of documentation means that your documentation will never be out of sync with your API.</li>
</ul>
<p>API contracts are very powerful. For more on this topic, I&rsquo;ve written an article discussing API contracts called <a href="https://kmcd.dev/posts/api-contracts/">Building APIs with Contracts</a>.</p>
<h2 id="streaming-support">Streaming Support</h2>
<p>Streaming support is arguably the best and most unique feature for gRPC. It does away with needing to frequently poll for updates in many scenarios which make it a good candidate for:</p>
<ul>
<li><strong>Chat applications:</strong> Seamlessly handle messages flowing back and forth.</li>
<li><strong>Live updates:</strong> Push updates to clients as soon as they happen.</li>
<li><strong>Any scenario where constant communication is key:</strong> From gaming to financial data, gRPC&rsquo;s streaming capabilities open up a world of possibilities.</li>
</ul>
<p>If you come from the networking world, you might know that gNMI (which is based on gRPC) is the replacement for SNMP. Instead of polling network devices for the same data every minute, you can now use gNMI to subscribe to counters. I&rsquo;ve written more about this in a post called <a href="https://kmcd.dev/posts/gnmi/">Why you should use gNMI over SNMP in 2024</a>.</p>
<h2 id="cross-language-support">Cross-Language Support</h2>
<p>gRPC doesn&rsquo;t care what programming language you prefer. Thanks to code generation tools, you can seamlessly work with gRPC in a wide range of languages, including:</p>
<ul>
<li>Go</li>
<li>Rust</li>
<li>Java</li>
<li>Python</li>
<li>C#</li>
<li>Node.js</li>
<li>Ruby</li>
<li>&hellip;and many more!</li>
</ul>
<p>This promotes flexibility, collaboration, and the ability to choose the right tool for the job. Currently, I believe gRPC has such a large momentum with language support that it&rsquo;s hard for me to consider an alternative that doesn&rsquo;t also speak gRPC. Many alternatives that have similar benefits to gRPC have mediocre support for a handful of languages at best.</p>
<h2 id="pioneered-http2">Pioneered HTTP/2</h2>
<p>gRPC was a driving force behind the adoption of HTTP/2, a major upgrade to the web&rsquo;s underlying protocol. This means you get all the benefits of HTTP/2&rsquo;s:</p>
<ul>
<li><strong>Multiplexing:</strong> Multiple requests and responses can share a single connection, improving efficiency.</li>
<li><strong>Header compression:</strong> Smaller headers mean faster transmission.</li>
<li><strong>Overall performance improvements:</strong> HTTP/2 is simply a faster, more efficient way to communicate over the web.</li>
</ul>
<h3 id="http3">HTTP/3</h3>
<p>There&rsquo;s some movement on HTTP/3 support for gRPC. There is an <a href="https://github.com/grpc/proposal/blob/master/G2-http3-protocol.md" rel="external">open proposal</a> created by the dotnet gRPC library maintainers and there is <a href="https://github.com/grpc/grpc/issues/19126" rel="external">an open issue to discuss actually adding HTTP/3 to the gRPC spec</a>. Frustratingly, there hasn&rsquo;t been a lot of movement on the official gRPC repo to add support directly to any of their implementations, but as you can see from the thread, there&rsquo;s a lot of interest and a lot of people making prototypes that prove the concept.</p>
<p>This is likely an incomplete list but here are the packages that you can likely use HTTP/3 with today:</p>
<ul>
<li>The standard grpc library for C#, dotnet-grpc <a href="https://devblogs.microsoft.com/dotnet/http-3-support-in-dotnet-6/#grpc-with-http-3" rel="external">(ref)</a></li>
<li>It may already be possible in rust with Tonic with the Hyper HTTP transport <a href="https://github.com/hyperium/tonic/issues/339" rel="external">(ref)</a></li>
<li>It&rsquo;s possible in Go if you use <a href="https://connectrpc.com/" rel="external">ConnectRPC</a> with <a href="https://github.com/quic-go/quic-go" rel="external">quic-go</a> - I don&rsquo;t have a link for this, but I&rsquo;ve tested this out myself. This is a topic for a future post!</li>
<li>This is untested but I believe many gRPC-Web implementations in the browser might &ldquo;just work&rdquo; with HTTP/3 as well as long as the browsers are informed of the support via the <a href="https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Alt-Svc" rel="external">ALT-SVC header</a> and the servers support it.</li>
</ul>
<p>As more servers and clients support HTTP/3 they should see faster connection establishment times, complete removal of the <a href="https://blog.cloudflare.com/the-road-to-quic#headoflineblocking" rel="external">head-of-line blocking problem</a> and much better recovery from packet loss. There&rsquo;s a long way to go here, but there is progress.</p>
<h2 id="bridging-the-gap">Bridging the Gap</h2>
<p>If you&rsquo;re looking to gradually adopt gRPC or need to support existing REST clients, there are several options available <em>today</em>!</p>
<h3 id="jsonhttp-transcoding">JSON/HTTP Transcoding</h3>
<p>Tools like <a href="https://github.com/grpc-ecosystem/grpc-gateway" rel="external">gRPC-Gateway</a>, <a href="https://cloud.google.com/endpoints" rel="external">Google Cloud Endpoints</a> and <a href="https://www.envoyproxy.io/" rel="external">Envoy</a> can expose REST-like interfaces while still reaping the benefits of gRPC on the backend. You can define a service that looks like this:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-protobuf" data-lang="protobuf"><span class="line"><span class="cl"><span class="n">syntax</span> <span class="o">=</span> <span class="s">&#34;proto3&#34;</span><span class="p">;</span><span class="err">
</span></span></span><span class="line"><span class="cl"><span class="err"></span><span class="kn">package</span> <span class="nn">your</span><span class="o">.</span><span class="n">service.v1</span><span class="p">;</span><span class="err">
</span></span></span><span class="line"><span class="cl"><span class="err"></span><span class="k">option</span> <span class="n">go_package</span> <span class="o">=</span> <span class="s">&#34;github.com/yourorg/yourprotos/gen/go/your/service/v1&#34;</span><span class="p">;</span><span class="err">
</span></span></span><span class="line"><span class="cl"><span class="err">
</span></span></span><span class="line"><span class="cl"><span class="err"></span><span class="k">import</span> <span class="s">&#34;google/api/annotations.proto&#34;</span><span class="p">;</span><span class="err">
</span></span></span><span class="line"><span class="cl"><span class="err">
</span></span></span><span class="line"><span class="cl"><span class="err"></span><span class="kd">message</span> <span class="nc">StringMessage</span> <span class="p">{</span><span class="err">
</span></span></span><span class="line"><span class="cl"><span class="err"></span>  <span class="kt">string</span> <span class="n">value</span> <span class="o">=</span> <span class="mi">1</span><span class="p">;</span><span class="err">
</span></span></span><span class="line"><span class="cl"><span class="err"></span><span class="p">}</span><span class="err">
</span></span></span><span class="line"><span class="cl"><span class="err">
</span></span></span><span class="line"><span class="cl"><span class="err"></span><span class="kd">service</span> <span class="n">YourService</span> <span class="p">{</span><span class="err">
</span></span></span><span class="line"><span class="cl"><span class="err"></span>  <span class="k">rpc</span> <span class="n">Echo</span><span class="p">(</span><span class="n">StringMessage</span><span class="p">)</span> <span class="k">returns</span> <span class="p">(</span><span class="n">StringMessage</span><span class="p">)</span> <span class="p">{</span><span class="err">
</span></span></span><span class="line"><span class="cl"><span class="err"></span>    <span class="k">option</span> <span class="p">(</span><span class="n">google.api.http</span><span class="p">)</span> <span class="o">=</span> <span class="p">{</span><span class="err">
</span></span></span><span class="line"><span class="cl"><span class="err"></span>      <span class="n">post</span><span class="o">:</span> <span class="s">&#34;/v1/example/echo&#34;</span><span class="err">
</span></span></span><span class="line"><span class="cl"><span class="err"></span>      <span class="n">body</span><span class="o">:</span> <span class="s">&#34;*&#34;</span><span class="err">
</span></span></span><span class="line"><span class="cl"><span class="err"></span>    <span class="p">};</span><span class="err">
</span></span></span><span class="line"><span class="cl"><span class="err"></span>  <span class="p">}</span><span class="err">
</span></span></span><span class="line"><span class="cl"><span class="err"></span><span class="p">}</span><span class="err">
</span></span></span></code></pre></div><p>And get a REST-like endpoint where you can make this request:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-shell" data-lang="shell"><span class="line"><span class="cl">curl -XPOST <span class="s1">&#39;{&#34;value&#34;: &#34;my value!&#34;}&#39;</span> http://localhost:8000/v1/example/echo
</span></span></code></pre></div><p>This is pretty amazing because it&rsquo;s doing a lot of the hard work for you and you can now support many different REST APIs without writing any additional code. This is a simple example here but there are many options, like being able to populate message fields from components of the path.</p>
<h3 id="grpc-web">gRPC-Web</h3>
<p>One of the big limitations of gRPC is that it doesn&rsquo;t work on the web with web browsers due to limited support of HTTP trailers. Browsers support receiving trailers but there isn&rsquo;t yet a way to retrieve those trailers from javascript. Yes, this is incredibly frustrating, especially since there are many small use cases where trailer support would be amazing to have.</p>
<p>The gRPC-Web protocol gives browsers the ability to use gRPC, which drastically improves the story of contract-based services in gRPC. It also allows for HTTP/1.1 clients to work with gRPC. Some platforms (I&rsquo;m looking at you, <a href="https://forum.unity.com/threads/support-for-http-2-with-unitywebrequest.1030510/" rel="external">Unity</a>) still don&rsquo;t support HTTP/2, even though it&rsquo;s 2024 and the <code>HTTP/2</code> spec was created nearly a decade ago.</p>
<h3 id="connectrpc">ConnectRPC</h3>
<p><a href="https://connectrpc.com/" rel="external">ConnectRPC</a> automatically generates JSON/HTTP APIs from your gRPC definitions while also maintaining compatibility with gRPC and gRPC-Web. This HTTP protocol, <a href="https://connectrpc.com/docs/protocol/" rel="external">called Connect</a>, follows HTTP standards more closely. For example, the <code>Content-Coding</code> header, <code>Content-Length</code> header, HTTP status codes, etc. all work as expected for unary RPC calls. That means you can run this normal-looking curl command and talk to a gRPC service:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-shell" data-lang="shell"><span class="line"><span class="cl">curl --header <span class="s2">&#34;Content-Type: application/json&#34;</span> <span class="se">\
</span></span></span><span class="line"><span class="cl"><span class="se"></span>    --data <span class="s1">&#39;{&#34;sentence&#34;: &#34;I feel happy.&#34;}&#39;</span> <span class="se">\
</span></span></span><span class="line"><span class="cl"><span class="se"></span>    https://demo.connectrpc.com/connectrpc.eliza.v1.ElizaService/Say
</span></span></code></pre></div><h3 id="twirp">Twirp</h3>
<p><a href="https://twitchtv.github.io/twirp/" rel="external">Twirp</a> is very similar to ConnectRPC. It was developed by Twitch, and is another framework that can help bridge the gap between gRPC and REST. <a href="https://twitchtv.github.io/twirp/docs/spec_v7.html" rel="external">Twirp&rsquo;s approach</a> is to use protobufs to generate an alternative protocol that also aligns more with HTTP conventions. It doesn&rsquo;t also support gRPC and gRPC-Web. Implementing those alongside twirp is left as an exercise for the user if you want to interoperate with other gRPC tooling.</p>
<h2 id="tooling">Tooling</h2>
<p>I have mentioned that gRPC tooling isn&rsquo;t that great. I still agree with that if we&rsquo;re talking about the &ldquo;out of the box&rdquo; tooling from the gRPC project. However, the community is much bigger than the gRPC Authors and someone finally made the protobuf code generation a lot better.</p>
<h3 id="buf-cli">Buf CLI</h3>
<p><a href="https://buf.build/" rel="external">Buf</a> (the company) has made a client called <a href="https://buf.build/product/cli" rel="external">Buf CLI</a>, which I&rsquo;m going to refer to as just &ldquo;buf&rdquo; from here on out.</p>
<p><code>protoc</code> is the official compiler for protobufs, which has plugins for many languages, frameworks, documentation and other kinds of outputs. Buf <em>completely</em> replaces <a href="https://grpc.io/docs/protoc-installation/" rel="external"><code>protoc</code></a> by using the same protoc plugins that <code>protoc</code> uses. How is it better? It adds a set of config files for defining the structure of your protobuf files, including external protobuf dependencies and external plugins using the <a href="https://buf.build/product/bsr" rel="external">Buf Schema Registry</a>. Instead of random makefile directives or bash scripts, we now have a well-defined config file for defining how the protobuf is built, which is amazing.</p>
<p>Similarly, <code>buf curl</code> provides a convenient way to interact with gRPC services, much like the popular tool <a href="https://github.com/fullstorydev/grpcurl" rel="external">grpcurl</a>.</p>
<p>In addition to replacing existing tooling with easier-to-use versions, buf also implements some extremely useful functions. I first started using buf by using <code>buf lint</code>, which helps enforce some <a href="https://buf.build/docs/lint/rules" rel="external">common rules and practices</a> that developers should follow when making their protobuf files. Soon after, I started using <code>buf breaking</code> which will report on breaking changes being made to protobuf files that may break clients. Both have easy-to-use Github actions and were pretty painless to set up.</p>
<p>Adding buf into the mix can greatly improve your workflow with protobufs, especially when working in a larger team or working with other teams.</p>
<h3 id="third-party-protoc-plugins-libraries-and-tools">Third-party protoc plugins, libraries and tools</h3>
<p>There&rsquo;s so many plugins now. I even <a href="https://github.com/sudorandom/protoc-gen-connect-openapi" rel="external">made one</a> and to be honest, plugins aren&rsquo;t that hard to make. I think this is the way that API development should work where you base API services off of a contract and generate everything from that same contract. No typos. No confusion over what methods exist. No arguing over REST semantics that has never been clear to anyone.</p>
<ul>
<li><strong><a href="https://github.com/pseudomuto/protoc-gen-doc" rel="external">protoc-gen-doc</a></strong>: Builds gRPC documentation in several formats. The default styling honestly doesn&rsquo;t look the prettiest but it does allow you to specify a custom template which has been amazing for me to generate something custom without requiring an entire plugin.</li>
<li><strong><a href="https://github.com/sudorandom/protoc-gen-connect-openapi" rel="external">protoc-gen-connect-openapi</a></strong>: This is my plugin. It generates OpenAPIv3 specs for your ConnectRPC services and has support for <a href="https://github.com/sudorandom/protoc-gen-connect-openapi/blob/main/protovalidate.md" rel="external">protovalidate</a>, <a href="https://github.com/sudorandom/protoc-gen-connect-openapi/blob/main/gnostic.md" rel="external">gnostic OpenAPIv3 annotations</a>, and <a href="https://github.com/sudorandom/protoc-gen-connect-openapi/blob/main/grpcgateway.md" rel="external">gRPC-Gateway annotations</a>.</li>
<li><strong><a href="https://github.com/bufbuild/protovalidate" rel="external">protovalidate</a></strong>: Protovalidate allows you to embed validation rules in your protobuf files which can be used by an associated library to enforce those rules. A large complaint that people have with gRPC is that it&rsquo;s hard to use whenever every single field is optional. Now you can replace a lot of validation code, including required fields, with protobuf options. I&rsquo;m anxiously awaiting <a href="https://github.com/bufbuild/protovalidate/issues/67" rel="external">typescript support</a> so validation logic can be shared on web frontends and backends, which, to me, is the &ldquo;holy grail&rdquo; feature of a contract-driven service.</li>
</ul>
<p>In addition to these libraries and plugins, more tools that you know and love from HTTP are supporting gRPC like <a href="https://blog.postman.com/postman-now-supports-grpc/" rel="external">Postman</a>, <a href="https://docs.insomnia.rest/insomnia/grpc" rel="external">Insomnia</a> and <a href="https://k6.io/docs/using-k6/protocols/grpc/" rel="external">k6</a>.</p>
<p>The availability of numerous third-party plugins underscores the fact that gRPC is more than just a framework – it&rsquo;s a dynamic ecosystem that fosters innovation and empowers developers to customize their workflows to meet their specific requirements.</p>
<h2 id="conclusion">Conclusion</h2>
<p>gRPC offers a compelling set of advantages for modern web development.</p>
<p>Its performance, strong typing, streaming capabilities, cross-language support, and HTTP/2 foundation make it a powerful tool for building efficient and scalable APIs. With various adoption options available, you can gradually incorporate gRPC into your projects and experience its benefits firsthand.</p>
<p>The growing community and active development around gRPC suggest a bright future for this technology. If you&rsquo;re looking to build fast, reliable, and future-proof APIs, gRPC is a tool that deserves a serious look. Dive in, explore the ecosystem, and discover how gRPC can revolutionize the way you approach API development.</p>
]]></content:encoded></item><item><title>gRPC: The Bad Parts</title><link>https://kmcd.dev/posts/grpc-the-bad-parts/</link><pubDate>Tue, 18 Jun 2024 00:00:00 +0000</pubDate><guid>https://kmcd.dev/posts/grpc-the-bad-parts/</guid><description><![CDATA[ 
                <p> <img hspace="5" src="https://kmcd.dev/posts/grpc-the-bad-parts/cover_hu_9a8f03bbeac1c25f.webp" /> </p>
                
                gRPC isn&#39;t perfect but who is?
                ]]></description><content:encoded><![CDATA[<p>gRPC, the high-performance RPC framework, has been super successful (if you work for Google) and has drastically changed the way we all deploy APIs (if you work for Google). gRPC and protobuf is an extremely performant contract-focused framework with extremely wide language support. But it&rsquo;s not without its downsides. Making an RPC framework that requires code generation and support in many programming languages is sure to get some things wrong. As gRPC approaches a decade of usage, it is important to reflect on what could have been better.</p>
<h2 id="learning-curve">Learning Curve</h2>
<p>Let&rsquo;s start out extremely nit-picky. So-called unary RPCs are calls where the client sends a single request to the server and gets a single response back. Why does gRPC have to use such a non-standard term for this that only mathematicians have an intuitive understanding of? I have to explain the term every time I use it. And I&rsquo;m a little tired of it.</p>

    
    
        
        
            
        
        <img src="https://kmcd.dev/posts/grpc-the-bad-parts/meme_hu_c4668fdfa3b56496.webp"
             alt="" class="center" width="400px"/>
    


<p>Speaking of unary RPCs, the implementation is more complicated than it needs to be. While gRPC&rsquo;s streaming capabilities are powerful, they have introduced complexity for simple RPC calls that don&rsquo;t require streaming. This hurts the ability to inspect gRPC calls because now there is framing on every unary RPC which only makes sense for streaming. Protobuf encoding is complicated enough so let&rsquo;s not add extra gRPC framing where it isn&rsquo;t needed. Also, it doesn&rsquo;t pass my &ldquo;send a friend a cURL example&rdquo; test for any web API. It&rsquo;s just super annoying to explain to someone how to use gRPC. I&rsquo;ve said &ldquo;okay, but is server reflection enabled?&rdquo; so many times. I&rsquo;m just tired of it.</p>
<p>This complexity also bleeds into the tooling with the mandatory code generation step. This can be a hurdle, especially for dynamic languages where runtime flexibility is valued. Additionally, some developers might be hesitant to adopt a technology that necessitates an extra build step. We already need 20 build steps for modern web development, so it&rsquo;s sometimes hard to justify one more.</p>
<h2 id="compatibility-with-the-web">Compatibility with the Web</h2>
<p>The reliance on HTTP/2 initially limited gRPC&rsquo;s reach, as not all platforms and browsers fully supported it. This has improved over time, but it still poses a challenge in some environments. But even with HTTP/2 support, browsers have avoided adding a way to process HTTP trailers so browsers today still cannot use &ldquo;original&rdquo; gRPC. gRPC-Web has acted as a plaster for this issue by avoiding the use of trailers, but it often requires &ldquo;extra stuff&rdquo; like running a proxy that supports gRPC-Web. Which is annoying.</p>
<p>Late Adoption of HTTP/3: The delay in embracing HTTP/3 might have hindered gRPC&rsquo;s ability to take full advantage of the protocol&rsquo;s performance and efficiency benefits. I have personally been affected by the <a href="https://http3-explained.haxx.se/en/why-quic/why-tcphol" rel="external">head-of-line blocking</a> issue that can happen when using gRPC with HTTP/2 and it would be so nice to be able to completely do away with this issue by being able to use HTTP/3 with gRPC. It&rsquo;s strange to see a framework that pushed many languages to support HTTP/2 struggling to do the same thing with HTTP/3.</p>
<h2 id="json-mapping-and-prototext">JSON Mapping and Prototext</h2>
<p>Another area where the &ldquo;timing&rdquo; was wrong was the lack of a standardized JSON mapping early on. It has made gRPC less accessible for developers accustomed to JSON-based APIs and I don&rsquo;t think it ever recovered from that stigma. Having a mapping between protobuf types and JSON simplifies integration and interoperability with existing tools and systems. You would not believe how happy web developers can get when you say &ldquo;yeah, this is a super-efficient binary format&hellip; but you can set this flag and get JSON back if you want to debug.&rdquo; They get unreasonably excited. <em>Unreasonably. excited.</em> Anyway, now that protobuf has standard rules for mapping protobuf types to JSON (and the other way) I feel like the <a href="https://protobuf.dev/reference/protobuf/textformat-spec/" rel="external">protobuf text format</a> is an unnecessary complexity. I don&rsquo;t see a use-case for the text format now that we have JSON. So let&rsquo;s throw the text format away. We don&rsquo;t need it and I&rsquo;m down to pretend like it never existed if everyone else is. Cool?</p>
<h2 id="finite-message-sizes">Finite Message Sizes</h2>
<p>Most Protobuf encoders/decoders expect to fully parse an entire message and give the full response to the consumer but memory is finite and sometimes you might want larger messages. Sometimes you want to stream parts of these larger messages somewhere else and not keep the entire message in memory. Therefore, if you want to, for example, upload large files you&rsquo;re going to need to implement some kind of chunking. While chunking is a reasonable solution for handling large files, the absence of a standardized approach within gRPC might lead to inconsistent implementations and increased development effort.</p>
<p>As a demonstration, here&rsquo;s what it may look like to upload a file with gRPC:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-protobuf" data-lang="protobuf"><span class="line"><span class="cl"><span class="n">syntax</span> <span class="o">=</span> <span class="s">&#34;proto3&#34;</span><span class="p">;</span><span class="err">
</span></span></span><span class="line"><span class="cl"><span class="err">
</span></span></span><span class="line"><span class="cl"><span class="err"></span><span class="kn">package</span> <span class="nn">file_service</span><span class="p">;</span><span class="err">
</span></span></span><span class="line"><span class="cl"><span class="err">
</span></span></span><span class="line"><span class="cl"><span class="err"></span><span class="kd">service</span> <span class="n">FileService</span> <span class="p">{</span><span class="err">
</span></span></span><span class="line"><span class="cl"><span class="err"></span>   <span class="k">rpc</span> <span class="n">Upload</span><span class="p">(</span><span class="n">stream</span> <span class="n">UploadRequest</span><span class="p">)</span> <span class="k">returns</span><span class="p">(</span><span class="n">UploadResponse</span><span class="p">);</span><span class="err">
</span></span></span><span class="line"><span class="cl"><span class="err"></span><span class="p">}</span><span class="err">
</span></span></span><span class="line"><span class="cl"><span class="err">
</span></span></span><span class="line"><span class="cl"><span class="err"></span><span class="kd">message</span> <span class="nc">UploadRequest</span> <span class="p">{</span><span class="err">
</span></span></span><span class="line"><span class="cl"><span class="err"></span>    <span class="kt">string</span> <span class="n">file_name</span> <span class="o">=</span> <span class="mi">1</span><span class="p">;</span><span class="err">
</span></span></span><span class="line"><span class="cl"><span class="err"></span>    <span class="kt">bytes</span> <span class="n">chunk</span> <span class="o">=</span> <span class="mi">2</span><span class="p">;</span><span class="err">
</span></span></span><span class="line"><span class="cl"><span class="err"></span><span class="p">}</span><span class="err">
</span></span></span><span class="line"><span class="cl"><span class="err">
</span></span></span><span class="line"><span class="cl"><span class="err"></span><span class="kd">message</span> <span class="nc">UploadResponse</span> <span class="p">{</span><span class="err">
</span></span></span><span class="line"><span class="cl"><span class="err"></span>  <span class="kt">string</span> <span class="n">etag</span> <span class="o">=</span> <span class="mi">1</span><span class="p">;</span><span class="err">
</span></span></span><span class="line"><span class="cl"><span class="err"></span><span class="p">}</span><span class="err">
</span></span></span></code></pre></div><p>This is both a strength and weakness of protobufs. This concept is super easy to define in protobuf but in practice, the code to properly implement this can be cumbersome and error-prone. And while Google, the creator of gRPC, has figured out solutions for their APIs, the lack of a standardized approach leaves others to reinvent the wheel.</p>
<p>You might be thinking &ldquo;Google uses gRPC in most of their APIs so obviously they&rsquo;ve done this&rdquo; and you&rsquo;d be right. They actually have a gRPC and HTTP version for downloading (potentially large) files. We can compare the <a href="https://github.com/googleapis/google-cloud-go/blob/v0.114.0/storage/grpc_client.go#L996-L1152" rel="external">gRPC</a> and <a href="https://github.com/googleapis/google-cloud-go/blob/v0.114.0/storage/http_client.go#L888-L911" rel="external">HTTP</a> versions directly and gRPC is BY FAR more complex. Go ahead and compare the linked code. I&rsquo;ll wait.</p>
<h2 id="ded-internet-theory">ded internet theory</h2>
<p>I see a lot of gRPC/protobuf communities that are devoid of activity. The lack of visible activity on some websites might create the impression that gRPC is stagnant or less actively maintained. This could deter potential adopters and contribute to slower community growth. This might be a case of too many options, making it difficult to find someone to nerd out about gRPC outside of GitHub issues where such enthusiasm might be perceived as annoying.</p>
<h2 id="bad-tooling">Bad tooling</h2>
<p>For the longest time, when I saw that a codebase uses protobuf I found a weird script that downloads random protobuf files in super custom ways and places them in random paths and then makes a series of super complex calls to <code>protoc</code>. Only google would think not solving dependency management is the solution to dependency management. Google has its own extremely Google-y way of managing dependencies that we peasants can only dream of using.</p>
<h2 id="it-can-be-and-is-better">It can be (and is) better</h2>
<p>While I&rsquo;ve been critical of gRPC, I hope my comments come across as constructive. Those who have read this far down in the article get to know that many of these issues are already fixed or at least on the way to being fixed!</p>
<ul>
<li>Several gRPC implementations already support HTTP/3. ConnectRPC makes it pretty easy to use HTTP/3 with gRPC (I&rsquo;ll follow up on this in a future post).</li>
<li>Since the <a href="https://protobuf.dev/programming-guides/proto3/#json" rel="external">protobuf spec has a canonical mapping to/from JSON</a> I no longer have to worry about the text format. I really do hope that everyone forgets that it exists. There&rsquo;s only room for so many text-based formats. I wasn&rsquo;t joking about that. This is the last time I&rsquo;m acknowledging its existence.</li>
<li>The gRPC community is actually alive and well if you know where to look. For example, the <a href="https://buf.build/links/slack" rel="external">buf slack</a> has been a great resource for me. You may find me hanging out and answering questions fairly often.</li>
<li>The <a href="https://buf.build/docs/ecosystem/cli-overview" rel="external">Buf CLI</a> is an amazing tool for gRPC. It completely replaces <code>protoc</code> but also adds linting, breaking change detection, curl for gRPC, integration with the Buf Schema Registry (wow, real dependency management!) and more! In addition, more tools that you know and love from HTTP support gRPC like <a href="https://blog.postman.com/postman-now-supports-grpc/" rel="external">Postman</a>, <a href="https://docs.insomnia.rest/insomnia/grpc" rel="external">Insomnia</a> and <a href="https://k6.io/docs/using-k6/protocols/grpc/" rel="external">k6</a>.</li>
</ul>
<p>Despite gRPC&rsquo;s undeniable successes, it&rsquo;s important to acknowledge the framework&rsquo;s shortcomings to ensure its continued evolution and improvement. By addressing its learning curve, compatibility issues, lack of standardization, and community engagement, we can unlock gRPC&rsquo;s full potential and make it a more accessible and user-friendly tool for all developers.</p>
]]></content:encoded></item><item><title>Dropping Unknown Fields in ConnectRPC</title><link>https://kmcd.dev/posts/connectrpc-dropping-unknown-fields/</link><pubDate>Tue, 02 Apr 2024 00:00:00 +0000</pubDate><guid>https://kmcd.dev/posts/connectrpc-dropping-unknown-fields/</guid><description> 
                &lt;p> &lt;img hspace="5" src="https://kmcd.dev/posts/connectrpc-dropping-unknown-fields/cover_hu_6b091745410a087d.webp" /> &lt;/p>
                
                Learn how to drop unknown fields in ConnectRPC to enhance the security of your gRPC services exposed to the internet.
                </description><content:encoded><![CDATA[<p>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&rsquo;s security posture.</p>
<p>One crucial optimization for internet-facing gRPC services is customizing the behavior towards <strong>unknown fields</strong>. I&rsquo;ve talked about <a href="https://kmcd.dev/posts/protobuf-unknown-fields/">unknown fields in a previous post</a>, 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.</p>
<p>Here&rsquo;s why you should consider dropping unknown fields when exposing gRPC to the internet:</p>
<ul>
<li><strong>Preventing Malicious Data:</strong> 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.</li>
<li><strong>Ensuring Compatibility:</strong> 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.</li>
<li><strong>Improving Performance:</strong> Skipping unknown fields during message parsing can lead to performance gains, especially when dealing with large datasets.</li>
</ul>
<h3 id="how-to-drop-unknown-fields">How to Drop Unknown Fields</h3>
<p>Here is how you can drop unknown fields while using the standard <code>proto.UnmarshalOptions</code> struct provided by the <code>google.golang.org/protobuf/proto</code> package. Here&rsquo;s how to do it in your Go code:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="cl"><span class="kn">import</span><span class="w"> </span><span class="p">(</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">	</span><span class="s">&#34;google.golang.org/protobuf/proto&#34;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">	</span><span class="o">...</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="p">)</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="c1">// Configure unmarshalling options to discard unknown fields</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="nx">opts</span><span class="w"> </span><span class="o">:=</span><span class="w"> </span><span class="nx">proto</span><span class="p">.</span><span class="nx">UnmarshalOptions</span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">	</span><span class="nx">DiscardUnknown</span><span class="p">:</span><span class="w"> </span><span class="kc">true</span><span class="p">,</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="p">}</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="c1">// Use the options when unmarshalling incoming messages</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="nx">msg</span><span class="w"> </span><span class="o">:=</span><span class="w"> </span><span class="o">&amp;</span><span class="nx">MyMessage</span><span class="p">{}</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="nx">err</span><span class="w"> </span><span class="o">:=</span><span class="w"> </span><span class="nx">proto</span><span class="p">.</span><span class="nf">Unmarshal</span><span class="p">(</span><span class="nx">data</span><span class="p">,</span><span class="w"> </span><span class="nx">msg</span><span class="p">,</span><span class="w"> </span><span class="nx">opts</span><span class="p">)</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="k">if</span><span class="w"> </span><span class="nx">err</span><span class="w"> </span><span class="o">!=</span><span class="w"> </span><span class="kc">nil</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">	</span><span class="c1">// Handle error</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="p">}</span><span class="w">
</span></span></span></code></pre></div><p>By setting the <code>DiscardUnknown</code> field to <code>true</code> in the <code>proto.UnmarshalOptions</code> 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.</p>
<h2 id="how-to-drop-unknown-fields-in-connect-rpc-servers">How to Drop Unknown Fields in Connect RPC Servers</h2>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="cl"><span class="kn">package</span><span class="w"> </span><span class="nx">main</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="kn">import</span><span class="w"> </span><span class="p">(</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">	</span><span class="s">&#34;log&#34;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">	</span><span class="s">&#34;net/http&#34;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">	</span><span class="s">&#34;golang.org/x/net/http2&#34;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">	</span><span class="s">&#34;golang.org/x/net/http2/h2c&#34;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">	</span><span class="s">&#34;go.akshayshah.org/connectproto&#34;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="p">)</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="kd">func</span><span class="w"> </span><span class="nf">main</span><span class="p">()</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">	</span><span class="nx">greeter</span><span class="w"> </span><span class="o">:=</span><span class="w"> </span><span class="o">&amp;</span><span class="nx">GreetServer</span><span class="p">{}</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">	</span><span class="nx">mux</span><span class="w"> </span><span class="o">:=</span><span class="w"> </span><span class="nx">http</span><span class="p">.</span><span class="nf">NewServeMux</span><span class="p">()</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">	</span><span class="nx">path</span><span class="p">,</span><span class="w"> </span><span class="nx">handler</span><span class="w"> </span><span class="o">:=</span><span class="w"> </span><span class="nx">greetv1connect</span><span class="p">.</span><span class="nf">NewGreetServiceHandler</span><span class="p">(</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">		</span><span class="nx">greeter</span><span class="p">,</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">		</span><span class="c1">// Add an option that customizes protobuf marshalling/unmarshalling behavior</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">		</span><span class="nx">connectproto</span><span class="p">.</span><span class="nf">WithBinary</span><span class="p">(</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">			</span><span class="nx">proto</span><span class="p">.</span><span class="nx">MarshalOptions</span><span class="p">{},</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">			</span><span class="nx">proto</span><span class="p">.</span><span class="nx">UnmarshalOptions</span><span class="p">{</span><span class="nx">DiscardUnknown</span><span class="p">:</span><span class="w"> </span><span class="kc">true</span><span class="p">},</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">		</span><span class="p">),</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">		</span><span class="c1">// Add an option to customize JSON marshalling/unmachalling</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">		</span><span class="nx">connectproto</span><span class="p">.</span><span class="nf">WithJSON</span><span class="p">(</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">			</span><span class="nx">protojson</span><span class="p">.</span><span class="nx">MarshalOptions</span><span class="p">{},</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">			</span><span class="nx">protojson</span><span class="p">.</span><span class="nx">UnmarshalOptions</span><span class="p">{</span><span class="nx">DiscardUnknown</span><span class="p">:</span><span class="w"> </span><span class="kc">true</span><span class="p">},</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">		</span><span class="p">)</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">	</span><span class="p">)</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">	</span><span class="nx">mux</span><span class="p">.</span><span class="nf">Handle</span><span class="p">(</span><span class="nx">path</span><span class="p">,</span><span class="w"> </span><span class="nx">handler</span><span class="p">)</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">	</span><span class="nx">log</span><span class="p">.</span><span class="nf">Fatal</span><span class="p">(</span><span class="nx">http</span><span class="p">.</span><span class="nf">ListenAndServe</span><span class="p">(</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">		</span><span class="s">&#34;localhost:9000&#34;</span><span class="p">,</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">		</span><span class="nx">h2c</span><span class="p">.</span><span class="nf">NewHandler</span><span class="p">(</span><span class="nx">mux</span><span class="p">,</span><span class="w"> </span><span class="o">&amp;</span><span class="nx">http2</span><span class="p">.</span><span class="nx">Server</span><span class="p">{}),</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">	</span><span class="p">))</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="p">}</span><span class="w">
</span></span></span></code></pre></div><p>In this example, <code>connectproto.WithBinary</code> ensures only messages with defined fields are processed, enhancing the security of your gRPC service. <code>connectproto.WithJSON</code> does the same thing but with JSON.</p>
<h3 id="additional-considerations">Additional Considerations</h3>
<p>While dropping unknown fields is a valuable security practice, it&rsquo;s important to consider potential trade-offs:</p>
<ul>
<li><strong>Backward compatibility:</strong> Clients using older versions of the proto schema will encounter errors if they rely on previously defined unknown fields.</li>
<li><strong>Logging and Debugging:</strong> Dropping unknown fields might make it harder to identify the source of unexpected behavior during development or debugging.</li>
</ul>
<p>In such cases, it&rsquo;s recommended to document these trade-offs and have a clear versioning policy for your gRPC service and client applications.</p>
<h3 id="conclusion">Conclusion</h3>
<p>Exposing gRPC services to the internet requires careful security considerations. By customizing protobuf encoding options, specifically by dropping unknown fields using <code>proto.UnmarshalOptions</code>, 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.</p>
]]></content:encoded></item><item><title>RESTless: Web APIs After REST</title><link>https://kmcd.dev/posts/restless/</link><pubDate>Tue, 26 Mar 2024 00:00:00 +0000</pubDate><guid>https://kmcd.dev/posts/restless/</guid><description> 
                &lt;p> &lt;img hspace="5" src="https://kmcd.dev/posts/restless/cover_hu_76b7adf3bf2226c0.webp" /> &lt;/p>
                
                Web APIs are the backbone of the modern web, but the ever-evolving landscape demands a rethink. This article explores alternatives to the traditional REST approach, diving into solutions like GraphQL, gRPC, and WebSockets. Unlock the full potential of your APIs and discover a world beyond REST!
                </description><content:encoded><![CDATA[<p>The &ldquo;RESTful API&rdquo; 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&rsquo;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.</p>
<h3 id="objects-more-like-objnoxious">Objects? More like &ldquo;objnoxious&rdquo;</h3>
<p>Imagine building a social media API endpoint to retrieve a user&rsquo;s feed. Using a single REST object to represent a feed item can get messy. This object would need to encompass:</p>
<ul>
<li>User information (username, profile picture)</li>
<li>Post content (text, images, videos)</li>
<li>User interactions (likes, comments, shares) with timestamps</li>
<li>Additional data like post visibility or author verification status</li>
</ul>
<p>This &ldquo;feed&rdquo; 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.</p>
<p>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.</p>
<h3 id="versioning-is-weird">Versioning is weird</h3>
<p>Let&rsquo;s say you introduce a new field to your product data model in a REST API. Versioning in the path (e.g., <code>/api/v2/products</code>) forces you to update every single endpoint URL that uses that data. Versioning each path by adding a query parameter (e.g., <code>/api/products?version=2</code>) 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.</p>
<h3 id="limited-options-clever-solution">Limited Options, Clever Solution</h3>
<p>REST only offers a handful of methods (GET, POST, etc.) to handle data. There are <strong>9</strong> methods in total. Let&rsquo;s talk about each one.</p>
<ul>
<li>CONNECT</li>
<li>DELETE</li>
<li>GET</li>
<li>HEAD</li>
<li>OPTIONS</li>
<li>PATCH</li>
<li>POST</li>
<li>PUT</li>
<li>TRACE</li>
</ul>
<p>Most of these are used in niche, hyper-specific ways. I suspect most developers don&rsquo;t know what <code>HEAD</code>, <code>TRACE</code>, <code>OPTIONS</code>, and <code>CONNECT</code> do. None of these are incredibly useful when developing web APIs. So let&rsquo;s ditch them. Let&rsquo;s also ditch <code>PATCH</code> because it&rsquo;s just <code>PUT</code> in a trenchcoat.</p>
<p>Now we have: <code>GET</code>, <code>POST</code>, <code>DELETE</code>, and <code>PUT</code>. Sweet, we&rsquo;re left with enough methods to make a CRUD application. Roughly speaking:</p>
<ul>
<li><strong>C</strong>reate = <code>POST</code></li>
<li><strong>R</strong>ead = <code>GET</code></li>
<li><strong>U</strong>pdate = <code>PUT</code></li>
<li><strong>D</strong>elete = <code>DELETE</code></li>
</ul>
<p>Wow! Such simple. So elegant. I&rsquo;m sure glad that everything web developers do boils down to these 4 simple actions&hellip; Oh wait, that&rsquo;s <strong><em>totally</em> wrong</strong>. 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&rsquo;d make on top of that. This sounds like utter insanity and is exactly what REST gives us.</p>
<p>Let&rsquo;s stop pretending that there are only 9 (but actually 4) things you can do to a resource.</p>
<h3 id="inefficiency-of-json">Inefficiency of JSON</h3>
<p>JSON is slow and inefficient. It&rsquo;d be insane if we built the internet around this format. Wait, we did? Really?  JSON is wasteful in many ways. It&rsquo;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&rsquo;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 <a href="https://auth0.com/blog/beating-json-performance-with-protobuf/" rel="external">in pretty much every way</a>: CPU usage, memory usage, message size and speed. This just shows that there are better formats than JSON.</p>
<h3 id="openapi">OpenAPI?</h3>
<p><a href="https://www.openapis.org/" rel="external">OpenAPI</a> 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.</p>
<p>Okay, now here&rsquo;s where I get philosophical. If we&rsquo;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&rsquo;t do this because the tooling isn&rsquo;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&rsquo;s coming in after we&rsquo;ve designed our APIs and is trying to describe what&rsquo;s already there. It&rsquo;s an afterthought.</p>
<p>I&rsquo;ve attempted to go down the route of using OpenAPI to generate server stubs and clients. It didn&rsquo;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&rsquo;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 &ldquo;targets&rdquo; 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.</p>
<h2 id="so-what-other-options-do-we-have">So what other options do we have?</h2>
<p>REST <em>has</em> served us well, but the modern web demands more. Let&rsquo;s explore the exciting alternatives that offer a range of benefits and functionalities:</p>
<h3 id="graphql">GraphQL</h3>
<p>The &ldquo;Choose Your Own Adventure&rdquo; API. Still with the social media app example, imagine fetching only the user&rsquo;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.</p>
<p>This method also has its downsides like making the backend API extremely complex.</p>
<h3 id="grpc">gRPC</h3>
<p>The &ldquo;Cut the Drama&rdquo; API. Consider a mobile game that communicates with a game server. gRPC allows you to define remote procedures (like <code>attackEnemy</code> or <code>usePowerUp</code>) 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 <a href="https://kmcd.dev/posts/connectrpc/">ConnectRPC</a> that allow for leveraging of HTTP GET requests so you can fully leverage browser caching.</p>
<h3 id="websockets">WebSockets</h3>
<p>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&rsquo;s request-response cycle, allowing for a more dynamic connection.</p>
<h3 id="server-sent-events-sse">Server-Sent Events (SSE)</h3>
<p>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).</p>
<p>Here&rsquo;s a conclusion that summarizes the key points and offers a final thought:</p>
<p><strong>The Verdict: REST vs. the Rest</strong></p>
<p>REST APIs have served us faithfully for years, but as our applications become more complex and data-hungry, it&rsquo;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.</p>
<p>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&rsquo;t be afraid to break free from the comfy (but maybe slightly threadbare) jeans of REST when a more fitting alternative emerges.</p>
]]></content:encoded></item><item><title>softlayer-python: language bindings/CLI for a cloud company</title><link>https://kmcd.dev/posts/softlayer-python/</link><pubDate>Mon, 31 Jul 2023 00:00:00 +0000</pubDate><guid>https://kmcd.dev/posts/softlayer-python/</guid><description> 
                &lt;p> &lt;img hspace="5" src="https://kmcd.dev/posts/softlayer-python/cover_hu_6e49055ded8250.webp" /> &lt;/p>
                
                I wrote and maintained language bindings for a large cloud company. Join me as I reflect on that experience.
                </description><content:encoded><![CDATA[<p>I used to work for a public cloud company called <a href="https://en.wikipedia.org/wiki/IBM_Cloud#SoftLayer" rel="external">SoftLayer</a>. As a cloud company, there is an API that customers can use to provision new virtual servers, load balancers, firewalls and whatever else you might want. On our team, we used SoftLayer services as a customer might and we ended up proving new products and just&hellip; experiencing what it was like as a customer. I loved the concept. Our team heavily used this practice of so-called &ldquo;eating your own dog food.&rdquo;</p>
<p>When dogfooding our services it became painfully obvious that our API was extremely hard to use. When making tooling we had to go through extremely complex ordering APIs that were designed for internal use and were exposed publicly for convenience. I preferred to do this work in Python at the time, so I used our public API bindings. This was the <a href="https://github.com/softlayer/softlayer-python/tree/59b331dd9c33d9582d425be192fd3c2d63368d5d" rel="external">current state of the project on github</a>. Let me point out some of the issues I had:</p>
<ul>
<li>Didn&rsquo;t work with Python 3.</li>
<li>Used class variables incorrectly, making it impossible to use multiple instances of the client at the same time.</li>
<li>Had an awkward use of dictionary accessors that made things more confusing as a user.</li>
<li>Had absolutely zero unit tests.</li>
<li>The Python module name used upper-case characters.</li>
</ul>
<p>So&hellip; Immediately, I wanted to improve things. However, this was the first open-source project that I would modify that had a non-trivial number of users. Because of this, I learned to make strategic changes that followed the pattern of:</p>
<ul>
<li>Add several unit tests for the part of the code that</li>
<li>Add a new interface that</li>
<li>Use the new interface in the implementation of the old interface (to reduce code duplication)</li>
<li>Add deprecation warnings for the old interface.</li>
<li>After enough time, bump the major version of the library and remove all code that is deprecated.</li>
</ul>
<p>This pattern can be incredibly tedious. However, if you are maintaining a project used by many then you need to worry about upgrading. You need to write release notes. You need to give enough examples. You need to provide an upgrade path when you want to make breaking changes. This is super boring work, but it&rsquo;s the difference between &ldquo;when I upgrade this package everything breaks&rdquo; and &ldquo;when I upgrade this package, I get more cool new stuff&rdquo;.</p>
<p>For perspective, <a href="https://github.com/softlayer/softlayer-python" rel="external">here&rsquo;s what the code looks like now</a>. Note that there are now almost 2,000 unit tests. Note that the tests are run with several different versions of Python. And you should also note that there&rsquo;s an entire command line client in the repo as well!</p>
<p>The CLI was also created from the same motivations. For us, it makes automating and testing things much easier. Instead of clicking through a virtual machine creation web form for 20 minutes I could just copy/paste a command that I&rsquo;ve run before that could specify everything I would have to type or select anyway. In fact, here&rsquo;s an example of that!</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">$ slcli vs create --hostname<span class="o">=</span>example --domain<span class="o">=</span>softlayer.com -f B1_1X2X25 -o DEBIAN_LATEST_64  --datacenter<span class="o">=</span>ams01 --billing<span class="o">=</span>hourly
</span></span><span class="line"><span class="cl">This action will incur charges on your account. Continue? <span class="o">[</span>y/N<span class="o">]</span>: y
</span></span><span class="line"><span class="cl">    :..........:.................................:......................................:...........................:
</span></span><span class="line"><span class="cl">    :    ID    :               FQDN              :                 guid                 :         Order Date        :
</span></span><span class="line"><span class="cl">    :..........:.................................:......................................:...........................:
</span></span><span class="line"><span class="cl">    : <span class="m">70112999</span> : testtesttest.test.com : 1abc7afb-9618-4835-89c9-586f3711d8ea : 2019-01-30T17:16:58-06:00 :
</span></span><span class="line"><span class="cl">    :..........:.................................:......................................:...........................:
</span></span><span class="line"><span class="cl">    :.........................................................................:
</span></span><span class="line"><span class="cl">    :                            OrderId: <span class="m">12345678</span>                            :
</span></span><span class="line"><span class="cl">    :.......:.................................................................:
</span></span><span class="line"><span class="cl">    :  Cost : Description                                                     :
</span></span><span class="line"><span class="cl">    :.......:.................................................................:
</span></span><span class="line"><span class="cl">    :   0.0 : Debian GNU/Linux 9.x Stretch/Stable - Minimal Install <span class="o">(</span><span class="m">64</span> bit<span class="o">)</span>  :
</span></span><span class="line"><span class="cl">    :   0.0 : <span class="m">25</span> GB <span class="o">(</span>SAN<span class="o">)</span>                                                     :
</span></span><span class="line"><span class="cl">    :   0.0 : Reboot / Remote Console                                         :
</span></span><span class="line"><span class="cl">    :   0.0 : <span class="m">100</span> Mbps Public <span class="p">&amp;</span> Private Network Uplinks                       :
</span></span><span class="line"><span class="cl">    :   0.0 : <span class="m">0</span> GB Bandwidth Allotment                                        :
</span></span><span class="line"><span class="cl">    :   0.0 : <span class="m">1</span> IP Address                                                    :
</span></span><span class="line"><span class="cl">    :   0.0 : Host Ping and TCP Service Monitoring                            :
</span></span><span class="line"><span class="cl">    :   0.0 : Email and Ticket                                                :
</span></span><span class="line"><span class="cl">    :   0.0 : Automated Reboot from Monitoring                                :
</span></span><span class="line"><span class="cl">    :   0.0 : Unlimited SSL VPN Users <span class="p">&amp;</span> <span class="m">1</span> PPTP VPN User per account           :
</span></span><span class="line"><span class="cl">    :   0.0 : <span class="m">2</span> GB                                                            :
</span></span><span class="line"><span class="cl">    :   0.0 : <span class="m">1</span> x 2.0 GHz or higher Core                                      :
</span></span><span class="line"><span class="cl">    : 0.000 : Total hourly cost                                               :
</span></span><span class="line"><span class="cl">    :.......:.................................................................:
</span></span></code></pre></div><p>Here we&rsquo;re creating a VM inside of the Amsterdam data center that uses the latest Debian image with 2GB of RAM, a single CPU core and 25GB of disk space. All by copying a single command. This is super powerful. If you&rsquo;re wondering why everything is free it&rsquo;s because our account had special billing. 😛</p>
<p>After you create a VM, you can also list the running instances to see it:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">$ slcli vs list
</span></span><span class="line"><span class="cl">:.........:............:....................:.......:........:................:..............:....................:
</span></span><span class="line"><span class="cl">:    id   : datacenter :       host         : cores : memory :   primary_ip   :  backend_ip  : active_transaction :
</span></span><span class="line"><span class="cl">:.........:............:....................:.......:........:................:..............:....................:
</span></span><span class="line"><span class="cl">: <span class="m">1234567</span> :   sjc01    :  test.example.com  :   <span class="m">4</span>   :   4G   :    12.34.56    :   65.43.21   :         -          :
</span></span><span class="line"><span class="cl">:.........:............:....................:.......:........:................:..............:....................:
</span></span></code></pre></div><p>The story is the same for several other products. Here&rsquo;s a list of what products are supported today:</p>
<ul>
<li>Account Management</li>
<li>Block Storage</li>
<li>Bandwidth Pools</li>
<li>CDN</li>
<li>Dedicated Hosts</li>
<li>DNS</li>
<li>Email</li>
<li>File Storage</li>
<li>Firewall</li>
<li>Global IP</li>
<li>Dedicated Hardware</li>
<li>Disk Images</li>
<li>IPSec</li>
<li>Licenses</li>
<li>Load Balancers</li>
<li>NAS</li>
<li>Object Storage</li>
<li>Ordering/Quotes</li>
<li>SSH Keys and Certificates</li>
<li>Security Groups</li>
<li>Subnets</li>
<li>Support Tickets</li>
<li>Users</li>
<li>VLANs</li>
<li>Virtual Servers</li>
</ul>
<p>It&rsquo;s remarkable how far this project has come. This started as a way to create some VMs with a script but it became an extremely important interface for the entire suite of cloud products. Slowly, the CLI grew to what it is today. This wasn&rsquo;t a result of just me. Instead, several members of mine and other people&rsquo;s teams contributed to the project. There were periods when I didn&rsquo;t write a lot of code but would spend most of my time reviewing and guiding others to make their contributions. I learned a lot about the importance of enforcing a single style and keeping the quality of code high. Essentially, the ways customers interfaced was through the website, API or the CLI.</p>
<p>The CLI also drove more usage of the Python client. It encouraged this growth in several ways:</p>
<ul>
<li>It showcased what was possible with the API in a way that the documentation just can&rsquo;t do.</li>
<li>It acted as a good reference for &ldquo;how can I do this with the API&rdquo;. The more we added to the CLI, the fewer extra examples we needed to make. It is very important that the CLI was also open source for this reason.</li>
<li>It had a verbose flag that showed the API calls that were being made, greatly increasing visibility into how it works.</li>
</ul>
<p>Here&rsquo;s an example of what a command looks like when running a command using the verbose flag.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">$ slcli -v vs detail <span class="m">74397127</span>
</span></span><span class="line"><span class="cl">Calling: SoftLayer_Virtual_Guest::getObject<span class="o">(</span><span class="nv">id</span><span class="o">=</span>74397127, <span class="nv">mask</span><span class="o">=</span><span class="s1">&#39;id,globalIdentifier,fullyQualifiedDomainName,hostname,domain&#39;</span>, <span class="nv">filter</span><span class="o">=</span><span class="s1">&#39;None&#39;</span>, <span class="nv">args</span><span class="o">=()</span>, <span class="nv">limit</span><span class="o">=</span>None, <span class="nv">offset</span><span class="o">=</span>None<span class="o">))</span>
</span></span><span class="line"><span class="cl">Calling: SoftLayer_Virtual_Guest::getReverseDomainRecords<span class="o">(</span><span class="nv">id</span><span class="o">=</span>77460683, <span class="nv">mask</span><span class="o">=</span><span class="s1">&#39;&#39;</span>, <span class="nv">filter</span><span class="o">=</span><span class="s1">&#39;None&#39;</span>, <span class="nv">args</span><span class="o">=()</span>, <span class="nv">limit</span><span class="o">=</span>None, <span class="nv">offset</span><span class="o">=</span>None<span class="o">))</span>
</span></span><span class="line"><span class="cl">:..................:..............................................................:
</span></span><span class="line"><span class="cl">:       name       :                            value                             :
</span></span><span class="line"><span class="cl">:..................:..............................................................:
</span></span><span class="line"><span class="cl">:  execution_time  :                          2.020334s                           :
</span></span><span class="line"><span class="cl">:    api_calls     :        SoftLayer_Virtual_Guest::getObject <span class="o">(</span>1.515583s<span class="o">)</span>        :
</span></span><span class="line"><span class="cl">:                  : SoftLayer_Virtual_Guest::getReverseDomainRecords <span class="o">(</span>0.494480s<span class="o">)</span> :
</span></span><span class="line"><span class="cl">:     version      :                   softlayer-python/v5.7.2                    :
</span></span><span class="line"><span class="cl">:  python_version  :           3.7.3 <span class="o">(</span>default, Mar <span class="m">27</span> 2019, 09:23:15<span class="o">)</span>             :
</span></span><span class="line"><span class="cl">:                  :              <span class="o">[</span>Clang 10.0.1 <span class="o">(</span>clang-1001.0.46.3<span class="o">)]</span>              :
</span></span><span class="line"><span class="cl">: library_location : /Users/chris/Code/py3/lib/python3.7/site-packages/SoftLayer  :
</span></span><span class="line"><span class="cl">:..................:..............................................................:
</span></span></code></pre></div><p>The more <code>v</code> characters you add, the more verbose the output gets. If you use <code>-vvv</code> then you will get the equivalent cURL commands to make the same API calls, which should be clear enough for any developer to make a client against the API.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">$ slcli -vvv account summary
</span></span><span class="line"><span class="cl">curl -u <span class="nv">$SL_USER</span>:<span class="nv">$SL_APIKEY</span> -X GET -H <span class="s2">&#34;Accept: */*&#34;</span> -H <span class="s2">&#34;Accept-Encoding: gzip, deflate, compress&#34;</span>  <span class="s1">&#39;https://api.softlayer.com/rest/v3.1/SoftLayer_Account/getObject.json?objectMask=mask%5B%0A++++++++++++nextInvoiceTotalAmount%2C%0A++++++++++++pendingInvoice%5BinvoiceTotalAmount%5D%2C%0A++++++++++++blockDeviceTemplateGroupCount%2C%0A++++++++++++dedicatedHostCount%2C%0A++++++++++++domainCount%2C%0A++++++++++++hardwareCount%2C%0A++++++++++++networkStorageCount%2C%0A++++++++++++openTicketCount%2C%0A++++++++++++networkVlanCount%2C%0A++++++++++++subnetCount%2C%0A++++++++++++userCount%2C%0A++++++++++++virtualGuestCount%0A++++++++++++%5D&#39;</span>
</span></span></code></pre></div><p>In summary, this was an incredibly successful side project. What started as a small script for internal use turned into a Swiss army knife that was a completely new way to access all products that SoftLayer offered. I learned so much about maintaining an open-source project, choosing reliable libraries to build on, code quality/style, and so much more.</p>
<p>References:</p>
<ul>
<li>Github: <a href="https://github.com/softlayer/softlayer-python" rel="external">https://github.com/softlayer/softlayer-python</a></li>
<li>Documentation: <a href="https://softlayer-python.readthedocs.io/en/latest/" rel="external">https://softlayer-python.readthedocs.io/en/latest/</a></li>
</ul>
]]></content:encoded></item><item><title>SwFTP: SFTP/FTP Server For Openstack Swift</title><link>https://kmcd.dev/posts/swftp/</link><pubDate>Sun, 30 Jul 2023 00:00:00 +0000</pubDate><guid>https://kmcd.dev/posts/swftp/</guid><description> 
                &lt;p> &lt;img hspace="5" src="https://kmcd.dev/posts/swftp/cover_hu_f456d62d712f6990.webp" /> &lt;/p>
                
                Describing an old project of mine from 2014; an SFTP/FTP interface over an object storage API using Python Twisted.
                </description><content:encoded><![CDATA[<p>I used to work for a public cloud company called <a href="https://en.wikipedia.org/wiki/IBM_Cloud#SoftLayer" rel="external">SoftLayer</a>. Before <a href="https://docs.aws.amazon.com/AmazonS3/latest/API/" rel="external">S3’s API</a> was the de-facto API standard that object storage services used several object storage APIs seemed like they could claim that crown. The company I worked for, SoftLayer, had recently come out with an Object Storage service based on the <a href="https://wiki.openstack.org/wiki/Swift" rel="external">OpenStack Swift</a> project. It came with its API, which is great. However, at the time, it was hard to get buy-in from customers to use under-supported APIs. They’d have to use unfamiliar tooling or even develop tooling themselves just to transfer files around&hellip; And if they currently had a product that didn’t support OpenStack Swift they may just be stuck. So I was charged with coming up with a solution for these customers.</p>
<p>After testing out a few different ideas I ended up deciding on the SFTP protocol. Object Storage doesn’t translate 1:1 with a “normal” filesystem but it was close enough. This might seem a strange decision in modern days where everyone has S3 integration but this was done as a way to integrate with older products and give customers a much more familiar feel with interacting with their data. I decided to call the project <a href="https://github.com/softlayer/swftp" rel="external">SwFTP</a>.</p>
<div class="container">
  <pre class="mermaid">graph LR
    cyberduck((Cyberduck)) -- SFTP --> swftp((SwFTP))
    filezilla((FileZilla)) -- SFTP --> swftp((SwFTP))
    client((SFTP/FTP Client)) -- SFTP or FTP --> swftp((SwFTP))
    swftp((SwFTP)) -- HTTPS --> swift((Swift))
  </pre>
</div>
<p>I was all set. I needed to just&hellip; implement an SFTP server. Oof. That’s actually very challenging. You see, the <a href="https://www.ietf.org/rfc/rfc0913.txt" rel="external">SFTP protocol</a> has lived for over a decade at that point so it has gone through several RFC drafts and it supports various extensions&hellip; And it’s built on top of SSH, which is also fairly complex to implement. So I needed to use something more proven. I needed a library that I could plug in OpenStack Swift integration into without needing to implement the wire protocol itself. Again, at the time this kind of ability was fairly rare. I was very experienced with Python and a bit with Go (nowadays I would totally write this project in Go, using this <a href="https://pkg.go.dev/github.com/pkg/sftp" rel="external">SFTP library</a> and implementing the <code>fs.Walker</code> interface). That would have made my life so much simpler. But no, I was essentially stuck with Python’s <a href="https://twisted.org/" rel="external">Twisted</a>. Twisted has a library called <a href="https://docs.twisted.org/en/stable/api/twisted.conch.html" rel="external">&ldquo;Conche&rdquo;</a> which implements the SSH protocol and it allows you to hook into the SFTP subsystem, which is exactly what I needed. Twisted seemed to be the best option at the time, but it was (and still is) very hard to work with. The failure modes can be very complex. Plus, the SFTP protocol is fairly complex and SFTP clients will behave in vastly different ways. For instance, some clients, in order to maximize throughput, will concurrently send several batches of data at once when uploading a file without waiting for the acknowledgment to be received. We can’t behave similarly with an object storage API call so I needed to force the concurrent write requests to queue up properly until it was their turn to be sent down the wire to Swift. This, alone, is complex but I was also using an unfamiliar framework that has completely different sync primitives than I’ve used in the past&hellip; So I found this work to be very challenging&hellip; but in the end, it was very rewarding.</p>
<p>SwFTP was never the only project I was working on so my attention was split multiple ways. Despite that, after a year of working on this project, it was finally good enough to deploy to production as a supported service. Testing took a long time because, as I said, many SFTP clients behave very differently. I am happy about where I got the functional test framework since I was able to easily write code that would reproduce errors that we saw when testing, including some super complicated cases of race conditions. From this experience, I&rsquo;ve learned some very important lessons about functional and manual testing.</p>
<p>All-in-all, SwFTP ended up being a success and a lot of data was transferred using this service. Thanks to my manager and support from others in the company I was able to perform all of this iteration and development as an open source project. There were no SoftLayer-specific implementation details included here so others could (and did) deploy the project for their own OpenStack Swift clusters.</p>
<blockquote>
<p>By the way, if you’re having issues pronouncing “SwFTP” in your head then you aren’t alone. I used to call it something like “Swefteepee”.</p>
</blockquote>
<p>References:</p>
<ul>
<li>SwFTP Github - <a href="https://github.com/softlayer/swftp" rel="external">https://github.com/softlayer/swftp</a></li>
<li>Conch (SSH library) - <a href="https://docs.twisted.org/en/stable/api/twisted.conch.html" rel="external">https://docs.twisted.org/en/stable/api/twisted.conch.html</a></li>
<li>Writing a client using Conch - <a href="https://docs.twisted.org/en/twisted-18.9.0/conch/howto/conch_client.html" rel="external">https://docs.twisted.org/en/twisted-18.9.0/conch/howto/conch_client.html</a></li>
</ul>
]]></content:encoded></item></channel></rss>