<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0"><channel><title><![CDATA[Formaty Blog]]></title><description><![CDATA[Formaty Blog]]></description><link>https://formaty.hashnode.dev</link><image><url>https://cdn.hashnode.com/uploads/logos/6adef387a8ec3d13298abf34/64b017f5-7d04-418d-b0c5-5cb13c74a5c1.png</url><title>Formaty Blog</title><link>https://formaty.hashnode.dev</link></image><generator>RSS for Node</generator><lastBuildDate>Sun, 01 Nov 2026 03:50:25 GMT</lastBuildDate><atom:link href="https://formaty.hashnode.dev/rss.xml" rel="self" type="application/rss+xml"/><language><![CDATA[en]]></language><ttl>60</ttl><item><title><![CDATA[Diff two API responses without the noise]]></title><description><![CDATA[You changed a serializer, bumped a dependency, or moved an endpoint to a new service. Now you want to know one thing: did the response change? The usual…]]></description><link>https://formaty.hashnode.dev/diff-two-api-responses-without-the-noise</link><guid isPermaLink="true">https://formaty.hashnode.dev/diff-two-api-responses-without-the-noise</guid><category><![CDATA[json]]></category><category><![CDATA[APIs]]></category><category><![CDATA[Web Development]]></category><category><![CDATA[Testing]]></category><dc:creator><![CDATA[Kuldeep KUmawat]]></dc:creator><pubDate>Mon, 26 Oct 2026 06:36:51 GMT</pubDate><content:encoded><![CDATA[<p>You changed a serializer, bumped a dependency, or moved an endpoint to a new service. Now you want to know one thing: <strong>did the response change?</strong></p>
<p>The usual move is to save both responses and run <code>diff before.json after.json</code>. That works until it doesn't.</p>
<h2>Why text diffs lie about JSON</h2>
<p>Take this response before and after a refactor:</p>
<pre><code class="language-json">{ "id": 42, "name": "Alice", "role": "admin",
  "tags": ["beta", "staff"],
  "plan": { "tier": "pro", "seats": 5 } }
</code></pre>
<pre><code class="language-json">{ "id": 42, "name": "Alice", "roles": ["admin", "editor"],
  "tags": ["staff", "beta"],
  "plan": { "tier": "pro", "seats": "5" } }
</code></pre>
<p>A line diff has three problems here:</p>
<ol>
<li><strong>Formatting noise.</strong> If one side is minified or the keys come out in a different order, every line "changed".</li>
<li><strong>Reordered arrays look like edits.</strong> <code>["beta", "staff"]</code> vs <code>["staff", "beta"]</code> is usually the same set of tags, but it shows up as two changed lines.</li>
<li><strong>Type changes are nearly invisible.</strong> <code>5</code> vs <code>"5"</code> is two characters. In a 400-line response you will scroll right past it, and it's the one that breaks clients.</li>
</ol>
<h2>Diff the structure, not the text</h2>
<p>A structural diff parses both sides and compares values by path. Running the two responses above through one gives:</p>
<table>
<thead>
<tr>
<th>Path</th>
<th>Before</th>
<th>After</th>
<th>Change</th>
</tr>
</thead>
<tbody><tr>
<td><code>$.role</code></td>
<td><code>"admin"</code></td>
<td>(missing)</td>
<td>removed</td>
</tr>
<tr>
<td><code>$.roles</code></td>
<td>(missing)</td>
<td><code>["admin","editor"]</code></td>
<td>added</td>
</tr>
<tr>
<td><code>$.tags[0]</code></td>
<td><code>"beta"</code></td>
<td><code>"staff"</code></td>
<td>changed</td>
</tr>
<tr>
<td><code>$.tags[1]</code></td>
<td><code>"staff"</code></td>
<td><code>"beta"</code></td>
<td>changed</td>
</tr>
<tr>
<td><code>$.plan.seats</code></td>
<td><code>5</code></td>
<td><code>"5"</code></td>
<td>changed</td>
</tr>
</tbody></table>
<p>Whitespace and key order are gone from the picture. But the tags rows are still noise.</p>
<h2>Turn on order-insensitive arrays</h2>
<p>Compare arrays as sets instead of by index and the same diff becomes:</p>
<table>
<thead>
<tr>
<th>Path</th>
<th>Before</th>
<th>After</th>
<th>Change</th>
</tr>
</thead>
<tbody><tr>
<td><code>$.role</code></td>
<td><code>"admin"</code></td>
<td>(missing)</td>
<td>removed</td>
</tr>
<tr>
<td><code>$.roles</code></td>
<td>(missing)</td>
<td><code>["admin","editor"]</code></td>
<td>added</td>
</tr>
<tr>
<td><code>$.plan.seats</code></td>
<td><code>5</code></td>
<td><code>"5"</code></td>
<td>changed</td>
</tr>
</tbody></table>
<p>Three rows, and every one is real:</p>
<ul>
<li><code>role</code> was renamed to <code>roles</code> and became an array. Breaking change for every client reading <code>role</code>.</li>
<li><code>seats</code> went from number to string. Breaking change for anything doing math on it.</li>
</ul>
<p>For arrays of objects (say, a list of line items), matching elements by their <code>id</code> instead of by position gives the same benefit: inserting one item at the top doesn't mark every following item as changed.</p>
<h2>Doing it in the browser</h2>
<p>I built this into <a href="https://formaty.dev/json-diff">Formaty's JSON Diff</a>: paste the old response on the left, the new one on the right, and toggle order-insensitive comparison (arrays of objects are then matched by <code>id</code>). It shows side-by-side or inline highlighting plus the path-level change list. Everything runs locally, so production payloads never leave your machine.</p>
<p>If you'd rather script it, the same idea works in a test: parse both sides, walk them by path, and treat arrays as multisets unless order matters for that field.</p>
<h2>FAQ</h2>
<p><strong>Does key order matter?</strong> No. Objects are compared by key, so <code>{"a":1,"b":2}</code> equals <code>{"b":2,"a":1}</code>.</p>
<p><strong>Can I export the result?</strong> Yes. Copy or download the change list as a JSON report to attach to a PR or ticket.</p>
<hr />
<p>Formaty is free and open source (<a href="https://github.com/kdkumawat/formaty">GitHub</a>). <a href="https://youtu.be/G1DtZnSSZEM">60-second demo</a>.</p>
]]></content:encoded></item></channel></rss>