<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Tahmid</title>
    <description>The latest articles on DEV Community by Tahmid (@pioneer10).</description>
    <link>https://dev.to/pioneer10</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F3886334%2Fa4efc629-8897-42e4-8973-bdfdaa5a25b5.png</url>
      <title>DEV Community: Tahmid</title>
      <link>https://dev.to/pioneer10</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/pioneer10"/>
    <language>en</language>
    <item>
      <title>How to Convert JSON to YAML (and Back) Without Writing a Single Line of Code</title>
      <dc:creator>Tahmid</dc:creator>
      <pubDate>Sun, 03 May 2026 01:30:10 +0000</pubDate>
      <link>https://dev.to/pioneer10/how-to-convert-json-to-yaml-and-back-without-writing-a-single-line-of-code-5655</link>
      <guid>https://dev.to/pioneer10/how-to-convert-json-to-yaml-and-back-without-writing-a-single-line-of-code-5655</guid>
      <description>&lt;p&gt;You're staring at a JSON API response and you need to paste it into a Kubernetes ConfigMap. Or your colleague sent you a YAML Helm values file and your integration test expects JSON. Either way, manually rewriting the format is tedious, error-prone, and a genuine waste of your afternoon.&lt;/p&gt;

&lt;p&gt;This is one of those tasks that sounds simple but hides dozens of small traps: indentation levels, quoted strings that need to stay quoted, booleans that change meaning, and nested arrays that look completely different in each format. Let's fix it properly.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why JSON and YAML Keep Colliding
&lt;/h2&gt;

&lt;p&gt;JSON and YAML represent the same data model — key-value pairs, arrays, nested objects — but they express it differently. JSON uses braces and brackets; YAML uses indentation and dashes. This makes them structurally compatible but visually incompatible.&lt;/p&gt;

&lt;p&gt;The collision happens most often in these real-world scenarios:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;API responses → Kubernetes manifests.&lt;/strong&gt; You fetch config data from an API (JSON) and need to embed it in a ConfigMap or Secret (YAML).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;GitHub Actions / CI pipelines.&lt;/strong&gt; Workflow files are YAML, but tool configs (ESLint, Prettier, TypeScript) often live in JSON.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Helm chart values.&lt;/strong&gt; Default values are YAML; your templating logic might generate JSON alongside them.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Ansible playbooks.&lt;/strong&gt; Variables defined in JSON need to flow into YAML playbook tasks.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  The Conversion Is Mechanical — But Still Fiddly
&lt;/h2&gt;

&lt;p&gt;Here's what the same data looks like in both formats:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;JSON:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"server"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"host"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"api.example.com"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"port"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;8080&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"tls"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"retries"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"tags"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"production"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"us-east-1"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;YAML equivalent:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;server&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;host&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;api.example.com&lt;/span&gt;
  &lt;span class="na"&gt;port&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;8080&lt;/span&gt;
  &lt;span class="na"&gt;tls&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
&lt;span class="na"&gt;retries&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;3&lt;/span&gt;
&lt;span class="na"&gt;tags&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;production&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;us-east-1&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Notice the changes: no braces, no quotes around plain strings, arrays become dash-prefixed lists, and the entire structure depends on consistent indentation. One wrong space and your YAML parser throws a fit.&lt;/p&gt;

&lt;p&gt;Going the other direction is just as mechanical — but converting by hand still risks mistakes, especially with deeply nested objects.&lt;/p&gt;

&lt;h2&gt;
  
  
  Converting in the Browser: The Fast Path
&lt;/h2&gt;

&lt;p&gt;Instead of writing conversion code every time, the &lt;a href="https://jsonindenter.com/json-to-yaml?utm_source=devto&amp;amp;utm_medium=article&amp;amp;utm_campaign=json-to-yaml-conversion" rel="noopener noreferrer"&gt;JSON to YAML Converter&lt;/a&gt; handles this in your browser. Paste your JSON on the left, get clean YAML on the right. No sign-up, nothing leaves your browser.&lt;/p&gt;

&lt;p&gt;Before converting, make sure your JSON is valid. Compacted API responses or copied-from-logs JSON often have hidden issues — trailing commas, unescaped characters, or single quotes instead of double. Running it through the &lt;a href="https://jsonindenter.com/json-beautifier?utm_source=devto&amp;amp;utm_medium=article&amp;amp;utm_campaign=json-to-yaml-conversion" rel="noopener noreferrer"&gt;JSON Beautifier &amp;amp; Validator&lt;/a&gt; first formats and validates it in one step, so you're not feeding broken JSON into a converter and wondering why the output looks wrong.&lt;/p&gt;

&lt;h2&gt;
  
  
  A Real-World Example: Kubernetes ConfigMap
&lt;/h2&gt;

&lt;p&gt;Let's say you have this JSON config coming out of a deployment pipeline:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"database"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"host"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"postgres.internal"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"port"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;5432&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"app_production"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"ssl_mode"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"require"&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"cache"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"ttl"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;300&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"max_size"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1000&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"feature_flags"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"new_checkout"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"beta_search"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;After conversion, you get YAML you can drop directly into a Kubernetes ConfigMap:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;database&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;host&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;postgres.internal&lt;/span&gt;
  &lt;span class="na"&gt;port&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;5432&lt;/span&gt;
  &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;app_production&lt;/span&gt;
  &lt;span class="na"&gt;ssl_mode&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;require&lt;/span&gt;
&lt;span class="na"&gt;cache&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;ttl&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;300&lt;/span&gt;
  &lt;span class="na"&gt;max_size&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;1000&lt;/span&gt;
&lt;span class="na"&gt;feature_flags&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;new_checkout&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
  &lt;span class="na"&gt;beta_search&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Clean, indented, and ready to embed. The tool handles the structural mapping — you just verify the output makes sense.&lt;/p&gt;

&lt;h2&gt;
  
  
  Going the Other Way: YAML Back to JSON
&lt;/h2&gt;

&lt;p&gt;The reverse conversion matters just as much. Your GitHub Actions workflow file is YAML; you want to parse specific values in a script that expects JSON. Or a colleague sends you an Ansible vars file and your test harness is JSON-only.&lt;/p&gt;

&lt;p&gt;The same &lt;a href="https://jsonindenter.com/json-to-yaml?utm_source=devto&amp;amp;utm_medium=article&amp;amp;utm_campaign=json-to-yaml-conversion" rel="noopener noreferrer"&gt;JSON to YAML Converter&lt;/a&gt; supports bidirectional conversion — paste YAML in the YAML panel and get JSON back.&lt;/p&gt;

&lt;p&gt;One thing to watch for when going YAML → JSON: YAML has native support for timestamps, multi-line strings (&lt;code&gt;|&lt;/code&gt; and &lt;code&gt;&amp;gt;&lt;/code&gt; blocks), and anchors/aliases (&lt;code&gt;&amp;amp;&lt;/code&gt; and &lt;code&gt;*&lt;/code&gt;). These don't have direct JSON equivalents, so they get serialized as plain strings or resolved inline. If your YAML uses these features heavily, review the JSON output carefully before using it downstream.&lt;/p&gt;

&lt;h2&gt;
  
  
  When to Write the Conversion in Code
&lt;/h2&gt;

&lt;p&gt;For a one-off config tweak, the browser tool is the fastest path. But if you're converting as part of a build pipeline or automation script, you'll want code. Here's the idiomatic approach in a couple of common languages:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Python:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;json&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;yaml&lt;/span&gt;

&lt;span class="k"&gt;with&lt;/span&gt; &lt;span class="nf"&gt;open&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;config.json&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;data&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;json&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;load&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;with&lt;/span&gt; &lt;span class="nf"&gt;open&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;config.yaml&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;w&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;yaml&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;dump&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;default_flow_style&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;False&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Node.js (with &lt;code&gt;js-yaml&lt;/code&gt;):&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;fs&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;fs&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;yaml&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;js-yaml&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;data&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;parse&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;fs&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;readFileSync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;config.json&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;utf8&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
&lt;span class="nx"&gt;fs&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;writeFileSync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;config.yaml&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;yaml&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;dump&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;For interactive exploration or debugging — especially when you're unsure why a converted file looks off — the browser tool remains the quickest feedback loop even if you have automation in place.&lt;/p&gt;

&lt;h2&gt;
  
  
  Further Reading
&lt;/h2&gt;

&lt;p&gt;If you're curious about where JSON and YAML differ beyond syntax — data type handling, comment support, schema strictness — the &lt;a href="https://jsonindenter.com/compare/json-vs-yaml?utm_source=devto&amp;amp;utm_medium=article&amp;amp;utm_campaign=json-to-yaml-conversion" rel="noopener noreferrer"&gt;JSON vs YAML comparison&lt;/a&gt; page goes deeper on the structural tradeoffs between the two formats.&lt;/p&gt;

&lt;h2&gt;
  
  
  What Format Mismatch Do You Hit Most?
&lt;/h2&gt;

&lt;p&gt;I'm curious — is it mostly the API → config file direction that catches you out, or is YAML → JSON (for testing or scripting) the bigger pain point in your workflow? Drop a comment below.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Free tools used in this post:&lt;/em&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://jsonindenter.com/json-to-yaml?utm_source=devto&amp;amp;utm_medium=article&amp;amp;utm_campaign=json-to-yaml-conversion" rel="noopener noreferrer"&gt;JSON to YAML Converter&lt;/a&gt; — converts between JSON and YAML formats instantly in the browser, bidirectional&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://jsonindenter.com/json-beautifier?utm_source=devto&amp;amp;utm_medium=article&amp;amp;utm_campaign=json-to-yaml-conversion" rel="noopener noreferrer"&gt;JSON Beautifier &amp;amp; Validator&lt;/a&gt; — formats and validates JSON before you convert, catching hidden errors fast&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://jsonindenter.com/all-json-tools?utm_source=devto&amp;amp;utm_medium=article&amp;amp;utm_campaign=json-to-yaml-conversion" rel="noopener noreferrer"&gt;All tools&lt;/a&gt; — client-side, no sign-up, nothing leaves your browser&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>javascript</category>
      <category>webdev</category>
      <category>tutorial</category>
      <category>devops</category>
    </item>
    <item>
      <title>How to Query Nested JSON with JSONPath (Without Writing Loops)</title>
      <dc:creator>Tahmid</dc:creator>
      <pubDate>Sun, 03 May 2026 01:21:33 +0000</pubDate>
      <link>https://dev.to/pioneer10/how-to-query-nested-json-with-jsonpath-without-writing-loops-4jpa</link>
      <guid>https://dev.to/pioneer10/how-to-query-nested-json-with-jsonpath-without-writing-loops-4jpa</guid>
      <description>&lt;p&gt;You just got back a 300-line API response. Somewhere inside three levels of nesting is the &lt;code&gt;email&lt;/code&gt; field you actually need. So you write a loop, then another loop, then a conditional — and now you're maintaining brittle traversal code that breaks every time the API schema shifts.&lt;/p&gt;

&lt;p&gt;There's a better way: JSONPath.&lt;/p&gt;

&lt;p&gt;JSONPath is a query language for JSON, similar to how XPath works for XML. Instead of writing code to traverse a structure, you write a short expression that reads like a path. It works across languages, and once you learn the syntax, you'll reach for it constantly.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Basics: What JSONPath Looks Like
&lt;/h2&gt;

&lt;p&gt;Here's a typical API response — a list of orders, each with nested customer and item data:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"store"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"orders"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"customer"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Alice"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"email"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"alice@example.com"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"items"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[{&lt;/span&gt;&lt;span class="nl"&gt;"sku"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"A1"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"qty"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nl"&gt;"sku"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"B3"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"qty"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;}]&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"customer"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Bob"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"email"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"bob@example.com"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"items"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[{&lt;/span&gt;&lt;span class="nl"&gt;"sku"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"C7"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"qty"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;}]&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;To get every customer email without writing any loops:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$.store.orders[*].customer.email
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Result:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"alice@example.com"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"bob@example.com"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;$&lt;/code&gt; represents the root. The &lt;code&gt;.&lt;/code&gt; navigates into an object key. &lt;code&gt;[*]&lt;/code&gt; means "all array elements." Three characters replace a full nested loop.&lt;/p&gt;

&lt;h2&gt;
  
  
  Filtering: The Real Power
&lt;/h2&gt;

&lt;p&gt;JSONPath shines when you need to filter by a condition. Say you only want items where &lt;code&gt;qty&lt;/code&gt; is greater than 1:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$.store.orders[*].items[?(@.qty &amp;gt; 1)]
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Result:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nl"&gt;"sku"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"A1"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"qty"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nl"&gt;"sku"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"C7"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"qty"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;?()&lt;/code&gt; is a filter expression. &lt;code&gt;@&lt;/code&gt; refers to the current node. You can combine conditions with &lt;code&gt;&amp;amp;&amp;amp;&lt;/code&gt; and &lt;code&gt;||&lt;/code&gt;, check for key existence with &lt;code&gt;@.key&lt;/code&gt;, and do string comparisons too.&lt;/p&gt;

&lt;p&gt;Here's the equivalent Python — same outcome, but compare the cognitive load:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="c1"&gt;# Without JSONPath — five lines, easy to get wrong
&lt;/span&gt;&lt;span class="n"&gt;results&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;
&lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;order&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;store&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;orders&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt;
    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;item&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;order&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;items&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;item&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;qty&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="n"&gt;results&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;append&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;item&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The JSONPath version is one line and self-documenting. Anyone reading it immediately knows what data you're after.&lt;/p&gt;

&lt;h2&gt;
  
  
  Recursive Descent: Finding a Key Anywhere in the Tree
&lt;/h2&gt;

&lt;p&gt;Sometimes you don't know exactly where a key lives — you just know it exists somewhere in a deeply nested structure. The &lt;code&gt;..&lt;/code&gt; (recursive descent) operator handles this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$..email
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This finds every &lt;code&gt;email&lt;/code&gt; field at any depth in the document. It's invaluable for exploring unfamiliar API schemas or debugging payloads where nesting is inconsistent across records.&lt;/p&gt;

&lt;p&gt;Try it live with the &lt;a href="https://jsonindenter.com/json-path?utm_source=devto&amp;amp;utm_medium=article&amp;amp;utm_campaign=jsonpath-query-guide" rel="noopener noreferrer"&gt;JSONPath Evaluator on jsonindenter.com&lt;/a&gt; — paste your JSON, type any expression, and see matched results highlighted instantly. No installation, nothing leaves your browser.&lt;/p&gt;

&lt;h2&gt;
  
  
  A Practical Workflow for Real API Responses
&lt;/h2&gt;

&lt;p&gt;When you're dealing with a large, unfamiliar JSON blob, this three-step routine saves a lot of "why does my query return nothing?" debugging time:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Paste the response into the &lt;a href="https://jsonindenter.com/json-beautifier?utm_source=devto&amp;amp;utm_medium=article&amp;amp;utm_campaign=jsonpath-query-guide" rel="noopener noreferrer"&gt;JSON Beautifier&lt;/a&gt; to get a clean indented view and understand the shape of the data.&lt;/li&gt;
&lt;li&gt;Run it through the &lt;a href="https://jsonindenter.com/json-validator?utm_source=devto&amp;amp;utm_medium=article&amp;amp;utm_campaign=jsonpath-query-guide" rel="noopener noreferrer"&gt;JSON Validator&lt;/a&gt; to confirm it's well-formed — a malformed payload will silently produce empty results and send you chasing the wrong problem.&lt;/li&gt;
&lt;li&gt;Switch to the JSONPath tool and iterate on your expression until you're extracting exactly what you need.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Quick Reference: Syntax That Covers 80% of Cases
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;$&lt;/code&gt; — root element&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;.key&lt;/code&gt; — child key&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;..key&lt;/code&gt; — recursive search for key at any depth&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;[*]&lt;/code&gt; — all array elements&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;[0]&lt;/code&gt; — first element; &lt;code&gt;[-1]&lt;/code&gt; — last element&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;[0,2]&lt;/code&gt; — elements at index 0 and 2&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;[?(@.price &amp;lt; 10)]&lt;/code&gt; — filter where price is less than 10&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;@.key&lt;/code&gt; — current node's key (used inside filter expressions)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;For edge cases and implementation differences across languages, the &lt;a href="https://jsonindenter.com/blog/jsonpath-complete-guide" rel="noopener noreferrer"&gt;JSONPath guide on jsonindenter.com&lt;/a&gt; covers the full spec with annotated examples worth bookmarking.&lt;/p&gt;

&lt;h2&gt;
  
  
  When JSONPath Is the Wrong Tool
&lt;/h2&gt;

&lt;p&gt;JSONPath is read-only — it queries and extracts, it doesn't mutate. If you need to patch a JSON document in place (add a field, replace a value, remove a key), that's what &lt;a href="https://jsonindenter.com/json-patch?utm_source=devto&amp;amp;utm_medium=article&amp;amp;utm_campaign=jsonpath-query-guide" rel="noopener noreferrer"&gt;JSON Patch&lt;/a&gt; is designed for. The two tools are complementary: JSONPath to find, JSON Patch to modify.&lt;/p&gt;

&lt;p&gt;Also worth knowing: JSONPath implementations vary across languages. &lt;code&gt;jsonpath-ng&lt;/code&gt; in Python, &lt;code&gt;jsonpath-plus&lt;/code&gt; in JavaScript, and Jayway in Java each have subtle differences. RFC 9535 (published in 2024) is working to standardize the spec, but for production code it's worth testing your expressions against the specific library you're targeting.&lt;/p&gt;

&lt;h2&gt;
  
  
  JSONPath Is Worth Adding to Your Toolkit
&lt;/h2&gt;

&lt;p&gt;The argument for JSONPath isn't that it's magic — it's that it's &lt;em&gt;composable and readable&lt;/em&gt;. A well-written JSONPath expression communicates intent at a glance. Nested loops require reading to the end before you understand what's being extracted.&lt;/p&gt;

&lt;p&gt;It's also portable: the same expression works in JavaScript, Python, Java, and tools like Postman, AWS CloudFormation, and Kubernetes JSON patches. Learning it once pays off across your entire stack.&lt;/p&gt;




&lt;p&gt;What's the most complex JSON query you've had to write? Did you reach for JSONPath, or did you end up with custom traversal logic you later regretted?&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Free tools used in this post:&lt;/em&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://jsonindenter.com/json-path?utm_source=devto&amp;amp;utm_medium=article&amp;amp;utm_campaign=jsonpath-query-guide" rel="noopener noreferrer"&gt;JSONPath Evaluator&lt;/a&gt; — test JSONPath expressions against any JSON payload, results highlighted live&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://jsonindenter.com/json-beautifier?utm_source=devto&amp;amp;utm_medium=article&amp;amp;utm_campaign=jsonpath-query-guide" rel="noopener noreferrer"&gt;JSON Beautifier&lt;/a&gt; — format and indent raw JSON for easy reading&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://jsonindenter.com/json-validator?utm_source=devto&amp;amp;utm_medium=article&amp;amp;utm_campaign=jsonpath-query-guide" rel="noopener noreferrer"&gt;JSON Validator&lt;/a&gt; — confirm your JSON is well-formed before querying it&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://jsonindenter.com/json-patch?utm_source=devto&amp;amp;utm_medium=article&amp;amp;utm_campaign=jsonpath-query-guide" rel="noopener noreferrer"&gt;JSON Patch&lt;/a&gt; — apply RFC 6902 patches to modify JSON structures in place&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://jsonindenter.com/all-json-tools?utm_source=devto&amp;amp;utm_medium=article&amp;amp;utm_campaign=jsonpath-query-guide" rel="noopener noreferrer"&gt;All tools&lt;/a&gt; — client-side, no sign-up, nothing leaves your browser&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>javascript</category>
      <category>webdev</category>
      <category>tutorial</category>
      <category>programming</category>
    </item>
    <item>
      <title>JSONL Explained: The Line-by-Line Format Powering AI Datasets</title>
      <dc:creator>Tahmid</dc:creator>
      <pubDate>Tue, 28 Apr 2026 01:50:11 +0000</pubDate>
      <link>https://dev.to/pioneer10/jsonl-explained-the-line-by-line-format-powering-ai-datasets-3op6</link>
      <guid>https://dev.to/pioneer10/jsonl-explained-the-line-by-line-format-powering-ai-datasets-3op6</guid>
      <description>&lt;p&gt;You're trying to load a 500,000-record dataset into your script. You reach for JSON — it's universal, readable, everyone knows it. But the moment you call &lt;code&gt;JSON.parse()&lt;/code&gt; on a 2 GB file, your process runs out of memory and crashes.&lt;/p&gt;

&lt;p&gt;This is the problem JSONL (JSON Lines) was built to solve. And if you're working with AI training data, log pipelines, or any large-scale data processing, understanding JSONL will save you from real production pain.&lt;/p&gt;

&lt;h2&gt;
  
  
  What Is JSONL?
&lt;/h2&gt;

&lt;p&gt;JSONL (also written &lt;code&gt;.jsonl&lt;/code&gt; or called "JSON Lines") is a text format where each line is a self-contained, valid JSON object. There's no wrapping array, no commas between records — just one JSON object per line, separated by newlines.&lt;/p&gt;

&lt;p&gt;Here's the key distinction:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Standard JSON array:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nl"&gt;"id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Alice"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"role"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"admin"&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nl"&gt;"id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Bob"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"role"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"editor"&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nl"&gt;"id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Carol"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"role"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"viewer"&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;JSONL equivalent:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;{"id": 1, "name": "Alice", "role": "admin"}
{"id": 2, "name": "Bob", "role": "editor"}
{"id": 3, "name": "Carol", "role": "viewer"}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The difference looks small. The impact at scale is enormous.&lt;/p&gt;

&lt;p&gt;With a JSON array, the entire file must be parsed into memory before you can read a single record. With JSONL, you can stream the file one line at a time — processing millions of records with constant memory usage.&lt;/p&gt;

&lt;h2&gt;
  
  
  Processing JSONL Line by Line
&lt;/h2&gt;

&lt;p&gt;Here's the practical difference in Node.js. First, the approach that breaks on large files:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// ❌ Loads the entire file into memory before processing a single record&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;data&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;parse&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;fs&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;readFileSync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;users.json&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;utf8&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;forEach&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;record&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;process&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;record&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now the JSONL equivalent, which handles files of any size:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// ✅ Streams one line at a time — constant memory usage&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;createReadStream&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;fs&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;createInterface&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;readline&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;rl&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;createInterface&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;input&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;createReadStream&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;users.jsonl&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="nx"&gt;rl&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;on&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;line&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;line&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;line&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;trim&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;record&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;parse&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;line&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nf"&gt;process&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;record&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The second approach works equally well on a 1,000-record file and a 50-million-record file. That's the core value of JSONL.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why AI and LLMs Love JSONL
&lt;/h2&gt;

&lt;p&gt;If you've worked with OpenAI's fine-tuning API, you've already encountered JSONL. The required format for training data is a &lt;code&gt;.jsonl&lt;/code&gt; file where each line is a conversation turn:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;{"messages": [{"role": "system", "content": "You are a helpful assistant."}, {"role": "user", "content": "What is the capital of France?"}, {"role": "assistant", "content": "Paris."}]}
{"messages": [{"role": "system", "content": "You are a helpful assistant."}, {"role": "user", "content": "How do I reverse a string in Python?"}, {"role": "assistant", "content": "You can use slicing: s[::-1]"}]}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Each line = one training example. You can add or remove examples without touching any other line in the file. You can run &lt;code&gt;wc -l&lt;/code&gt; on the file to instantly count your training examples. You can &lt;code&gt;head -n 10&lt;/code&gt; to preview the first 10 records. These are the small ergonomic wins that matter when you're curating thousands of examples.&lt;/p&gt;

&lt;p&gt;Log aggregation systems like Elasticsearch, Datadog, and Loki also ingest JSONL natively — each log entry is a self-contained object that can be appended without locking the file.&lt;/p&gt;

&lt;h2&gt;
  
  
  Working With JSONL Files
&lt;/h2&gt;

&lt;p&gt;When you have a messy JSONL file — maybe it came from an export, maybe lines got mangled in transit — validation can get frustrating fast. Standard JSON validators reject the entire file because, as a whole, it isn't valid JSON.&lt;/p&gt;

&lt;p&gt;The &lt;a href="https://jsonindenter.com/jsonl-formatter?utm_source=devto&amp;amp;utm_medium=article&amp;amp;utm_campaign=jsonl-ai-datasets" rel="noopener noreferrer"&gt;JSONL Formatter on JSON Indenter&lt;/a&gt; handles this correctly: it validates and formats each line independently, highlights which specific lines have errors, and lets you fix them without losing the rest of the file. No sign-up, nothing leaves your browser.&lt;/p&gt;

&lt;p&gt;If you need to inspect a single record in detail, paste it into the &lt;a href="https://jsonindenter.com/json-beautifier?utm_source=devto&amp;amp;utm_medium=article&amp;amp;utm_campaign=jsonl-ai-datasets" rel="noopener noreferrer"&gt;JSON Beautifier&lt;/a&gt; for a properly indented, readable view — especially handy for deeply nested objects crammed into a single JSONL line.&lt;/p&gt;

&lt;p&gt;For production pipelines where you're writing JSONL to disk or streaming it over the wire, run each record through the &lt;a href="https://jsonindenter.com/json-minifier?utm_source=devto&amp;amp;utm_medium=article&amp;amp;utm_campaign=jsonl-ai-datasets" rel="noopener noreferrer"&gt;JSON Minifier&lt;/a&gt; first. Stripping whitespace from each record before appending it to your &lt;code&gt;.jsonl&lt;/code&gt; file keeps sizes lean and ingestion fast.&lt;/p&gt;

&lt;p&gt;For a deeper look at edge cases — empty lines, Unicode handling, NDJSON compatibility — the &lt;a href="https://jsonindenter.com/blog/jsonl-format-explained" rel="noopener noreferrer"&gt;JSONL Format Explained&lt;/a&gt; guide covers the full picture.&lt;/p&gt;

&lt;h2&gt;
  
  
  When to Use JSONL vs Regular JSON
&lt;/h2&gt;

&lt;p&gt;JSONL wins when:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Your dataset has more than a few thousand records&lt;/li&gt;
&lt;li&gt;You need to append records incrementally (logs, event streams, AI training data)&lt;/li&gt;
&lt;li&gt;You're feeding data to an LLM fine-tuning job or a RAG pipeline&lt;/li&gt;
&lt;li&gt;You want line-level git diffs that are actually readable&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Stick with standard JSON when:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The data is a config file or small lookup table&lt;/li&gt;
&lt;li&gt;You need deep, document-level structure where the whole object matters&lt;/li&gt;
&lt;li&gt;You're calling an API that expects a JSON array in the request body&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  A Common Gotcha
&lt;/h2&gt;

&lt;p&gt;Empty lines in JSONL files will cause &lt;code&gt;JSON.parse('')&lt;/code&gt; to throw a SyntaxError. Always guard against them:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;rl&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;on&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;line&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;line&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;line&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;trim&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;''&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;// skip blank lines&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;record&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;parse&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;line&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="c1"&gt;// ...&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This bites people constantly with JSONL files exported from tools that append a trailing newline after the last record — which is nearly all of them.&lt;/p&gt;




&lt;p&gt;Are you using JSONL in your stack — for logs, datasets, or AI pipelines? And have you run into any tools or patterns that make working with large JSONL files smoother? Drop a note in the comments.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Free tools used in this post:&lt;/em&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://jsonindenter.com/jsonl-formatter?utm_source=devto&amp;amp;utm_medium=article&amp;amp;utm_campaign=jsonl-ai-datasets" rel="noopener noreferrer"&gt;JSONL Formatter&lt;/a&gt; — validates and formats each line in a JSONL file independently&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://jsonindenter.com/json-beautifier?utm_source=devto&amp;amp;utm_medium=article&amp;amp;utm_campaign=jsonl-ai-datasets" rel="noopener noreferrer"&gt;JSON Beautifier&lt;/a&gt; — pretty-prints any JSON object with proper indentation&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://jsonindenter.com/json-minifier?utm_source=devto&amp;amp;utm_medium=article&amp;amp;utm_campaign=jsonl-ai-datasets" rel="noopener noreferrer"&gt;JSON Minifier&lt;/a&gt; — strips whitespace from JSON to shrink file sizes&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://jsonindenter.com/all-json-tools?utm_source=devto&amp;amp;utm_medium=article&amp;amp;utm_campaign=jsonl-ai-datasets" rel="noopener noreferrer"&gt;All tools&lt;/a&gt; — client-side, no sign-up, nothing leaves your browser&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>javascript</category>
      <category>ai</category>
      <category>webdev</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>How to Convert JSON to TypeScript Types (Without Writing Them by Hand)</title>
      <dc:creator>Tahmid</dc:creator>
      <pubDate>Tue, 28 Apr 2026 01:40:20 +0000</pubDate>
      <link>https://dev.to/pioneer10/how-to-convert-json-to-typescript-types-without-writing-them-by-hand-1b8b</link>
      <guid>https://dev.to/pioneer10/how-to-convert-json-to-typescript-types-without-writing-them-by-hand-1b8b</guid>
      <description>&lt;p&gt;You just got access to a new API. The response comes back — it's a deeply nested JSON object with 40 fields, some optional, some arrays, some nested objects three levels deep. Your team lead says: "We need this typed properly." So you open a new &lt;code&gt;.ts&lt;/code&gt; file and start typing &lt;code&gt;interface&lt;/code&gt;...&lt;/p&gt;

&lt;p&gt;That's an hour of your life you're not getting back — and the types might still be wrong.&lt;/p&gt;

&lt;p&gt;Manual JSON-to-TypeScript conversion is one of those tasks that &lt;em&gt;feels&lt;/em&gt; like real work but is basically just copying data shapes from one format to another. Here's a better way.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Problem With Manual Type Writing
&lt;/h2&gt;

&lt;p&gt;Hand-typing TypeScript interfaces from JSON has three common failure modes:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Missing optional fields&lt;/strong&gt; — APIs often return &lt;code&gt;null&lt;/code&gt; or omit fields in edge cases. If you type from a single example response, you'll likely mark optional fields as required.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Wrong primitive types&lt;/strong&gt; — A field that looks like a number (&lt;code&gt;"count": 42&lt;/code&gt;) might occasionally come back as a string (&lt;code&gt;"count": "42"&lt;/code&gt;) from a legacy backend. You won't catch this until runtime.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;It doesn't scale&lt;/strong&gt; — When the API response changes, you have to re-read and re-type the whole thing manually.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Real Example: Before and After
&lt;/h2&gt;

&lt;p&gt;Say you get this from an API:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"user"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1024&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Alice"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"email"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"alice@example.com"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"roles"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"admin"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"editor"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"profile"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"bio"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Engineer at Acme"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"avatar_url"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"https://example.com/alice.png"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"joined_at"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"2024-01-15T09:30:00Z"&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"settings"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"notifications_enabled"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"theme"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"dark"&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If you wrote this by hand, you'd probably produce something like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kr"&gt;interface&lt;/span&gt; &lt;span class="nx"&gt;Profile&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;bio&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;avatar_url&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;joined_at&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="kr"&gt;interface&lt;/span&gt; &lt;span class="nx"&gt;Settings&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;notifications_enabled&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;boolean&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;theme&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="kr"&gt;interface&lt;/span&gt; &lt;span class="nx"&gt;User&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;number&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;email&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;roles&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;[];&lt;/span&gt;
  &lt;span class="nl"&gt;profile&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Profile&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;settings&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Settings&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="kr"&gt;interface&lt;/span&gt; &lt;span class="nx"&gt;ApiResponse&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;user&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;User&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That works — but you typed it from &lt;em&gt;one&lt;/em&gt; example. Real-world responses can have &lt;code&gt;bio: null&lt;/code&gt;, or &lt;code&gt;avatar_url&lt;/code&gt; might be absent entirely. You'd discover those gaps at runtime, not at compile time.&lt;/p&gt;

&lt;p&gt;Using the &lt;a href="https://jsonindenter.com/json-to-typescript?utm_source=devto&amp;amp;utm_medium=article&amp;amp;utm_campaign=json-to-typescript-types" rel="noopener noreferrer"&gt;JSON to TypeScript converter&lt;/a&gt;, you paste the JSON and get properly structured interfaces in seconds. It handles nested objects, arrays, and can mark fields as optional — which is a much safer default for API responses.&lt;/p&gt;

&lt;h2&gt;
  
  
  Going Further: Runtime Validation With Zod
&lt;/h2&gt;

&lt;p&gt;TypeScript types are compile-time only. They don't protect you at runtime — and if you're fetching from an external API, that's exactly when things go wrong.&lt;/p&gt;

&lt;p&gt;A common pattern is pairing your TypeScript interfaces with a Zod schema so you can validate actual API responses at runtime:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;z&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;zod&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;ProfileSchema&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;z&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;object&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;bio&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;z&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;string&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;nullable&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
  &lt;span class="na"&gt;avatar_url&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;z&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;string&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;optional&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
  &lt;span class="na"&gt;joined_at&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;z&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;string&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;UserSchema&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;z&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;object&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;z&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;number&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
  &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;z&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;string&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
  &lt;span class="na"&gt;email&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;z&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;string&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
  &lt;span class="na"&gt;roles&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;z&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;array&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;z&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;string&lt;/span&gt;&lt;span class="p"&gt;()),&lt;/span&gt;
  &lt;span class="na"&gt;profile&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;ProfileSchema&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;settings&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;z&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;object&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
    &lt;span class="na"&gt;notifications_enabled&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;z&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;boolean&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
    &lt;span class="na"&gt;theme&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;z&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;string&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
  &lt;span class="p"&gt;}),&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="c1"&gt;// Runtime check — throws if the shape doesn't match&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;fetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;/api/user/1024&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;data&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;UserSchema&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;parse&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;
&lt;span class="c1"&gt;// Now `data` is fully typed AND validated&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You can generate this Zod schema automatically from any JSON using the &lt;a href="https://jsonindenter.com/json-to-zod?utm_source=devto&amp;amp;utm_medium=article&amp;amp;utm_campaign=json-to-typescript-types" rel="noopener noreferrer"&gt;JSON to Zod tool&lt;/a&gt;. The jsonindenter.com blog also has a deeper post on &lt;a href="https://jsonindenter.com/blog/json-to-zod-schema-for-typescript" rel="noopener noreferrer"&gt;building Zod schemas from JSON for TypeScript projects&lt;/a&gt; that covers validation edge cases worth reading.&lt;/p&gt;

&lt;h2&gt;
  
  
  One More Step: Validate Before You Convert
&lt;/h2&gt;

&lt;p&gt;If the JSON you're working with is malformed — missing quotes, trailing commas, comments copied from a config file — the converter will fail silently or produce wrong output. Run your JSON through a &lt;a href="https://jsonindenter.com/json-validator?utm_source=devto&amp;amp;utm_medium=article&amp;amp;utm_campaign=json-to-typescript-types" rel="noopener noreferrer"&gt;JSON validator&lt;/a&gt; first. It highlights the exact line with the problem so you fix it before conversion.&lt;/p&gt;

&lt;p&gt;The full workflow looks like this:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Paste the raw JSON from the API response or config file&lt;/li&gt;
&lt;li&gt;Validate it to catch any syntax issues&lt;/li&gt;
&lt;li&gt;Convert to TypeScript interfaces (and optionally a Zod schema)&lt;/li&gt;
&lt;li&gt;Drop the generated types into your codebase&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;This is especially useful when onboarding a new third-party API or when a backend teammate sends you a sample payload in Slack and you need types in under 60 seconds.&lt;/p&gt;

&lt;h2&gt;
  
  
  What Auto-Generation Can't Do (Yet)
&lt;/h2&gt;

&lt;p&gt;Generated types have a ceiling. The tools give you the &lt;em&gt;shape&lt;/em&gt; of your data, but they can't infer:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Semantic constraints&lt;/strong&gt; — a &lt;code&gt;status&lt;/code&gt; field might only accept &lt;code&gt;"active" | "inactive"&lt;/code&gt; but the converter will type it as &lt;code&gt;string&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Cross-field relationships&lt;/strong&gt; — if &lt;code&gt;is_verified&lt;/code&gt; being &lt;code&gt;true&lt;/code&gt; implies &lt;code&gt;verified_at&lt;/code&gt; is non-null, you'll need to express that manually with a discriminated union&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Generic patterns&lt;/strong&gt; — paginated responses where the same wrapper type repeats across endpoints&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;For those cases, treat auto-generated types as a starting point, not a final answer. But for the vast majority of everyday API work, generation gets you 80% of the way there in seconds rather than spending 30 minutes typing interfaces that might still contain mistakes.&lt;/p&gt;




&lt;p&gt;What's the most tedious JSON-to-types conversion you've done by hand? Did anything break when the API response changed later? Drop it in the comments — I'd genuinely like to know how others handle this.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Free tools used in this post:&lt;/em&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://jsonindenter.com/json-to-typescript?utm_source=devto&amp;amp;utm_medium=article&amp;amp;utm_campaign=json-to-typescript-types" rel="noopener noreferrer"&gt;JSON to TypeScript&lt;/a&gt; — paste JSON, get TypeScript interfaces instantly&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://jsonindenter.com/json-to-zod?utm_source=devto&amp;amp;utm_medium=article&amp;amp;utm_campaign=json-to-typescript-types" rel="noopener noreferrer"&gt;JSON to Zod&lt;/a&gt; — generate Zod validation schemas from any JSON structure&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://jsonindenter.com/json-validator?utm_source=devto&amp;amp;utm_medium=article&amp;amp;utm_campaign=json-to-typescript-types" rel="noopener noreferrer"&gt;JSON Validator&lt;/a&gt; — catch syntax errors before converting&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://jsonindenter.com/all-json-tools?utm_source=devto&amp;amp;utm_medium=article&amp;amp;utm_campaign=json-to-typescript-types" rel="noopener noreferrer"&gt;All tools&lt;/a&gt; — client-side, no sign-up, nothing leaves your browser&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>javascript</category>
      <category>webdev</category>
      <category>tutorial</category>
      <category>beginners</category>
    </item>
    <item>
      <title>How to Minify JSON and Shrink Your API Payloads in Seconds</title>
      <dc:creator>Tahmid</dc:creator>
      <pubDate>Mon, 27 Apr 2026 23:21:33 +0000</pubDate>
      <link>https://dev.to/pioneer10/how-to-minify-json-and-shrink-your-api-payloads-in-seconds-4dkp</link>
      <guid>https://dev.to/pioneer10/how-to-minify-json-and-shrink-your-api-payloads-in-seconds-4dkp</guid>
      <description>&lt;p&gt;You're debugging a slow API and you open the response in your browser's network tab. The payload is 620 KB. You paste it into a text editor and immediately spot the problem: every key is quoted, every value is neatly indented, and there are blank lines between sections. Your beautiful, readable JSON is costing you hundreds of milliseconds per request.&lt;/p&gt;

&lt;p&gt;Whitespace is free when you're reading JSON. It's not free when you're sending it across a network a thousand times a day.&lt;/p&gt;

&lt;h2&gt;
  
  
  What minification actually does
&lt;/h2&gt;

&lt;p&gt;JSON minification strips out every character that doesn't change what the data &lt;em&gt;means&lt;/em&gt;: spaces, newline characters, and indentation. The JSON spec doesn't require any of it — a parser doesn't care whether your keys are separated by &lt;code&gt;\n&lt;/code&gt; or nothing at all.&lt;/p&gt;

&lt;p&gt;Here's the same config object, formatted vs. minified:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Before (formatted):&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"user"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1042&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Priya Kapoor"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"role"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"admin"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"preferences"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"theme"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"dark"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"notifications"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"language"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"en-US"&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"session"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"token"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"eyJhbGciOiJIUzI1NiJ9"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"expires_at"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"2026-05-01T00:00:00Z"&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;After (minified):&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nl"&gt;"user"&lt;/span&gt;&lt;span class="p"&gt;:{&lt;/span&gt;&lt;span class="nl"&gt;"id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;1042&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="s2"&gt;"Priya Kapoor"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="nl"&gt;"role"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="s2"&gt;"admin"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="nl"&gt;"preferences"&lt;/span&gt;&lt;span class="p"&gt;:{&lt;/span&gt;&lt;span class="nl"&gt;"theme"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="s2"&gt;"dark"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="nl"&gt;"notifications"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="nl"&gt;"language"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="s2"&gt;"en-US"&lt;/span&gt;&lt;span class="p"&gt;}},&lt;/span&gt;&lt;span class="nl"&gt;"session"&lt;/span&gt;&lt;span class="p"&gt;:{&lt;/span&gt;&lt;span class="nl"&gt;"token"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="s2"&gt;"eyJhbGciOiJIUzI1NiJ9"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="nl"&gt;"expires_at"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="s2"&gt;"2026-05-01T00:00:00Z"&lt;/span&gt;&lt;span class="p"&gt;}}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Same data. 43% fewer characters. For a small config object the difference is trivial — but when you're serialising paginated API responses, search results, or analytics events, it adds up fast.&lt;/p&gt;

&lt;h2&gt;
  
  
  Three ways to minify JSON
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;In the browser (zero setup)&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;If you're working with a one-off payload — say, you grabbed a response from Postman and need to paste it into a config file — the fastest path is &lt;a href="https://jsonindenter.com/json-minifier?utm_source=devto&amp;amp;utm_medium=article&amp;amp;utm_campaign=minify-json-payloads" rel="noopener noreferrer"&gt;JSON Minifier on jsonindenter.com&lt;/a&gt;. Paste, click, copy. Everything runs client-side so nothing leaves your browser, which matters when the JSON contains credentials or PII.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;In Python&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Python's &lt;code&gt;json&lt;/code&gt; module handles this with a single argument:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;json&lt;/span&gt;

&lt;span class="c1"&gt;# Load from a file or string
&lt;/span&gt;&lt;span class="k"&gt;with&lt;/span&gt; &lt;span class="nf"&gt;open&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;response.json&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;data&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;json&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;load&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="c1"&gt;# Minify by setting separators and no indentation
&lt;/span&gt;&lt;span class="n"&gt;minified&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;json&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;dumps&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;separators&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;,&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;:&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;minified&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;separators=(",", ":")&lt;/code&gt; tells the encoder to drop the space after each comma and colon. That's the entire trick. For bulk processing, wrap this in a script that walks a directory of JSON files and overwrites each one with its minified version.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;In Node.js / JavaScript&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;fs&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;fs&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;raw&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;fs&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;readFileSync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;data.json&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;utf8&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;minified&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;stringify&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;parse&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;raw&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;

&lt;span class="nx"&gt;fs&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;writeFileSync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;data.min.json&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;minified&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`Original: &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;raw&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt; chars → Minified: &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;minified&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt; chars`&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;JSON.stringify&lt;/code&gt; without a &lt;code&gt;space&lt;/code&gt; argument produces minified output by default. If your current code passes &lt;code&gt;JSON.stringify(data, null, 2)&lt;/code&gt; for pretty-printing, removing that third argument is all you need to do in production.&lt;/p&gt;

&lt;h2&gt;
  
  
  When minification matters most
&lt;/h2&gt;

&lt;p&gt;Minification pays off most in these situations:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Static JSON files served from a CDN&lt;/strong&gt; — config files, feature-flag payloads, i18n translation bundles. These are downloaded on every cold start; every kilobyte counts.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;High-frequency API endpoints&lt;/strong&gt; — if an endpoint is hit thousands of times per minute, even a 10 KB saving compounds quickly into meaningful bandwidth cost reduction.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Mobile clients on flaky connections&lt;/strong&gt; — smaller payloads parse faster and fail less often on spotty networks.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Logging pipelines&lt;/strong&gt; — if you're shipping structured JSON logs to a log aggregator, minifying before transmission can cut your ingestion bill noticeably.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Where it &lt;em&gt;doesn't&lt;/em&gt; matter: internal service-to-service calls on a fast private network, or any place where debugging the raw wire format outweighs the bandwidth saving.&lt;/p&gt;

&lt;h2&gt;
  
  
  Minify in production, read in development
&lt;/h2&gt;

&lt;p&gt;The natural workflow is to keep formatted JSON in your version control and your editor (it's much easier to review diffs and catch mistakes), and minify only at build time or at the serialisation layer in production.&lt;/p&gt;

&lt;p&gt;If you ever need to go the other direction — you've received a minified blob and need to read it — &lt;a href="https://jsonindenter.com/?utm_source=devto&amp;amp;utm_medium=article&amp;amp;utm_campaign=minify-json-payloads" rel="noopener noreferrer"&gt;JSON Indenter&lt;/a&gt; formats it back into something human-readable instantly. Paste the minified string and it comes out with proper indentation and syntax highlighting. Useful when you're hunting a bug in a production response.&lt;/p&gt;

&lt;p&gt;For tips on validating JSON before you minify it (invalid JSON will cause &lt;code&gt;JSON.parse&lt;/code&gt; to throw at runtime), see &lt;em&gt;&lt;a href="https://dev.to/pioneer10/why-your-json-keeps-breaking-and-how-to-fix-it-fast-2lej"&gt;Why Your JSON Keeps Breaking (And How to Fix It Fast)&lt;/a&gt;&lt;/em&gt; — it covers the most common syntax mistakes that slip through code review.&lt;/p&gt;




&lt;p&gt;What's your preferred approach — minifying at the application layer, at the CDN edge, or somewhere else? And have you ever hit a production bug that traced back to a minification step mangling something unexpectedly?&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Free tools used in this post:&lt;/em&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://jsonindenter.com/json-minifier?utm_source=devto&amp;amp;utm_medium=article&amp;amp;utm_campaign=minify-json-payloads" rel="noopener noreferrer"&gt;JSON Minifier&lt;/a&gt; — strips whitespace from any JSON payload instantly, runs entirely in your browser&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://jsonindenter.com/?utm_source=devto&amp;amp;utm_medium=article&amp;amp;utm_campaign=minify-json-payloads" rel="noopener noreferrer"&gt;JSON Indenter&lt;/a&gt; — formats minified JSON back into readable, indented output&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://jsonindenter.com/all-json-tools?utm_source=devto&amp;amp;utm_medium=article&amp;amp;utm_campaign=minify-json-payloads" rel="noopener noreferrer"&gt;All tools&lt;/a&gt; — client-side, no sign-up, nothing leaves your browser&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>javascript</category>
      <category>webdev</category>
      <category>tutorial</category>
      <category>programming</category>
    </item>
    <item>
      <title>Why Your JSON Keeps Breaking (And How to Fix It Fast)</title>
      <dc:creator>Tahmid</dc:creator>
      <pubDate>Sat, 18 Apr 2026 21:48:08 +0000</pubDate>
      <link>https://dev.to/pioneer10/why-your-json-keeps-breaking-and-how-to-fix-it-fast-2lej</link>
      <guid>https://dev.to/pioneer10/why-your-json-keeps-breaking-and-how-to-fix-it-fast-2lej</guid>
      <description>&lt;p&gt;You paste a JSON config into your app and hit &lt;code&gt;SyntaxError: Unexpected token&lt;/code&gt;. You scan 60 lines of curly braces, counting quotes by eye. Five minutes later you spot it — a trailing comma on line 47. Sound familiar?&lt;/p&gt;

&lt;p&gt;JSON is ruthlessly strict. No trailing commas. No comments. No single quotes. No unquoted keys. Even one character out of place produces a completely opaque error message that points you to the wrong line half the time. This post covers the four JSON mistakes that cause 90% of parse failures, how to spot them instantly, and how to stop wasting time on them.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Four Most Common JSON Errors
&lt;/h2&gt;

&lt;h3&gt;
  
  
  1. Trailing Commas
&lt;/h3&gt;

&lt;p&gt;This is the most frequent offender, especially if your JSON was hand-written or copied from a JavaScript object literal.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Broken:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Alice"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"role"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"admin"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"active"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Fixed:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Alice"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"role"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"admin"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"active"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The trailing comma after &lt;code&gt;true&lt;/code&gt; is valid JavaScript but illegal JSON. Most parsers point you to the closing brace, not the offending comma, which sends you hunting in the wrong direction.&lt;/p&gt;

&lt;h3&gt;
  
  
  2. Single Quotes Instead of Double Quotes
&lt;/h3&gt;

&lt;p&gt;Developers who spend most of their time in JavaScript or Python often reach for single quotes out of habit.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Broken:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="err"&gt;'username':&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;'bob'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="err"&gt;'permissions':&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="err"&gt;'read'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;'write'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Fixed:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"username"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"bob"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"permissions"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"read"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"write"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The JSON specification (RFC 8259) is explicit: strings must use double quotes. Single quotes are not valid, period.&lt;/p&gt;

&lt;h3&gt;
  
  
  3. Comments Embedded in JSON
&lt;/h3&gt;

&lt;p&gt;If you copied a config from documentation or a README, it may include comments added for human clarity. JSON does not support comments.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Broken:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="err"&gt;//&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;database&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;settings&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"host"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"localhost"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"port"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;5432&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="err"&gt;/*&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;default&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;postgres&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;port&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;*/&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"database"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"myapp"&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Fixed:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"host"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"localhost"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"port"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;5432&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"database"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"myapp"&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Strip every &lt;code&gt;//&lt;/code&gt; line comment and every &lt;code&gt;/* */&lt;/code&gt; block comment before parsing. If you need to annotate your configs, consider YAML or TOML — or adopt a &lt;code&gt;_comment&lt;/code&gt; key as a convention.&lt;/p&gt;

&lt;h3&gt;
  
  
  4. JavaScript-Only Values
&lt;/h3&gt;

&lt;p&gt;&lt;code&gt;undefined&lt;/code&gt;, &lt;code&gt;NaN&lt;/code&gt;, and &lt;code&gt;Infinity&lt;/code&gt; are valid JavaScript but completely foreign to JSON. Only &lt;code&gt;null&lt;/code&gt;, &lt;code&gt;true&lt;/code&gt;, &lt;code&gt;false&lt;/code&gt;, numbers, strings, arrays, and objects are allowed.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Broken:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"count"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;NaN&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"result"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;undefined&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"ratio"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;Infinity&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Fixed:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"count"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"result"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"ratio"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;These typically appear when serializing JavaScript objects that contain these special values — a common trap when using &lt;code&gt;JSON.stringify()&lt;/code&gt; without a replacer function.&lt;/p&gt;

&lt;h2&gt;
  
  
  Stop Hunting for Errors by Eye
&lt;/h2&gt;

&lt;p&gt;Reading JSON manually for syntax errors is a poor use of your attention. Paste your broken JSON into the &lt;a href="https://jsonindenter.com/?utm_source=devto&amp;amp;utm_medium=article&amp;amp;utm_campaign=why-your-json-keeps-breaking" rel="noopener noreferrer"&gt;JSON Formatter &amp;amp; Validator&lt;/a&gt; at jsonindenter.com and it will immediately highlight exactly which line is wrong and why. It also re-indents the output so that nested structures become readable at a glance — a lifesaver when someone hands you a 2KB single-line blob.&lt;/p&gt;

&lt;p&gt;The validator catches all four of the errors described above and gives you a plain-English error message rather than the cryptic column-number output your runtime throws.&lt;/p&gt;

&lt;h2&gt;
  
  
  Comparing Two JSON Responses
&lt;/h2&gt;

&lt;p&gt;Another common scenario: you have two versions of a JSON response — one from staging, one from production — and something is different but you can't tell what. Diffing them by eye across two editor tabs is tedious and error-prone.&lt;/p&gt;

&lt;p&gt;The &lt;a href="https://jsonindenter.com/json-diff?utm_source=devto&amp;amp;utm_medium=article&amp;amp;utm_campaign=why-your-json-keeps-breaking" rel="noopener noreferrer"&gt;JSON Diff tool&lt;/a&gt; does a structural comparison of two JSON documents and highlights exactly which keys were added, removed, or changed in value. Because it understands JSON structure, it doesn't get confused by whitespace changes or key reordering — it shows you only what actually changed semantically.&lt;/p&gt;

&lt;h2&gt;
  
  
  Minifying for Production
&lt;/h2&gt;

&lt;p&gt;Once your JSON is clean and validated, you may want to strip all whitespace before embedding it in a build artifact or sending it over the wire. The &lt;a href="https://jsonindenter.com/json-minifier?utm_source=devto&amp;amp;utm_medium=article&amp;amp;utm_campaign=why-your-json-keeps-breaking" rel="noopener noreferrer"&gt;JSON Minifier&lt;/a&gt; removes all unnecessary whitespace in one click. For a 500-line formatted config file, the minified output is typically 3–5× smaller by byte count — a meaningful difference in payload-sensitive contexts like mobile APIs or edge functions.&lt;/p&gt;

&lt;h2&gt;
  
  
  A Practical Debugging Workflow
&lt;/h2&gt;

&lt;p&gt;When JSON breaks in a project, this three-step process gets me to a fix in under two minutes:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Paste into the formatter — it shows the exact error location and corrects indentation so the structure is visible&lt;/li&gt;
&lt;li&gt;Fix the flagged issue and validate again until the document is clean&lt;/li&gt;
&lt;li&gt;If the JSON parses successfully but behavior is still wrong, use the diff tool to compare against the last known-good version line by line&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;This replaces what used to be ten minutes of manual scanning and guessing.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Bigger Picture
&lt;/h2&gt;

&lt;p&gt;Most JSON headaches come from the same handful of mistakes, and almost all of them come from editing JSON by hand or copying it from contexts that allow looser syntax — JavaScript objects, Python dicts, config files with comments. The fix isn't memorizing the spec; it's building a habit of running JSON through a validator before it ever reaches your parser.&lt;/p&gt;

&lt;p&gt;What's your most painful "I spent an hour on a trailing comma" story? Drop it in the comments — I know I'm not the only one who's been there.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Free tools used in this post:&lt;/em&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://jsonindenter.com/?utm_source=devto&amp;amp;utm_medium=article&amp;amp;utm_campaign=why-your-json-keeps-breaking" rel="noopener noreferrer"&gt;JSON Formatter &amp;amp; Validator&lt;/a&gt; — paste broken JSON and get instant error highlighting with clean re-indentation&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://jsonindenter.com/json-diff?utm_source=devto&amp;amp;utm_medium=article&amp;amp;utm_campaign=why-your-json-keeps-breaking" rel="noopener noreferrer"&gt;JSON Diff&lt;/a&gt; — structural comparison of two JSON documents, shows exactly what changed semantically&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://jsonindenter.com/json-minifier?utm_source=devto&amp;amp;utm_medium=article&amp;amp;utm_campaign=why-your-json-keeps-breaking" rel="noopener noreferrer"&gt;JSON Minifier&lt;/a&gt; — strips all whitespace from JSON for smaller payloads&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://jsonindenter.com/all-json-tools?utm_source=devto&amp;amp;utm_medium=article&amp;amp;utm_campaign=why-your-json-keeps-breaking" rel="noopener noreferrer"&gt;All tools&lt;/a&gt; — client-side, no sign-up, nothing leaves your browser&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>javascript</category>
      <category>webdev</category>
      <category>tutorial</category>
      <category>beginners</category>
    </item>
    <item>
      <title>How to Compare Two JSON Objects and Spot the Differences Instantly</title>
      <dc:creator>Tahmid</dc:creator>
      <pubDate>Sat, 18 Apr 2026 21:47:55 +0000</pubDate>
      <link>https://dev.to/pioneer10/how-to-compare-two-json-objects-and-spot-the-differences-instantly-1b4j</link>
      <guid>https://dev.to/pioneer10/how-to-compare-two-json-objects-and-spot-the-differences-instantly-1b4j</guid>
      <description>&lt;p&gt;You're two hours into debugging a production issue. Your API contract says the response should look like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"user"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;42&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Alice"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"role"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"admin"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"active"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"permissions"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"read"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"write"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"delete"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;But something downstream is breaking. You log the actual response and squint at it. It &lt;em&gt;looks&lt;/em&gt; the same. Roughly. But your authorization middleware just denied an admin user.&lt;/p&gt;

&lt;p&gt;That's when you notice: &lt;code&gt;"role"&lt;/code&gt; is missing. Or it became &lt;code&gt;"roles"&lt;/code&gt;. Or &lt;code&gt;"active"&lt;/code&gt; is now the string &lt;code&gt;"true"&lt;/code&gt; instead of a boolean. Bugs like this are trivially small and brutally hard to spot in a wall of JSON text — especially when the payload is minified or poorly indented.&lt;/p&gt;

&lt;p&gt;Here's a practical workflow for comparing two JSON objects and catching differences before they catch you.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 1: Format both objects identically
&lt;/h2&gt;

&lt;p&gt;Comparing minified JSON visually is a waste of time. If your two blobs have different indentation, trailing whitespace, or inconsistent key ordering, even a side-by-side diff tool will light up with false positives.&lt;/p&gt;

&lt;p&gt;Before anything else, run both objects through a formatter. Paste each one into &lt;a href="https://jsonindenter.com/?utm_source=devto&amp;amp;utm_medium=article&amp;amp;utm_campaign=json-diff-compare-objects" rel="noopener noreferrer"&gt;JSON Indenter&lt;/a&gt; and make sure you're comparing apples to apples: same indentation style, same structure, same readability.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Before (minified):&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nl"&gt;"user"&lt;/span&gt;&lt;span class="p"&gt;:{&lt;/span&gt;&lt;span class="nl"&gt;"id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;42&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="s2"&gt;"Alice"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="nl"&gt;"role"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="s2"&gt;"admin"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="nl"&gt;"active"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="nl"&gt;"permissions"&lt;/span&gt;&lt;span class="p"&gt;:[&lt;/span&gt;&lt;span class="s2"&gt;"read"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="s2"&gt;"write"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="s2"&gt;"delete"&lt;/span&gt;&lt;span class="p"&gt;]}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;After (formatted):&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"user"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;42&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Alice"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"role"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"admin"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"active"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"permissions"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"read"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"write"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"delete"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now you can actually read it. This single step eliminates a whole class of "differences" that are really just formatting noise.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 2: Validate before you compare
&lt;/h2&gt;

&lt;p&gt;If either object has a syntax error, your diff will give you nonsense. A stray trailing comma, an unquoted key, or a missing bracket will corrupt the comparison before it starts.&lt;/p&gt;

&lt;p&gt;Run both objects through the &lt;a href="https://jsonindenter.com/validator?utm_source=devto&amp;amp;utm_medium=article&amp;amp;utm_campaign=json-diff-compare-objects" rel="noopener noreferrer"&gt;JSON Validator&lt;/a&gt; before proceeding. It surfaces syntax errors with exact line numbers, so you fix the structure first and compare content second.&lt;/p&gt;

&lt;p&gt;This matters more than you'd think. Many JSON objects in the wild come from sources that produce &lt;em&gt;technically invalid&lt;/em&gt; JSON — config files with comments, legacy APIs with trailing commas, log entries with prepended timestamps. Validate early.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 3: Compare programmatically when you need precision
&lt;/h2&gt;

&lt;p&gt;For automated tests or CI pipelines, visual comparison doesn't scale. Here's where programmatic diffing becomes essential.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The naive approach — and why it fails:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;a&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;user&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;42&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Alice&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="p"&gt;};&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;b&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;user&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Alice&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;42&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="p"&gt;};&lt;/span&gt;

&lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;stringify&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;a&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="nx"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;stringify&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;b&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
&lt;span class="c1"&gt;// false — key order matters for string comparison, not for JSON semantics&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;JSON.stringify&lt;/code&gt; is order-sensitive. Two objects that are semantically identical can produce different strings if their keys were inserted in a different order.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;A better approach — recursive diff with specific output:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;jsonDiff&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;a&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;b&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;path&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;""&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;diffs&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[];&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;allKeys&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Set&lt;/span&gt;&lt;span class="p"&gt;([...&lt;/span&gt;&lt;span class="nb"&gt;Object&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;keys&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;a&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="p"&gt;...&lt;/span&gt;&lt;span class="nb"&gt;Object&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;keys&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;b&lt;/span&gt;&lt;span class="p"&gt;)]);&lt;/span&gt;

  &lt;span class="k"&gt;for &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;key&lt;/span&gt; &lt;span class="k"&gt;of&lt;/span&gt; &lt;span class="nx"&gt;allKeys&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;fullPath&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;path&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;path&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;.&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;key&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;key&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;key&lt;/span&gt; &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="nx"&gt;a&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nx"&gt;diffs&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;push&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`ADDED: &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;fullPath&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt; = &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;stringify&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;b&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;key&lt;/span&gt;&lt;span class="p"&gt;])}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;key&lt;/span&gt; &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="nx"&gt;b&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nx"&gt;diffs&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;push&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`REMOVED: &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;fullPath&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;typeof&lt;/span&gt; &lt;span class="nx"&gt;a&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;key&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;object&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nx"&gt;a&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;key&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;!==&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;
               &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="k"&gt;typeof&lt;/span&gt; &lt;span class="nx"&gt;b&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;key&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;object&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nx"&gt;b&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;key&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;!==&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nx"&gt;diffs&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;push&lt;/span&gt;&lt;span class="p"&gt;(...&lt;/span&gt;&lt;span class="nf"&gt;jsonDiff&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;a&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;key&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="nx"&gt;b&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;key&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="nx"&gt;fullPath&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;a&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;key&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;!==&lt;/span&gt; &lt;span class="nx"&gt;b&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;key&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nx"&gt;diffs&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;push&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`CHANGED: &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;fullPath&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;: &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;stringify&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;a&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;key&lt;/span&gt;&lt;span class="p"&gt;])}&lt;/span&gt;&lt;span class="s2"&gt; → &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;stringify&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;b&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;key&lt;/span&gt;&lt;span class="p"&gt;])}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;diffs&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;jsonDiff&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;user&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;42&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Alice&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;role&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;admin&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="na"&gt;active&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;user&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;42&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Alice&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="na"&gt;active&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;true&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;result&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="c1"&gt;// ["REMOVED: user.role", "CHANGED: active: true → \"true\""]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is far more useful than a raw boolean — you get the exact path and the exact values that changed.&lt;/p&gt;

&lt;h2&gt;
  
  
  Where types silently bite you
&lt;/h2&gt;

&lt;p&gt;The most dangerous JSON diffs aren't missing keys — they're type changes. A value going from &lt;code&gt;true&lt;/code&gt; (boolean) to &lt;code&gt;"true"&lt;/code&gt; (string) looks identical in most log outputs but behaves completely differently in application logic.&lt;/p&gt;

&lt;p&gt;Your comparison logic needs to be explicit about types. In the function above, &lt;code&gt;a[key] !== b[key]&lt;/code&gt; catches this because JavaScript's strict equality distinguishes &lt;code&gt;true&lt;/code&gt; from &lt;code&gt;"true"&lt;/code&gt;. In Python, watch out for integers and floats comparing as equal (&lt;code&gt;1 == 1.0&lt;/code&gt;), which can mask precision changes that matter to downstream typed systems.&lt;/p&gt;

&lt;p&gt;For a deeper look at the syntax issues and type mismatches that show up most often in real payloads, the &lt;a href="https://jsonindenter.com/blog/5-common-json-errors?utm_source=devto&amp;amp;utm_medium=article&amp;amp;utm_campaign=json-diff-compare-objects" rel="noopener noreferrer"&gt;5 Common JSON Errors&lt;/a&gt; post on JSON Indenter's blog is worth a read — it covers the patterns that trip up developers most reliably.&lt;/p&gt;

&lt;h2&gt;
  
  
  When to reach for a library
&lt;/h2&gt;

&lt;p&gt;For production use, libraries like &lt;code&gt;deep-diff&lt;/code&gt; (Node.js) or &lt;code&gt;deepdiff&lt;/code&gt; (Python) go further: they handle arrays intelligently, track moved elements, and give you structured change records you can iterate over. But for day-to-day debugging — the "why is this response different from yesterday?" question you face a dozen times a month — formatting both payloads consistently, validating them, and running a simple recursive diff gets you to the answer in under two minutes.&lt;/p&gt;

&lt;p&gt;What's your go-to approach when two API responses look identical but aren't? Have you ever been burned by a boolean quietly becoming a string, or a key silently renamed in a schema update? Drop your war stories in the comments — I'd love to hear how others handle this.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Free tools used in this post:&lt;/em&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://jsonindenter.com/?utm_source=devto&amp;amp;utm_medium=article&amp;amp;utm_campaign=json-diff-compare-objects" rel="noopener noreferrer"&gt;JSON Formatter &amp;amp; Validator&lt;/a&gt; — instantly formats and beautifies JSON in your browser, client-side&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://jsonindenter.com/validator?utm_source=devto&amp;amp;utm_medium=article&amp;amp;utm_campaign=json-diff-compare-objects" rel="noopener noreferrer"&gt;JSON Validator Pro&lt;/a&gt; — lint and validate JSON with exact line-number error reporting&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://jsonindenter.com/all-json-tools?utm_source=devto&amp;amp;utm_medium=article&amp;amp;utm_campaign=json-diff-compare-objects" rel="noopener noreferrer"&gt;All tools&lt;/a&gt; — client-side, no sign-up, nothing leaves your browser&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>javascript</category>
      <category>webdev</category>
      <category>tutorial</category>
      <category>programming</category>
    </item>
    <item>
      <title>How to structure JSON for LLMs (and stop wasting tokens)</title>
      <dc:creator>Tahmid</dc:creator>
      <pubDate>Sat, 18 Apr 2026 19:15:15 +0000</pubDate>
      <link>https://dev.to/pioneer10/how-to-structure-json-for-llms-and-stop-wasting-tokens-4dc6</link>
      <guid>https://dev.to/pioneer10/how-to-structure-json-for-llms-and-stop-wasting-tokens-4dc6</guid>
      <description>&lt;p&gt;Most developers treat JSON as an afterthought when building LLM-powered apps. They dump raw API responses into prompts and wonder why the model hallucinates, misreads fields, or burns through tokens.&lt;/p&gt;

&lt;p&gt;JSON structure is a first-class concern in AI engineering. Here's how to get it right.&lt;/p&gt;




&lt;h2&gt;
  
  
  The problem: LLMs don't read JSON like humans do
&lt;/h2&gt;

&lt;p&gt;When you paste this into a prompt:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nl"&gt;"user"&lt;/span&gt;&lt;span class="p"&gt;:{&lt;/span&gt;&lt;span class="nl"&gt;"id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="s2"&gt;"Alice"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="nl"&gt;"preferences"&lt;/span&gt;&lt;span class="p"&gt;:{&lt;/span&gt;&lt;span class="nl"&gt;"theme"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="s2"&gt;"dark"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="nl"&gt;"notifications"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="nl"&gt;"language"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="s2"&gt;"en"&lt;/span&gt;&lt;span class="p"&gt;}}}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The model tokenizes it character by character. Every &lt;code&gt;{&lt;/code&gt;, &lt;code&gt;"&lt;/code&gt;, &lt;code&gt;:&lt;/code&gt;, and &lt;code&gt;,&lt;/code&gt; is a token. Deeply nested structures force the model to maintain more working context just to understand the shape of the data — before it even processes the values.&lt;/p&gt;

&lt;p&gt;The result: more tokens consumed, more room for misinterpretation, higher API costs.&lt;/p&gt;




&lt;h2&gt;
  
  
  Rule 1: Flatten before you prompt
&lt;/h2&gt;

&lt;p&gt;Nested JSON is great for APIs. It's bad for prompts.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Before:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"user"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"profile"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Alice"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"age"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;28&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;After (flattened):&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"user_name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Alice"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"user_age"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;28&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;One level deep is almost always enough for LLM context. If the model needs to reason about relationships, describe them in natural language alongside the data — don't encode them in nesting.&lt;/p&gt;




&lt;h2&gt;
  
  
  Rule 2: Strip fields the model doesn't need
&lt;/h2&gt;

&lt;p&gt;Every field in your JSON costs tokens. If the model doesn't need &lt;code&gt;created_at&lt;/code&gt;, &lt;code&gt;updated_at&lt;/code&gt;, &lt;code&gt;internal_id&lt;/code&gt;, or &lt;code&gt;_metadata&lt;/code&gt; — remove them before building the prompt.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;created_at&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;updated_at&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;_metadata&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;...&lt;/span&gt;&lt;span class="nx"&gt;relevant&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;apiResponse&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;prompt&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;`Here is the user data: &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;stringify&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;relevant&lt;/span&gt;&lt;span class="p"&gt;)}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This alone can cut token usage by 20–40% on typical API responses.&lt;/p&gt;




&lt;h2&gt;
  
  
  Rule 3: Use TOON for large payloads
&lt;/h2&gt;

&lt;p&gt;If you're passing payloads larger than ~500 tokens, consider TOON (Token-Oriented Object Notation). It's a compact alternative to JSON that strips redundant syntax while preserving structure.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;JSON:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Alice"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"role"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"admin"&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Bob"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"role"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"editor"&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;TOON:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csvs"&gt;&lt;code&gt;&lt;span class="k"&gt;name&lt;/span&gt;&lt;span class="err"&gt;|&lt;/span&gt;&lt;span class="k"&gt;role&lt;/span&gt;
&lt;span class="k"&gt;Alice&lt;/span&gt;&lt;span class="err"&gt;|&lt;/span&gt;&lt;span class="k"&gt;admin&lt;/span&gt;
&lt;span class="k"&gt;Bob&lt;/span&gt;&lt;span class="err"&gt;|&lt;/span&gt;&lt;span class="k"&gt;editor&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Token reduction: &lt;strong&gt;30–60%&lt;/strong&gt; on typical datasets. The model reads it correctly because the structure is still unambiguous — just more compact.&lt;/p&gt;

&lt;p&gt;Try it on your own payloads with the &lt;a href="https://jsonindenter.com/json-to-toon?utm_source=devto&amp;amp;utm_medium=article&amp;amp;utm_campaign=llm-json" rel="noopener noreferrer"&gt;JSON to TOON converter&lt;/a&gt;. There's also a &lt;a href="https://jsonindenter.com/toon-to-json?utm_source=devto&amp;amp;utm_medium=article&amp;amp;utm_campaign=llm-json" rel="noopener noreferrer"&gt;TOON to JSON&lt;/a&gt; converter for decoding the model's response back.&lt;/p&gt;




&lt;h2&gt;
  
  
  Rule 4: Use JSON Schema to enforce structured outputs
&lt;/h2&gt;

&lt;p&gt;LLMs can return JSON — but without constraints, they hallucinate keys, change types, and add fields you didn't ask for.&lt;/p&gt;

&lt;p&gt;The fix: define a schema and include it in your system prompt.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"object"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"properties"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"sentiment"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"string"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"enum"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"positive"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"negative"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"neutral"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"confidence"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"number"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"minimum"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"maximum"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"summary"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"string"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"maxLength"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;200&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"required"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"sentiment"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"confidence"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"summary"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Tell the model: &lt;em&gt;"Respond only with a JSON object matching this schema. No explanation, no markdown."&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Then validate the output with a JSON Schema validator before trusting it. This is especially critical in agentic workflows where one bad output poisons downstream steps. You can generate a schema automatically from any sample payload using the &lt;a href="https://jsonindenter.com/json-to-schema?utm_source=devto&amp;amp;utm_medium=article&amp;amp;utm_campaign=llm-json" rel="noopener noreferrer"&gt;JSON Schema generator&lt;/a&gt; — useful as a starting point you can then tighten up.&lt;/p&gt;




&lt;h2&gt;
  
  
  Rule 5: Use Pydantic or Zod to validate at the boundary
&lt;/h2&gt;

&lt;p&gt;Never trust raw LLM JSON output in production. Parse and validate it immediately.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Python (FastAPI / AI agents):&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;pydantic&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;BaseModel&lt;/span&gt;

&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;SentimentResult&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;BaseModel&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;sentiment&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;
    &lt;span class="n"&gt;confidence&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;float&lt;/span&gt;
    &lt;span class="n"&gt;summary&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;

&lt;span class="n"&gt;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;SentimentResult&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;model_validate_json&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;llm_output&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;TypeScript (Next.js / tRPC):&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;z&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;zod&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;SentimentResult&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;z&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;object&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;sentiment&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;z&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;enum&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;positive&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;negative&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;neutral&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]),&lt;/span&gt;
  &lt;span class="na"&gt;confidence&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;z&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;number&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;min&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;max&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
  &lt;span class="na"&gt;summary&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;z&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;string&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;max&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;200&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;SentimentResult&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;parse&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;parse&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;llmOutput&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Writing these by hand from a large JSON payload is tedious. The &lt;a href="https://jsonindenter.com/json-to-pydantic?utm_source=devto&amp;amp;utm_medium=article&amp;amp;utm_campaign=llm-json" rel="noopener noreferrer"&gt;JSON to Pydantic&lt;/a&gt; and &lt;a href="https://jsonindenter.com/json-to-zod?utm_source=devto&amp;amp;utm_medium=article&amp;amp;utm_campaign=llm-json" rel="noopener noreferrer"&gt;JSON to Zod&lt;/a&gt; generators handle it instantly — paste your payload, get the model.&lt;/p&gt;




&lt;h2&gt;
  
  
  Rule 6: Use TypeScript interfaces when working with typed LLM outputs
&lt;/h2&gt;

&lt;p&gt;If you're building in TypeScript, generating interfaces from your JSON response shapes saves time and prevents drift between what the LLM returns and what your code expects.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Generated from your actual LLM response shape&lt;/span&gt;
&lt;span class="kr"&gt;interface&lt;/span&gt; &lt;span class="nx"&gt;SentimentResponse&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;sentiment&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;positive&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;negative&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;neutral&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;confidence&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;number&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;summary&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;a href="https://jsonindenter.com/json-to-typescript?utm_source=devto&amp;amp;utm_medium=article&amp;amp;utm_campaign=llm-json" rel="noopener noreferrer"&gt;JSON to TypeScript&lt;/a&gt; converter generates these from any payload — useful when you're iterating quickly on prompt outputs and want the type system to catch regressions.&lt;/p&gt;




&lt;h2&gt;
  
  
  The full checklist
&lt;/h2&gt;

&lt;p&gt;Before passing JSON to an LLM:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Flatten nested structures to one level where possible&lt;/li&gt;
&lt;li&gt;Strip fields irrelevant to the task&lt;/li&gt;
&lt;li&gt;Use TOON for payloads &amp;gt; 500 tokens&lt;/li&gt;
&lt;li&gt;Define a JSON Schema for expected output&lt;/li&gt;
&lt;li&gt;Validate output with Pydantic or Zod before use&lt;/li&gt;
&lt;li&gt;Use TypeScript interfaces to catch output shape regressions&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;These aren't micro-optimisations. On high-volume AI apps, they compound into significant cost and reliability improvements.&lt;/p&gt;




&lt;p&gt;What's your current approach to JSON in LLM workflows? Drop it in the comments — I'm curious how others handle this.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Free tools used in this post:&lt;/em&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://jsonindenter.com/json-to-toon?utm_source=devto&amp;amp;utm_medium=article&amp;amp;utm_campaign=llm-json" rel="noopener noreferrer"&gt;JSON to TOON&lt;/a&gt; — compress JSON for LLM context&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://jsonindenter.com/toon-to-json?utm_source=devto&amp;amp;utm_medium=article&amp;amp;utm_campaign=llm-json" rel="noopener noreferrer"&gt;TOON to JSON&lt;/a&gt; — decode TOON back to JSON&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://jsonindenter.com/json-to-schema?utm_source=devto&amp;amp;utm_medium=article&amp;amp;utm_campaign=llm-json" rel="noopener noreferrer"&gt;JSON Schema Generator&lt;/a&gt; — generate schemas from sample payloads&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://jsonindenter.com/json-to-pydantic?utm_source=devto&amp;amp;utm_medium=article&amp;amp;utm_campaign=llm-json" rel="noopener noreferrer"&gt;JSON to Pydantic&lt;/a&gt; — instant Python models&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://jsonindenter.com/json-to-zod?utm_source=devto&amp;amp;utm_medium=article&amp;amp;utm_campaign=llm-json" rel="noopener noreferrer"&gt;JSON to Zod&lt;/a&gt; — instant TypeScript validation schemas&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://jsonindenter.com/json-to-typescript?utm_source=devto&amp;amp;utm_medium=article&amp;amp;utm_campaign=llm-json" rel="noopener noreferrer"&gt;JSON to TypeScript&lt;/a&gt; — generate interfaces from any payload&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://jsonindenter.com/all-json-tools?utm_source=devto&amp;amp;utm_medium=article&amp;amp;utm_campaign=llm-json" rel="noopener noreferrer"&gt;All tools&lt;/a&gt; — client-side, no sign-up, nothing leaves your browser&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>ai</category>
      <category>json</category>
      <category>webdev</category>
      <category>llm</category>
    </item>
    <item>
      <title>JWT security mistakes that will get you breached</title>
      <dc:creator>Tahmid</dc:creator>
      <pubDate>Sat, 18 Apr 2026 19:05:55 +0000</pubDate>
      <link>https://dev.to/pioneer10/jwt-security-mistakes-that-will-get-you-breached-4834</link>
      <guid>https://dev.to/pioneer10/jwt-security-mistakes-that-will-get-you-breached-4834</guid>
      <description>&lt;p&gt;JWTs are everywhere. Auth tokens, API keys, session management — if you're building web apps, you're almost certainly using them. They're also one of the most commonly misconfigured pieces of security infrastructure in production systems.&lt;/p&gt;

&lt;p&gt;Here are the mistakes I see repeatedly, and exactly how to fix them.&lt;/p&gt;




&lt;h2&gt;
  
  
  Quick recap: what a JWT actually is
&lt;/h2&gt;

&lt;p&gt;A JWT has three parts separated by dots:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;header.payload.signature
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Header&lt;/strong&gt; — algorithm used to sign the token (&lt;code&gt;HS256&lt;/code&gt;, &lt;code&gt;RS256&lt;/code&gt;, etc.)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Payload&lt;/strong&gt; — the claims (user ID, roles, expiry, etc.)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Signature&lt;/strong&gt; — cryptographic proof the token hasn't been tampered with
The payload is &lt;strong&gt;base64-encoded, not encrypted&lt;/strong&gt;. Anyone who intercepts your token can read everything in it. This matters more than most developers realise.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If you want to see this in action, paste any token into a &lt;a href="https://jsonindenter.com/jwt-decoder?utm_source=devto&amp;amp;utm_medium=article&amp;amp;utm_campaign=jwt-security" rel="noopener noreferrer"&gt;JWT decoder&lt;/a&gt; and read the payload in plain text — no tools, no secret needed.&lt;/p&gt;




&lt;h2&gt;
  
  
  Mistake 1: Using the &lt;code&gt;none&lt;/code&gt; algorithm
&lt;/h2&gt;

&lt;p&gt;This is the most catastrophic JWT vulnerability. Some libraries accept a token signed with &lt;code&gt;alg: none&lt;/code&gt; — meaning no signature at all.&lt;/p&gt;

&lt;p&gt;An attacker can craft a token like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="err"&gt;//&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;Header&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;(base&lt;/span&gt;&lt;span class="mi"&gt;64&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;decoded)&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"alg"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"none"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"typ"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"JWT"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;

&lt;/span&gt;&lt;span class="err"&gt;//&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;Payload&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;(base&lt;/span&gt;&lt;span class="mi"&gt;64&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;decoded)&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"sub"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"1234"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"role"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"admin"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If your server accepts it, the attacker just gave themselves admin access with no credentials.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Fix:&lt;/strong&gt; Explicitly allowlist the algorithms your server accepts. Never allow &lt;code&gt;none&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Node.js / jsonwebtoken&lt;/span&gt;
&lt;span class="nx"&gt;jwt&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;verify&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;token&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;secret&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;algorithms&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;HS256&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  Mistake 2: Weak or hardcoded secrets
&lt;/h2&gt;

&lt;p&gt;&lt;code&gt;HS256&lt;/code&gt; is a symmetric algorithm — the same secret signs and verifies tokens. If your secret is weak or leaked, every token ever issued is compromised.&lt;/p&gt;

&lt;p&gt;Common offenders:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;secret
password
jwt_secret
mysecretkey
your-256-bit-secret   ← literally from the JWT.io example
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Fix:&lt;/strong&gt; Generate secrets with sufficient entropy and store them in environment variables, never in code.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Generate a strong secret&lt;/span&gt;
node &lt;span class="nt"&gt;-e&lt;/span&gt; &lt;span class="s2"&gt;"console.log(require('crypto').randomBytes(64).toString('hex'))"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;For production systems with multiple services, prefer &lt;code&gt;RS256&lt;/code&gt; (asymmetric) — the private key signs, the public key verifies. A compromised verification service can't forge tokens. You can test tokens signed with different algorithms using the &lt;a href="https://jsonindenter.com/jwt-encoder?utm_source=devto&amp;amp;utm_medium=article&amp;amp;utm_campaign=jwt-security" rel="noopener noreferrer"&gt;JWT encoder&lt;/a&gt; to understand exactly what gets embedded.&lt;/p&gt;




&lt;h2&gt;
  
  
  Mistake 3: Storing JWTs in localStorage
&lt;/h2&gt;

&lt;p&gt;&lt;code&gt;localStorage&lt;/code&gt; is accessible to any JavaScript running on your page. A single XSS vulnerability — in your code, a dependency, or a third-party script — exposes every stored token.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Fix:&lt;/strong&gt; Store JWTs in &lt;code&gt;HttpOnly&lt;/code&gt; cookies. They're invisible to JavaScript entirely.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;cookie&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;token&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;jwt&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;httpOnly&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;    &lt;span class="c1"&gt;// not accessible via JS&lt;/span&gt;
  &lt;span class="na"&gt;secure&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;      &lt;span class="c1"&gt;// HTTPS only&lt;/span&gt;
  &lt;span class="na"&gt;sameSite&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;strict&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="c1"&gt;// CSRF protection&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Yes, this requires handling CSRF. That's a better problem to have than XSS token theft.&lt;/p&gt;




&lt;h2&gt;
  
  
  Mistake 4: No expiry — or expiry that's too long
&lt;/h2&gt;

&lt;p&gt;A JWT with no &lt;code&gt;exp&lt;/code&gt; claim is valid forever. If it's leaked, stolen, or the user's account is compromised, you have no way to invalidate it short of rotating your signing secret (which invalidates every token).&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Fix:&lt;/strong&gt; Set short expiry for access tokens, longer for refresh tokens.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;accessToken&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;jwt&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;sign&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;payload&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;secret&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;expiresIn&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;15m&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;refreshToken&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;jwt&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;sign&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;payload&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;secret&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;expiresIn&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;7d&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;15 minutes for access tokens is a common production standard. Refresh tokens should be rotated on use and stored server-side so they can be revoked.&lt;/p&gt;




&lt;h2&gt;
  
  
  Mistake 5: Putting sensitive data in the payload
&lt;/h2&gt;

&lt;p&gt;The payload is not encrypted. It's trivially decoded by anyone with the token:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"eyJ1c2VyIjoiYWxpY2UiLCJwYXNzd29yZCI6InNlY3JldCJ9"&lt;/span&gt; | &lt;span class="nb"&gt;base64&lt;/span&gt; &lt;span class="nt"&gt;-d&lt;/span&gt;
&lt;span class="c"&gt;# {"user":"alice","password":"secret"}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I've seen production tokens containing passwords, full PII, credit card fragments, and internal system details. All readable by anyone who intercepts the token. Try it yourself — paste that string into a &lt;a href="https://jsonindenter.com/jwt-decoder?utm_source=devto&amp;amp;utm_medium=article&amp;amp;utm_campaign=jwt-security" rel="noopener noreferrer"&gt;JWT decoder&lt;/a&gt; and see exactly what's exposed.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Fix:&lt;/strong&gt; Put only the minimum identifying information in the payload.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"sub"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"user_abc123"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"role"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"admin"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"exp"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1735689600&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Look up everything else server-side using the &lt;code&gt;sub&lt;/code&gt; claim. If you need the payload encrypted, use JWE (JSON Web Encryption) — a different standard entirely.&lt;/p&gt;




&lt;h2&gt;
  
  
  Mistake 6: Not validating claims server-side
&lt;/h2&gt;

&lt;p&gt;Libraries verify the signature automatically — but claim validation is on you.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Bad — only verifies signature&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;decoded&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;jwt&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;verify&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;token&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;secret&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="c1"&gt;// Good — verify signature AND validate claims&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;decoded&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;jwt&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;verify&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;token&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;secret&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;algorithms&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;HS256&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;decoded&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;exp&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="nb"&gt;Date&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;now&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="mi"&gt;1000&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Token expired&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;decoded&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;iss&lt;/span&gt; &lt;span class="o"&gt;!==&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;https://yourapp.com&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Invalid issuer&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;decoded&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;aud&lt;/span&gt; &lt;span class="o"&gt;!==&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;your-api&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Invalid audience&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Always validate &lt;code&gt;exp&lt;/code&gt;, &lt;code&gt;iss&lt;/code&gt; (issuer), and &lt;code&gt;aud&lt;/code&gt; (audience) explicitly — especially if tokens from multiple issuers could reach your API.&lt;/p&gt;




&lt;h2&gt;
  
  
  Mistake 7: No token revocation strategy
&lt;/h2&gt;

&lt;p&gt;JWTs are stateless by design — once issued, they're valid until expiry. But what happens when:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A user logs out&lt;/li&gt;
&lt;li&gt;An account is compromised&lt;/li&gt;
&lt;li&gt;A user's role changes mid-session
If you're relying purely on token expiry, a stolen 15-minute token is valid for up to 15 minutes after the breach is detected.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Fix:&lt;/strong&gt; Maintain a server-side revocation list (denylist) for sensitive operations.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// On logout&lt;/span&gt;
&lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;redis&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;set&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`revoked:&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;jti&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;1&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;EX&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;900&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// expire after token TTL&lt;/span&gt;

&lt;span class="c1"&gt;// On verify&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;isRevoked&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;redis&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`revoked:&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;decoded&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;jti&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;isRevoked&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Token revoked&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Add a &lt;code&gt;jti&lt;/code&gt; (JWT ID) claim — a unique identifier per token — so you can target specific tokens for revocation without rotating your secret.&lt;/p&gt;




&lt;h2&gt;
  
  
  The JWT security checklist
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Explicitly allowlist algorithms — never allow &lt;code&gt;none&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Use strong, randomly generated secrets (64+ bytes)&lt;/li&gt;
&lt;li&gt;Prefer &lt;code&gt;RS256&lt;/code&gt; for multi-service architectures&lt;/li&gt;
&lt;li&gt;Store tokens in &lt;code&gt;HttpOnly&lt;/code&gt; cookies, not &lt;code&gt;localStorage&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Set short expiry on access tokens (15 minutes)&lt;/li&gt;
&lt;li&gt;Put only non-sensitive identifiers in the payload&lt;/li&gt;
&lt;li&gt;Validate &lt;code&gt;exp&lt;/code&gt;, &lt;code&gt;iss&lt;/code&gt;, and &lt;code&gt;aud&lt;/code&gt; claims explicitly&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  - Implement token revocation with a &lt;code&gt;jti&lt;/code&gt; denylist
&lt;/h2&gt;

&lt;p&gt;JWTs are a solid auth primitive when implemented correctly. The problems almost always come from default configurations and library trust — not the spec itself.&lt;/p&gt;

&lt;p&gt;What JWT issue have you encountered in production? Drop it in the comments.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Free tools used in this post:&lt;/em&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://jsonindenter.com/jwt-decoder?utm_source=devto&amp;amp;utm_medium=article&amp;amp;utm_campaign=jwt-security" rel="noopener noreferrer"&gt;JWT Decoder&lt;/a&gt; — inspect any token without a secret&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://jsonindenter.com/jwt-encoder?utm_source=devto&amp;amp;utm_medium=article&amp;amp;utm_campaign=jwt-security" rel="noopener noreferrer"&gt;JWT Encoder&lt;/a&gt; — generate and sign JWTs for testing&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://jsonindenter.com/all-json-tools?utm_source=devto&amp;amp;utm_medium=article&amp;amp;utm_campaign=jwt-security" rel="noopener noreferrer"&gt;All JSON &amp;amp; JWT tools&lt;/a&gt; — everything runs client-side, nothing leaves your browser&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>security</category>
      <category>json</category>
      <category>webdev</category>
      <category>jwt</category>
    </item>
    <item>
      <title>5 JSON tricks most developers don't use</title>
      <dc:creator>Tahmid</dc:creator>
      <pubDate>Sat, 18 Apr 2026 18:01:11 +0000</pubDate>
      <link>https://dev.to/pioneer10/5-json-tricks-most-developers-dont-use-4jbi</link>
      <guid>https://dev.to/pioneer10/5-json-tricks-most-developers-dont-use-4jbi</guid>
      <description>&lt;p&gt;JSON is everywhere, but most developers only use &lt;code&gt;JSON.parse()&lt;/code&gt; and &lt;code&gt;JSON.stringify()&lt;/code&gt;. Here are a few tricks worth knowing.&lt;/p&gt;

&lt;h2&gt;
  
  
  1. Pretty-print in one line
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;stringify&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The third argument is your indent size. Instant readability.&lt;/p&gt;

&lt;h2&gt;
  
  
  2. Filter keys on stringify
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;stringify&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;user&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;name&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;email&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Pass an array of keys to only include those fields. Great for logging without exposing sensitive data.&lt;/p&gt;

&lt;h2&gt;
  
  
  3. JSONPath for deep queries
&lt;/h2&gt;

&lt;p&gt;Instead of chaining &lt;code&gt;.map()&lt;/code&gt; and &lt;code&gt;.filter()&lt;/code&gt;, JSONPath lets you query nested JSON like a database:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="err"&gt;$.users&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="err"&gt;?(@.age&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;30&lt;/span&gt;&lt;span class="err"&gt;)&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="err"&gt;.name&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  4. Auto-repair broken JSON
&lt;/h2&gt;

&lt;p&gt;LLMs often return slightly malformed JSON — trailing commas, single quotes, missing brackets. Instead of debugging it manually, a repair function can fix most common issues automatically.&lt;/p&gt;

&lt;h2&gt;
  
  
  5. Reduce LLM token usage with compact formats
&lt;/h2&gt;

&lt;p&gt;If you're passing large JSON payloads as context to GPT or Claude, consider stripping whitespace at minimum. Better yet, look into TOON (Token-Oriented Object Notation) — a compact alternative that cuts token count by 30–60% without losing structure.&lt;/p&gt;




&lt;p&gt;I built &lt;strong&gt;&lt;a href="https://jsonindenter.com/?utm_source=devto&amp;amp;utm_medium=article&amp;amp;utm_campaign=json-tricks" rel="noopener noreferrer"&gt;jsonindenter.com&lt;/a&gt;&lt;/strong&gt; as a free client-side toolkit that covers all of the above — JSONPath tester, JSON repair, TOON converter, Zod/Pydantic generators, JWT tools, and more. No sign-up, no ads, your data never leaves your browser.&lt;/p&gt;

&lt;p&gt;What JSON tricks do you swear by? Drop them in the comments 👇&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>javascript</category>
      <category>json</category>
      <category>showdev</category>
    </item>
  </channel>
</rss>
