<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:content="http://purl.org/rss/1.0/modules/content/"><channel><title>Api-Design on Kinoko's TIL Log</title><link>https://kinoko-tech-blog-theta.vercel.app/tags/api-design/</link><description>Recent content in Api-Design on Kinoko's TIL Log</description><generator>Hugo -- gohugo.io</generator><language>en</language><managingEditor>pippimotta@gmail.com (Kinoko)</managingEditor><webMaster>pippimotta@gmail.com (Kinoko)</webMaster><copyright>&amp;copy; Brewed by Kinoko</copyright><lastBuildDate>Tue, 10 Mar 2026 00:00:00 +0000</lastBuildDate><atom:link href="https://kinoko-tech-blog-theta.vercel.app/tags/api-design/index.xml" rel="self" type="application/rss+xml"/><item><title>gRPC vs HTTP</title><link>https://kinoko-tech-blog-theta.vercel.app/posts/grpc-vs-http/</link><pubDate>Tue, 10 Mar 2026 00:00:00 +0000</pubDate><author>pippimotta@gmail.com (Kinoko)</author><guid>https://kinoko-tech-blog-theta.vercel.app/posts/grpc-vs-http/</guid><description>&lt;h2 id="the-point"&gt;The Point&lt;/h2&gt;
&lt;p&gt;gRPC is an RPC framework developed by Google. It uses HTTP/2 for transport and Protobuf for serialization, making it faster and more structured than traditional REST/HTTP+JSON &amp;ndash; but less readable, so it is mainly used for internal microservice communication.&lt;/p&gt;
&lt;h2 id="explanation"&gt;Explanation&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;Traditional HTTP (REST + JSON)&lt;/strong&gt;&lt;/p&gt;





&lt;pre tabindex="0"&gt;&lt;code&gt;Client → POST /users HTTP/1.1
 Content-Type: application/json
 {&amp;#34;name&amp;#34;: &amp;#34;Alice&amp;#34;, &amp;#34;age&amp;#34;: 30}

Server → 200 OK
 {&amp;#34;id&amp;#34;: 1, &amp;#34;name&amp;#34;: &amp;#34;Alice&amp;#34;}&lt;/code&gt;&lt;/pre&gt;&lt;ul&gt;
&lt;li&gt;Operations are defined by URL + HTTP method&lt;/li&gt;
&lt;li&gt;Data format is JSON (human-readable text)&lt;/li&gt;
&lt;li&gt;Based on HTTP/1.1 (each request uses an independent connection)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;How gRPC does it&lt;/strong&gt;&lt;/p&gt;</description><content:encoded><![CDATA[<h2 id="the-point">The Point</h2>
<p>gRPC is an RPC framework developed by Google. It uses HTTP/2 for transport and Protobuf for serialization, making it faster and more structured than traditional REST/HTTP+JSON &ndash; but less readable, so it is mainly used for internal microservice communication.</p>
<h2 id="explanation">Explanation</h2>
<p><strong>Traditional HTTP (REST + JSON)</strong></p>





<pre tabindex="0"><code>Client → POST /users HTTP/1.1
         Content-Type: application/json
         {&#34;name&#34;: &#34;Alice&#34;, &#34;age&#34;: 30}

Server → 200 OK
         {&#34;id&#34;: 1, &#34;name&#34;: &#34;Alice&#34;}</code></pre><ul>
<li>Operations are defined by URL + HTTP method</li>
<li>Data format is JSON (human-readable text)</li>
<li>Based on HTTP/1.1 (each request uses an independent connection)</li>
</ul>
<p><strong>How gRPC does it</strong></p>





<pre tabindex="0"><code>Client → calls UserService.CreateUser(CreateUserRequest)
Server → returns CreateUserResponse</code></pre><ul>
<li>Services and messages are defined in <code>.proto</code> files; calling them feels like calling a local function</li>
<li>Data format is Protobuf (binary, not directly readable)</li>
<li>Based on HTTP/2 (multiplexing &ndash; a single connection handles multiple requests)</li>
</ul>
<p><strong>Core differences</strong></p>
<table>
	<thead>
			<tr>
					<th></th>
					<th>REST + JSON</th>
					<th>gRPC</th>
			</tr>
	</thead>
	<tbody>
			<tr>
					<td>Protocol</td>
					<td>HTTP/1.1</td>
					<td>HTTP/2</td>
			</tr>
			<tr>
					<td>Data format</td>
					<td>JSON (text)</td>
					<td>Protobuf (binary)</td>
			</tr>
			<tr>
					<td>Schema</td>
					<td>Not enforced</td>
					<td>Enforced by <code>.proto</code></td>
			</tr>
			<tr>
					<td>Performance</td>
					<td>Slower</td>
					<td>Fast (binary + multiplexing)</td>
			</tr>
			<tr>
					<td>Readability</td>
					<td>High, easy to debug</td>
					<td>Low, needs tooling</td>
			</tr>
			<tr>
					<td>Browser support</td>
					<td>Native</td>
					<td>Requires grpc-web</td>
			</tr>
			<tr>
					<td>Best for</td>
					<td>External public APIs</td>
					<td>Internal microservices</td>
			</tr>
	</tbody>
</table>
<h2 id="knowledge-sugar">Knowledge Sugar</h2>
<p><strong>What is HTTP/2 multiplexing?</strong></p>
<p>In HTTP/1.1, each request must wait for the previous response before sending the next one (or open a new connection). HTTP/2 can handle multiple request/response pairs in parallel over a single connection, significantly reducing latency.</p>
<p><strong>Streaming</strong></p>
<p>gRPC supports four communication modes, which are hard to do with REST:</p>





<pre tabindex="0"><code>Unary:              one request → one response (most common)
Server streaming:   one request → multiple responses (e.g. real-time push)
Client streaming:   multiple requests → one response (e.g. uploading chunked data)
Bidirectional:      multiple requests ↔ multiple responses (e.g. real-time chat)</code></pre><p><strong>Relation to Protobuf</strong>
gRPC&rsquo;s data format is Protobuf &ndash; see the earlier post on Protobuf Reserved Fields &amp; API Versioning for more context.</p>
]]></content:encoded></item><item><title>Accept-Language Header</title><link>https://kinoko-tech-blog-theta.vercel.app/posts/accept-language-header/</link><pubDate>Fri, 06 Mar 2026 00:00:00 +0000</pubDate><author>pippimotta@gmail.com (Kinoko)</author><guid>https://kinoko-tech-blog-theta.vercel.app/posts/accept-language-header/</guid><description>&lt;h2 id="the-point"&gt;The Point&lt;/h2&gt;
&lt;p&gt;Language preference does not need to be a request parameter. HTTP already has the &lt;code&gt;Accept-Language&lt;/code&gt; header for this &amp;ndash; the server reads it directly from the header, and the client does not need to pass an extra field.&lt;/p&gt;
&lt;h2 id="explanation"&gt;Explanation&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;What is &lt;code&gt;Accept-Language&lt;/code&gt;?&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;It is a request header defined by the HTTP standard (RFC 7231) that tells the server which languages the client prefers. Browsers set it automatically based on the user&amp;rsquo;s system language. API clients can set it manually:&lt;/p&gt;</description><content:encoded><![CDATA[<h2 id="the-point">The Point</h2>
<p>Language preference does not need to be a request parameter. HTTP already has the <code>Accept-Language</code> header for this &ndash; the server reads it directly from the header, and the client does not need to pass an extra field.</p>
<h2 id="explanation">Explanation</h2>
<p><strong>What is <code>Accept-Language</code>?</strong></p>
<p>It is a request header defined by the HTTP standard (RFC 7231) that tells the server which languages the client prefers. Browsers set it automatically based on the user&rsquo;s system language. API clients can set it manually:</p>





<pre tabindex="0"><code>Accept-Language: zh-TW,zh;q=0.9,en;q=0.8</code></pre><ul>
<li>Multiple languages are separated by commas</li>
<li><code>q</code> is the quality factor, ranging from 0 to 1 (default 1.0) &ndash; higher means more preferred</li>
<li>The example above: prefer <code>zh-TW</code>, then <code>zh</code>, then <code>en</code></li>
</ul>
<p><strong>How does the server read it?</strong></p>
<p>In Go, just get it from the header and parse:</p>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="ln">1</span><span class="cl"><span class="nx">lang</span><span class="w"> </span><span class="o">:=</span><span class="w"> </span><span class="nx">r</span><span class="p">.</span><span class="nx">Header</span><span class="p">.</span><span class="nf">Get</span><span class="p">(</span><span class="s">&#34;Accept-Language&#34;</span><span class="p">)</span><span class="w">
</span></span></span><span class="line"><span class="ln">2</span><span class="cl"><span class="c1">// &#34;zh-TW,zh;q=0.9,en;q=0.8&#34;</span><span class="w">
</span></span></span><span class="line"><span class="ln">3</span><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="ln">4</span><span class="cl"><span class="c1">// Simple approach: take the first one (highest priority)</span><span class="w">
</span></span></span><span class="line"><span class="ln">5</span><span class="cl"><span class="nx">preferred</span><span class="w"> </span><span class="o">:=</span><span class="w"> </span><span class="nx">strings</span><span class="p">.</span><span class="nf">Split</span><span class="p">(</span><span class="nx">lang</span><span class="p">,</span><span class="w"> </span><span class="s">&#34;,&#34;</span><span class="p">)[</span><span class="mi">0</span><span class="p">]</span><span class="w">  </span><span class="c1">// &#34;zh-TW&#34;</span></span></span></code></pre></div><p>In practice you would use an existing library to parse the full q-value ordering, match against the server&rsquo;s supported languages, and fall back to a default if nothing matches.</p>
<p><strong>Why is this better than a request parameter?</strong></p>
<table>
	<thead>
			<tr>
					<th></th>
					<th>Request param <code>?lang=zh-TW</code></th>
					<th><code>Accept-Language</code> header</th>
			</tr>
	</thead>
	<tbody>
			<tr>
					<td>Standardization</td>
					<td>Custom format, varies across APIs</td>
					<td>HTTP standard, all clients understand</td>
			</tr>
			<tr>
					<td>Browser support</td>
					<td>Client must add it manually</td>
					<td>Browser sends it automatically</td>
			</tr>
			<tr>
					<td>Semantics</td>
					<td>Mixed in with business parameters</td>
					<td>Clearly belongs to content negotiation</td>
			</tr>
			<tr>
					<td>API cleanliness</td>
					<td>Every endpoint must handle this field</td>
					<td>Handle once in middleware</td>
			</tr>
	</tbody>
</table>
<h2 id="knowledge-sugar">Knowledge Sugar</h2>
<p><strong>Content Negotiation</strong></p>
<p><code>Accept-Language</code> is part of HTTP content negotiation &ndash; the client tells the server &ldquo;here&rsquo;s what I can accept&rdquo; and the server picks the best match from what it can provide. Related headers in the same family:</p>
<ul>
<li><code>Accept</code>: preferred response format (<code>application/json</code>, <code>text/html</code>)</li>
<li><code>Accept-Encoding</code>: preferred compression (<code>gzip</code>, <code>br</code>)</li>
</ul>
<p><strong>Usually handled in middleware</strong></p>
<p>No need for every handler to read the header itself. Parse it in middleware, put the result in the context, and let downstream handlers read from there:</p>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="ln">1</span><span class="cl"><span class="kd">func</span><span class="w"> </span><span class="nf">LanguageMiddleware</span><span class="p">(</span><span class="nx">next</span><span class="w"> </span><span class="nx">http</span><span class="p">.</span><span class="nx">Handler</span><span class="p">)</span><span class="w"> </span><span class="nx">http</span><span class="p">.</span><span class="nx">Handler</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="ln">2</span><span class="cl"><span class="w">    </span><span class="k">return</span><span class="w"> </span><span class="nx">http</span><span class="p">.</span><span class="nf">HandlerFunc</span><span class="p">(</span><span class="kd">func</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="ln">3</span><span class="cl"><span class="w">        </span><span class="nx">lang</span><span class="w"> </span><span class="o">:=</span><span class="w"> </span><span class="nf">parseAcceptLanguage</span><span class="p">(</span><span class="nx">r</span><span class="p">.</span><span class="nx">Header</span><span class="p">.</span><span class="nf">Get</span><span class="p">(</span><span class="s">&#34;Accept-Language&#34;</span><span class="p">))</span><span class="w">
</span></span></span><span class="line"><span class="ln">4</span><span class="cl"><span class="w">        </span><span class="nx">ctx</span><span class="w"> </span><span class="o">:=</span><span class="w"> </span><span class="nx">context</span><span class="p">.</span><span class="nf">WithValue</span><span class="p">(</span><span class="nx">r</span><span class="p">.</span><span class="nf">Context</span><span class="p">(),</span><span class="w"> </span><span class="s">&#34;lang&#34;</span><span class="p">,</span><span class="w"> </span><span class="nx">lang</span><span class="p">)</span><span class="w">
</span></span></span><span class="line"><span class="ln">5</span><span class="cl"><span class="w">        </span><span class="nx">next</span><span class="p">.</span><span class="nf">ServeHTTP</span><span class="p">(</span><span class="nx">w</span><span class="p">,</span><span class="w"> </span><span class="nx">r</span><span class="p">.</span><span class="nf">WithContext</span><span class="p">(</span><span class="nx">ctx</span><span class="p">))</span><span class="w">
</span></span></span><span class="line"><span class="ln">6</span><span class="cl"><span class="w">    </span><span class="p">})</span><span class="w">
</span></span></span><span class="line"><span class="ln">7</span><span class="cl"><span class="p">}</span></span></span></code></pre></div>]]></content:encoded></item><item><title>API Request Validation</title><link>https://kinoko-tech-blog-theta.vercel.app/posts/api-request-validation/</link><pubDate>Wed, 04 Mar 2026 00:00:00 +0000</pubDate><author>pippimotta@gmail.com (Kinoko)</author><guid>https://kinoko-tech-blog-theta.vercel.app/posts/api-request-validation/</guid><description>&lt;h2 id="the-point"&gt;The Point&lt;/h2&gt;
&lt;p&gt;API request validation should happen on both sides, but with different roles: the client provides instant UX feedback, while the server enforces all business logic validation. The server side is mandatory because client validation can be bypassed.&lt;/p&gt;
&lt;h2 id="explanation"&gt;Explanation&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;Why can&amp;rsquo;t you skip server-side validation?&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Anyone can skip the frontend and hit the API directly. Client-side validation is just a UX optimization; the server is the real enforcement layer.&lt;/p&gt;</description><content:encoded><![CDATA[<h2 id="the-point">The Point</h2>
<p>API request validation should happen on both sides, but with different roles: the client provides instant UX feedback, while the server enforces all business logic validation. The server side is mandatory because client validation can be bypassed.</p>
<h2 id="explanation">Explanation</h2>
<p><strong>Why can&rsquo;t you skip server-side validation?</strong></p>
<p>Anyone can skip the frontend and hit the API directly. Client-side validation is just a UX optimization; the server is the real enforcement layer.</p>
<p><strong>Cross-field Validation</strong></p>
<p>When fields have logical dependencies (e.g. <code>discount</code> cannot be 0 when <code>eligibility</code> is true), handle it in a <code>Validate()</code> method on the server side:</p>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="ln"> 1</span><span class="cl"><span class="kd">type</span><span class="w"> </span><span class="nx">CreateDiscountRequest</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="ln"> 2</span><span class="cl"><span class="w">    </span><span class="nx">Eligibility</span><span class="w"> </span><span class="kt">bool</span><span class="w">    </span><span class="s">`json:&#34;eligibility&#34;`</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 3</span><span class="cl"><span class="w">    </span><span class="nx">Discount</span><span class="w">    </span><span class="kt">float64</span><span class="w"> </span><span class="s">`json:&#34;discount&#34;`</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 4</span><span class="cl"><span class="p">}</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 5</span><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="ln"> 6</span><span class="cl"><span class="kd">func</span><span class="w"> </span><span class="p">(</span><span class="nx">r</span><span class="w"> </span><span class="nx">CreateDiscountRequest</span><span class="p">)</span><span class="w"> </span><span class="nf">Validate</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="ln"> 7</span><span class="cl"><span class="w">    </span><span class="k">if</span><span class="w"> </span><span class="nx">r</span><span class="p">.</span><span class="nx">Eligibility</span><span class="w"> </span><span class="o">&amp;&amp;</span><span class="w"> </span><span class="nx">r</span><span class="p">.</span><span class="nx">Discount</span><span class="w"> </span><span class="o">==</span><span class="w"> </span><span class="mi">0</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 8</span><span class="cl"><span class="w">        </span><span class="k">return</span><span class="w"> </span><span class="nx">errors</span><span class="p">.</span><span class="nf">New</span><span class="p">(</span><span class="s">&#34;discount cannot be 0 when eligibility is true&#34;</span><span class="p">)</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 9</span><span class="cl"><span class="w">    </span><span class="p">}</span><span class="w">
</span></span></span><span class="line"><span class="ln">10</span><span class="cl"><span class="w">    </span><span class="k">return</span><span class="w"> </span><span class="kc">nil</span><span class="w">
</span></span></span><span class="line"><span class="ln">11</span><span class="cl"><span class="p">}</span></span></span></code></pre></div><p>Call it in the handler right after decoding:</p>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="ln">1</span><span class="cl"><span class="kd">var</span><span class="w"> </span><span class="nx">req</span><span class="w"> </span><span class="nx">CreateDiscountRequest</span><span class="w">
</span></span></span><span class="line"><span class="ln">2</span><span class="cl"><span class="nx">json</span><span class="p">.</span><span class="nf">NewDecoder</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="nf">Decode</span><span class="p">(</span><span class="o">&amp;</span><span class="nx">req</span><span class="p">)</span><span class="w">
</span></span></span><span class="line"><span class="ln">3</span><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="ln">4</span><span class="cl"><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">req</span><span class="p">.</span><span class="nf">Validate</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="ln">5</span><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="nx">err</span><span class="p">.</span><span class="nf">Error</span><span class="p">(),</span><span class="w"> </span><span class="nx">http</span><span class="p">.</span><span class="nx">StatusBadRequest</span><span class="p">)</span><span class="w"> </span><span class="c1">// 400</span><span class="w">
</span></span></span><span class="line"><span class="ln">6</span><span class="cl"><span class="w">    </span><span class="k">return</span><span class="w">
</span></span></span><span class="line"><span class="ln">7</span><span class="cl"><span class="p">}</span></span></span></code></pre></div><h2 id="knowledge-sugar">Knowledge Sugar</h2>
<p><strong>Should you use a validation library?</strong></p>
<p>For simple cases, a hand-written <code>Validate()</code> is enough. When rules get complex or you have many structs, consider <code>go-playground/validator</code>, which supports struct tags for basic rules:</p>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="ln">1</span><span class="cl"><span class="kd">type</span><span class="w"> </span><span class="nx">Request</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="ln">2</span><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">`validate:&#34;required&#34;`</span><span class="w">
</span></span></span><span class="line"><span class="ln">3</span><span class="cl"><span class="w">    </span><span class="nx">Email</span><span class="w"> </span><span class="kt">string</span><span class="w">  </span><span class="s">`validate:&#34;required,email&#34;`</span><span class="w">
</span></span></span><span class="line"><span class="ln">4</span><span class="cl"><span class="w">    </span><span class="nx">Age</span><span class="w">   </span><span class="kt">int</span><span class="w">     </span><span class="s">`validate:&#34;gte=0,lte=130&#34;`</span><span class="w">
</span></span></span><span class="line"><span class="ln">5</span><span class="cl"><span class="p">}</span></span></span></code></pre></div><p>But cross-field logic (dependencies between fields) still needs a custom validator &ndash; no library handles your business logic for you.</p>
<p><strong>Return 400 or 422?</strong></p>
<ul>
<li><code>400 Bad Request</code>: malformed input, parse failure</li>
<li><code>422 Unprocessable Entity</code>: valid format but failed business logic validation (like the cross-field error above)</li>
</ul>
<p>Semantically 422 is more precise, but in practice many APIs use 400 for everything. Follow your team&rsquo;s convention.</p>
]]></content:encoded></item><item><title>BFF 101</title><link>https://kinoko-tech-blog-theta.vercel.app/posts/bff101/</link><pubDate>Mon, 02 Mar 2026 00:00:00 +0000</pubDate><author>pippimotta@gmail.com (Kinoko)</author><guid>https://kinoko-tech-blog-theta.vercel.app/posts/bff101/</guid><description>&lt;h2 id="the-point"&gt;The Point&lt;/h2&gt;
&lt;p&gt;BFF (Backend For Frontend) is an architecture pattern where you build a dedicated backend layer for each frontend client, instead of having all clients share a single API.&lt;/p&gt;
&lt;h2 id="explanation"&gt;Explanation&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;Why do you need BFF?&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Different clients have very different data needs. Take an e-commerce platform as an example:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Mobile app: small screen, limited bandwidth, needs only a few compact fields&lt;/li&gt;
&lt;li&gt;Web browser: can show richer data, needs more fields&lt;/li&gt;
&lt;li&gt;Third-party partners: yet another set of data format requirements&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;With a single shared API, two problems arise:&lt;/p&gt;</description><content:encoded><![CDATA[<h2 id="the-point">The Point</h2>
<p>BFF (Backend For Frontend) is an architecture pattern where you build a dedicated backend layer for each frontend client, instead of having all clients share a single API.</p>
<h2 id="explanation">Explanation</h2>
<p><strong>Why do you need BFF?</strong></p>
<p>Different clients have very different data needs. Take an e-commerce platform as an example:</p>
<ul>
<li>Mobile app: small screen, limited bandwidth, needs only a few compact fields</li>
<li>Web browser: can show richer data, needs more fields</li>
<li>Third-party partners: yet another set of data format requirements</li>
</ul>
<p>With a single shared API, two problems arise:</p>
<ul>
<li><strong>Over-fetching</strong>: returning fields the client does not use</li>
<li><strong>Under-fetching</strong>: one request is not enough, so the client sends multiple requests</li>
</ul>
<p>BFF adds a layer between clients and backend microservices:</p>





<pre tabindex="0"><code>Mobile App  →  Mobile BFF  ┐
Web App     →  Web BFF     ├─→  microservices
Partner API →  Partner BFF ┘</code></pre><p>Each BFF handles:</p>
<ol>
<li>Aggregating responses from multiple microservices (one request instead of many)</li>
<li>Trimming data to the format that specific client needs</li>
<li>Client-specific logic (e.g. mobile pagination)</li>
</ol>
<p><strong>Who maintains the BFF?</strong></p>
<p>Usually the frontend team, so they can adjust the API format on their own without waiting for backend changes.</p>
<h2 id="knowledge-sugar">Knowledge Sugar</h2>
<p><strong>BFF vs API Gateway</strong></p>
<p>These are easy to confuse, but they serve different purposes:</p>
<table>
	<thead>
			<tr>
					<th></th>
					<th>BFF</th>
					<th>API Gateway</th>
			</tr>
	</thead>
	<tbody>
			<tr>
					<td>Purpose</td>
					<td>Trim data for a specific client</td>
					<td>Traffic routing, auth, rate limiting</td>
			</tr>
			<tr>
					<td>Maintained by</td>
					<td>Frontend team</td>
					<td>Platform / infra team</td>
			</tr>
			<tr>
					<td>Count</td>
					<td>One per client type</td>
					<td>Usually one</td>
			</tr>
	</tbody>
</table>
<p>In practice they can coexist &ndash; the API Gateway sits in front handling common concerns, while BFFs sit behind it doing client-specific data aggregation.</p>
<p><strong>When do you not need BFF?</strong></p>
<ul>
<li>Only one client type (e.g. web only)</li>
<li>The backend is a monolith with nothing to aggregate</li>
<li>The team is small and maintaining multiple BFFs costs more than it saves</li>
</ul>
]]></content:encoded></item><item><title>Protobuf Reserved Fields &amp; API Versioning</title><link>https://kinoko-tech-blog-theta.vercel.app/posts/protobuf-reserved-fields-api-versioning/</link><pubDate>Fri, 27 Feb 2026 00:00:00 +0000</pubDate><author>pippimotta@gmail.com (Kinoko)</author><guid>https://kinoko-tech-blog-theta.vercel.app/posts/protobuf-reserved-fields-api-versioning/</guid><description>&lt;h2 id="the-point"&gt;The Point&lt;/h2&gt;
&lt;p&gt;When removing a response field from a protobuf message, you cannot simply delete it &amp;ndash; you must mark it as &lt;code&gt;reserved&lt;/code&gt;. Otherwise, if a future field reuses the same field number, old clients will misinterpret the new field&amp;rsquo;s value as the old field.&lt;/p&gt;
&lt;h2 id="explanation"&gt;Explanation&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;Why is &lt;code&gt;reserved&lt;/code&gt; needed?&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Protobuf identifies data on the wire by &lt;strong&gt;field number&lt;/strong&gt;, not field name. So when you:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Originally have &lt;code&gt;string old_field = 3;&lt;/code&gt; in a response message&lt;/li&gt;
&lt;li&gt;Integrate a new external service, the backend logic changes, this field is no longer populated, so you delete it&lt;/li&gt;
&lt;li&gt;Later add &lt;code&gt;int32 new_field = 3;&lt;/code&gt; (reusing number 3)&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;At this point, clients still using the old proto definition will receive the response and try to read &lt;code&gt;new_field&lt;/code&gt;&amp;rsquo;s value as &lt;code&gt;old_field&lt;/code&gt; &amp;ndash; the type mismatch causes a crash.&lt;/p&gt;</description><content:encoded><![CDATA[<h2 id="the-point">The Point</h2>
<p>When removing a response field from a protobuf message, you cannot simply delete it &ndash; you must mark it as <code>reserved</code>. Otherwise, if a future field reuses the same field number, old clients will misinterpret the new field&rsquo;s value as the old field.</p>
<h2 id="explanation">Explanation</h2>
<p><strong>Why is <code>reserved</code> needed?</strong></p>
<p>Protobuf identifies data on the wire by <strong>field number</strong>, not field name. So when you:</p>
<ol>
<li>Originally have <code>string old_field = 3;</code> in a response message</li>
<li>Integrate a new external service, the backend logic changes, this field is no longer populated, so you delete it</li>
<li>Later add <code>int32 new_field = 3;</code> (reusing number 3)</li>
</ol>
<p>At this point, clients still using the old proto definition will receive the response and try to read <code>new_field</code>&rsquo;s value as <code>old_field</code> &ndash; the type mismatch causes a crash.</p>
<p><strong>Correct approach: mark it as <code>reserved</code></strong></p>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-protobuf" data-lang="protobuf"><span class="line"><span class="ln">1</span><span class="cl"><span class="kd">message</span> <span class="nc">MyResponse</span> <span class="p">{</span>
</span></span><span class="line"><span class="ln">2</span><span class="cl">  <span class="k">reserved</span> <span class="mi">3</span><span class="p">,</span> <span class="mi">5</span><span class="p">;</span>                    <span class="c1">// reserve these field numbers from reuse
</span></span></span><span class="line"><span class="ln">3</span><span class="cl">  <span class="k">reserved</span> <span class="s">&#34;old_field&#34;</span><span class="p">,</span> <span class="s">&#34;another&#34;</span><span class="p">;</span>  <span class="c1">// also reserve the field names (prevents accidental reuse in code)
</span></span></span><span class="line"><span class="ln">4</span><span class="cl">
</span></span><span class="line"><span class="ln">5</span><span class="cl">  <span class="kt">string</span> <span class="n">active_field</span> <span class="o">=</span> <span class="mi">1</span><span class="p">;</span>
</span></span><span class="line"><span class="ln">6</span><span class="cl">  <span class="kt">int32</span> <span class="n">other_field</span> <span class="o">=</span> <span class="mi">2</span><span class="p">;</span>
</span></span><span class="line"><span class="ln">7</span><span class="cl"><span class="p">}</span></span></span></code></pre></div><p>With <code>reserved</code> in place, any attempt to reuse those numbers or names will cause a <code>protoc</code> compilation error.</p>
<hr>
<p><strong>What is the relationship between v2alpha and v2beta?</strong></p>
<p>These are API version <strong>stability labels</strong>, following Google AIP (API Improvement Proposals) naming conventions, commonly seen in gRPC / proto package naming:</p>
<table>
	<thead>
			<tr>
					<th>Version</th>
					<th>Meaning</th>
			</tr>
	</thead>
	<tbody>
			<tr>
					<td><code>v2alpha</code> / <code>v2alpha1</code></td>
					<td>Experimental; may change drastically or be removed at any time; no backward compatibility guarantee</td>
			</tr>
			<tr>
					<td><code>v2beta</code> / <code>v2beta1</code></td>
					<td>Feature is mostly finalized, but details may still change; usually has compatibility commitments but incomplete</td>
			</tr>
			<tr>
					<td><code>v2</code></td>
					<td>Stable release; full backward compatibility guarantee</td>
			</tr>
	</tbody>
</table>
<p>The typical progression is: <code>v2alpha1 -&gt; v2alpha2 -&gt; v2beta1 -&gt; v2beta2 -&gt; v2</code></p>
<p><strong>How it looks in proto files:</strong></p>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-protobuf" data-lang="protobuf"><span class="line"><span class="ln">1</span><span class="cl"><span class="c1">// Experimental version
</span></span></span><span class="line"><span class="ln">2</span><span class="cl"><span class="kn">package</span> <span class="nn">mycompany.myservice.v2alpha1</span><span class="p">;</span>
</span></span><span class="line"><span class="ln">3</span><span class="cl">
</span></span><span class="line"><span class="ln">4</span><span class="cl"><span class="c1">// Feature-complete but still being polished
</span></span></span><span class="line"><span class="ln">5</span><span class="cl"><span class="kn">package</span> <span class="nn">mycompany.myservice.v2beta1</span><span class="p">;</span>
</span></span><span class="line"><span class="ln">6</span><span class="cl">
</span></span><span class="line"><span class="ln">7</span><span class="cl"><span class="c1">// Stable release
</span></span></span><span class="line"><span class="ln">8</span><span class="cl"><span class="kn">package</span> <span class="nn">mycompany.myservice.v2</span><span class="p">;</span></span></span></code></pre></div><p>Different versioned packages are <strong>completely independent namespaces</strong> that can coexist, allowing old and new clients to each use their corresponding version.</p>
<h2 id="knowledge-sugar">Knowledge Sugar</h2>
<p><strong>Two ways to write <code>reserved</code>, and they can be separate or combined:</strong></p>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-protobuf" data-lang="protobuf"><span class="line"><span class="ln">1</span><span class="cl"><span class="k">reserved</span> <span class="mi">1</span> <span class="k">to</span> <span class="mi">3</span><span class="p">;</span>       <span class="c1">// contiguous range
</span></span></span><span class="line"><span class="ln">2</span><span class="cl"><span class="k">reserved</span> <span class="mi">1</span><span class="p">,</span> <span class="mi">2</span><span class="p">,</span> <span class="mi">3</span><span class="p">;</span>      <span class="c1">// individual listing
</span></span></span><span class="line"><span class="ln">3</span><span class="cl"><span class="k">reserved</span> <span class="s">&#34;foo&#34;</span><span class="p">,</span> <span class="s">&#34;bar&#34;</span><span class="p">;</span> <span class="o">//</span> <span class="n">reserve</span> <span class="n">names</span> <span class="p">(</span><span class="n">recommended</span> <span class="k">to</span> <span class="n">do</span> <span class="n">both</span><span class="p">,</span> <span class="n">prevents</span> <span class="n">typo</span> <span class="n">reuse</span><span class="p">)</span></span></span></code></pre></div><p><strong>Difference from <code>deprecated</code>:</strong></p>
<ul>
<li><code>deprecated = true</code> is a human-readable warning telling developers &ldquo;this field is not recommended anymore,&rdquo; but it still works</li>
<li><code>reserved</code> is enforced at compile time &ndash; it completely blocks any reuse</li>
</ul>
<p><strong>Practical mindset when designing new proto contracts:</strong></p>
<ul>
<li>A field number, once released, is forever &ndash; before deleting a field, decide whether it needs <code>reserved</code></li>
<li>Alpha/Beta versions give you room to experiment; once you reach a stable <code>v2</code>, removing or changing fields requires a formal deprecation process</li>
</ul>
<p><strong>Further reading:</strong></p>
<ul>
<li>Google AIP-180: definition of breaking vs non-breaking changes</li>
<li><code>oneof</code> field numbers in protobuf also need <code>reserved</code></li>
<li>Buf (buf.build): a protobuf linter that can automatically detect breaking changes</li>
</ul>
]]></content:encoded></item></channel></rss>