<?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: Guo</title>
    <description>The latest articles on DEV Community by Guo (@toolexo).</description>
    <link>https://dev.to/toolexo</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.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F4122095%2F931d11ef-36af-4f9a-a2e6-8ee82c832439.png</url>
      <title>DEV Community: Guo</title>
      <link>https://dev.to/toolexo</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/toolexo"/>
    <language>en</language>
    <item>
      <title>URL Encoding Explained: %20, +, Query Strings, and Common API Bugs</title>
      <dc:creator>Guo</dc:creator>
      <pubDate>Wed, 30 Sep 2026 16:36:26 +0000</pubDate>
      <link>https://dev.to/toolexo/url-encoding-explained-20-query-strings-and-common-api-bugs-47h3</link>
      <guid>https://dev.to/toolexo/url-encoding-explained-20-query-strings-and-common-api-bugs-47h3</guid>
      <description>&lt;p&gt;A URL is not one string with one encoding rule.&lt;/p&gt;

&lt;p&gt;It has a scheme, host, path, query string, fragment, and sometimes user-controlled values inside those parts. Bugs appear when code treats all of them as interchangeable text.&lt;/p&gt;

&lt;p&gt;The familiar examples are spaces encoded as &lt;code&gt;%20&lt;/code&gt; or &lt;code&gt;+&lt;/code&gt;, a literal plus sign that turns into a space, and an API request whose query parameters break when a user enters &lt;code&gt;&amp;amp;&lt;/code&gt; or &lt;code&gt;#&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;The fix starts with a simple question: what exactly are you encoding?&lt;/p&gt;

&lt;h2&gt;
  
  
  Percent-encoding protects structure
&lt;/h2&gt;

&lt;p&gt;Some characters have structural meaning in a URL:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;? starts a query string
&amp;amp; separates query parameters
= separates a parameter name from its value
# starts a fragment
/ separates path segments
% begins a percent-encoded byte
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If one of those characters belongs to user-provided data, it must be encoded before it is placed into the relevant URL component.&lt;/p&gt;

&lt;p&gt;For example, this search term is data:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;coffee &amp;amp; tea
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If it is inserted directly into a query string, the &lt;code&gt;&amp;amp;&lt;/code&gt; can be read as the start of another parameter:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;https://example.com/search?q=coffee &amp;amp; tea
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The intended value should instead be encoded:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;https://example.com/search?q=coffee%20%26%20tea
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Percent-encoding represents bytes with &lt;code&gt;%&lt;/code&gt; followed by hexadecimal digits. A space can become &lt;code&gt;%20&lt;/code&gt;, &lt;code&gt;&amp;amp;&lt;/code&gt; becomes &lt;code&gt;%26&lt;/code&gt;, and a literal plus sign becomes &lt;code&gt;%2B&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;MDN has a useful overview of &lt;a href="https://developer.mozilla.org/en-US/docs/Glossary/Percent-encoding" rel="noopener noreferrer"&gt;percent-encoding in URLs&lt;/a&gt;, including why the same character may be encoded differently in different contexts.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why &lt;code&gt;%20&lt;/code&gt; and &lt;code&gt;+&lt;/code&gt; both mean space sometimes
&lt;/h2&gt;

&lt;p&gt;This is where many API bugs begin.&lt;/p&gt;

&lt;p&gt;For an ordinary URL component, a space is commonly represented as &lt;code&gt;%20&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="nf"&gt;encodeURIComponent&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;summer sale&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="c1"&gt;// "summer%20sale"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;HTML form encoding uses a related but different convention. In &lt;code&gt;application/x-www-form-urlencoded&lt;/code&gt; data, a space is serialized as &lt;code&gt;+&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="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;params&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;URLSearchParams&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;campaign&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;summer sale&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;params&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;toString&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="c1"&gt;// "campaign=summer+sale"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Both strings can represent the same value in the right context.&lt;/p&gt;

&lt;p&gt;The important distinction is that a literal plus sign is not a space. If the original value is &lt;code&gt;C++ guide&lt;/code&gt;, a form-style query must encode the plus signs as &lt;code&gt;%2B&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="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;params&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;URLSearchParams&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;q&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;C++ guide&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;params&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;toString&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="c1"&gt;// "q=C%2B%2B+guide"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;When that query string is parsed, &lt;code&gt;+&lt;/code&gt; becomes a space and &lt;code&gt;%2B&lt;/code&gt; becomes a literal plus sign.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;URLSearchParams&lt;/code&gt; follows the &lt;code&gt;application/x-www-form-urlencoded&lt;/code&gt; rules. MDN documents that its string parser decodes &lt;code&gt;+&lt;/code&gt; as a space, and its serializer writes spaces as &lt;code&gt;+&lt;/code&gt;. &lt;a href="https://developer.mozilla.org/en-US/docs/Web/API/URLSearchParams" rel="noopener noreferrer"&gt;Read the URLSearchParams reference&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;code&gt;encodeURI&lt;/code&gt; and &lt;code&gt;encodeURIComponent&lt;/code&gt; solve different problems
&lt;/h2&gt;

&lt;p&gt;JavaScript provides two similarly named functions. They should not be swapped casually.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;encodeURI()&lt;/code&gt; assumes that the input is already a complete URI. It preserves URL punctuation such as &lt;code&gt;:&lt;/code&gt;, &lt;code&gt;/&lt;/code&gt;, &lt;code&gt;?&lt;/code&gt;, &lt;code&gt;&amp;amp;&lt;/code&gt;, &lt;code&gt;=&lt;/code&gt;, and &lt;code&gt;#&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;encodeURIComponent()&lt;/code&gt; is for one component or value. It encodes a wider set of characters, including &lt;code&gt;&amp;amp;&lt;/code&gt;, &lt;code&gt;=&lt;/code&gt;, and &lt;code&gt;#&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;This is unsafe when &lt;code&gt;value&lt;/code&gt; comes from a user:&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;value&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;coffee &amp;amp; tea&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;url&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;`https://example.com/search?q=&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nf"&gt;encodeURI&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;value&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;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;url&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="c1"&gt;// https://example.com/search?q=coffee%20&amp;amp;%20tea&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;&amp;amp;&lt;/code&gt; remains structural. The server may interpret part of the value as another query parameter.&lt;/p&gt;

&lt;p&gt;For an individual value, use &lt;code&gt;encodeURIComponent()&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="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;value&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;coffee &amp;amp; tea&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;url&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;`https://example.com/search?q=&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nf"&gt;encodeURIComponent&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;value&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;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;url&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="c1"&gt;// https://example.com/search?q=coffee%20%26%20tea&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;For a full URL with query parameters, the &lt;code&gt;URL&lt;/code&gt; and &lt;code&gt;URLSearchParams&lt;/code&gt; APIs are usually clearer:&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;url&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;URL&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;https://example.com/search&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="nx"&gt;url&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;searchParams&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="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;q&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;coffee &amp;amp; tea&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="nx"&gt;url&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;searchParams&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="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;source&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;docs&lt;/span&gt;&lt;span class="dl"&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;url&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;toString&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;
&lt;span class="c1"&gt;// https://example.com/search?q=coffee+%26+tea&amp;amp;source=docs&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The serialized space appears as &lt;code&gt;+&lt;/code&gt; because &lt;code&gt;searchParams&lt;/code&gt; uses form-style query encoding. The value remains correct.&lt;/p&gt;

&lt;h2&gt;
  
  
  Encode values, not an entire URL by accident
&lt;/h2&gt;

&lt;p&gt;A common mistake is passing a complete URL through &lt;code&gt;encodeURIComponent()&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="nf"&gt;encodeURIComponent&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;https://example.com/search?q=coffee&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="c1"&gt;// "https%3A%2F%2Fexample.com%2Fsearch%3Fq%3Dcoffee"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That output is useful only when the complete URL itself must become one value, such as a &lt;code&gt;redirect&lt;/code&gt; parameter:&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;redirect&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;https://example.com/account?tab=billing&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;url&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;`https://auth.example.com/login?redirect=&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nf"&gt;encodeURIComponent&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;redirect&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;It is not a replacement for constructing a normal URL.&lt;/p&gt;

&lt;p&gt;The reverse mistake is decoding a full URL or query string blindly. A decoded &lt;code&gt;&amp;amp;&lt;/code&gt;, &lt;code&gt;=&lt;/code&gt;, or &lt;code&gt;#&lt;/code&gt; may regain structural meaning in the wrong layer. Decode the component you expect, then validate what it means.&lt;/p&gt;

&lt;h2&gt;
  
  
  Avoid double encoding
&lt;/h2&gt;

&lt;p&gt;Double encoding happens when already encoded text is treated as raw text and encoded again.&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="nf"&gt;encodeURIComponent&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;hello%20world&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="c1"&gt;// "hello%2520world"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;%&lt;/code&gt; becomes &lt;code&gt;%25&lt;/code&gt;, so &lt;code&gt;%20&lt;/code&gt; becomes &lt;code&gt;%2520&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;This is sometimes intentional. It is often a sign that one layer of the application does not know whether it receives raw or encoded data.&lt;/p&gt;

&lt;p&gt;A practical rule helps:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Store and pass decoded values inside your application.&lt;/li&gt;
&lt;li&gt;Encode only when constructing the URL or request.&lt;/li&gt;
&lt;li&gt;Decode only when parsing a URL or request.&lt;/li&gt;
&lt;li&gt;Document any boundary that intentionally expects encoded text.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The same rule applies to &lt;code&gt;URLSearchParams&lt;/code&gt;. Give it raw keys and values:&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;params&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;URLSearchParams&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="nx"&gt;params&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="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;q&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;coffee &amp;amp; tea&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;Do not pre-encode the value before calling &lt;code&gt;set()&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  URL encoding is not validation or security
&lt;/h2&gt;

&lt;p&gt;Encoding preserves URL structure. It does not prove that a URL is allowed, trustworthy, or safe to request.&lt;/p&gt;

&lt;p&gt;For example, percent-encoding does not:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;allowlist redirect destinations;&lt;/li&gt;
&lt;li&gt;prevent server-side request forgery;&lt;/li&gt;
&lt;li&gt;validate an API token;&lt;/li&gt;
&lt;li&gt;escape HTML for display in a page;&lt;/li&gt;
&lt;li&gt;make a query parameter safe for SQL;&lt;/li&gt;
&lt;li&gt;confirm that a URL uses HTTPS.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Those are separate checks with separate rules.&lt;/p&gt;

&lt;p&gt;If your application accepts a user-supplied URL, parse it with &lt;code&gt;new URL()&lt;/code&gt;, validate the protocol and host against the rules for that feature, and avoid using string replacement as a parser.&lt;/p&gt;

&lt;h2&gt;
  
  
  Test the cases that tend to break
&lt;/h2&gt;

&lt;p&gt;When testing URL handling, do not stop at a simple word.&lt;/p&gt;

&lt;p&gt;Use values such as:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;summer sale
C++ guide
coffee &amp;amp; tea
a=b
100% ready
https://example.com/a?x=1
你好
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;These cases reveal different failures:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;spaces test &lt;code&gt;%20&lt;/code&gt; versus &lt;code&gt;+&lt;/code&gt;;&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;+&lt;/code&gt; tests whether a literal plus survives form parsing;&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;&amp;amp;&lt;/code&gt; and &lt;code&gt;=&lt;/code&gt; test query-string structure;&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;%&lt;/code&gt; tests malformed or double encoding;&lt;/li&gt;
&lt;li&gt;complete URLs test component boundaries;&lt;/li&gt;
&lt;li&gt;non-ASCII text tests UTF-8 encoding.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I built the &lt;a href="https://toolexo.net/url-encoder/" rel="noopener noreferrer"&gt;ToolExo URL Encoder and Decoder&lt;/a&gt; for this exact distinction. It keeps URL component encoding and form query-value encoding separate, shows &lt;code&gt;%20&lt;/code&gt; versus &lt;code&gt;+&lt;/code&gt;, encodes a literal plus as &lt;code&gt;%2B&lt;/code&gt; in form mode, and decodes only one layer at a time.&lt;/p&gt;

&lt;p&gt;The tool is for a single component or query value. It deliberately does not rewrite a complete URL, host name, or protocol. Those parts have their own structure and should be parsed as URLs, not treated as text.&lt;/p&gt;

&lt;p&gt;A URL bug often looks small in a log. It can be one missing &lt;code&gt;%26&lt;/code&gt;, one accidental &lt;code&gt;+&lt;/code&gt;, or one value encoded twice. Keeping the URL structure separate from its data values makes those bugs much easier to prevent.&lt;/p&gt;

</description>
      <category>javascript</category>
      <category>webdev</category>
      <category>api</category>
      <category>security</category>
    </item>
    <item>
      <title>Regex Testers Can Freeze the Browser: Safer Testing with Timeouts</title>
      <dc:creator>Guo</dc:creator>
      <pubDate>Mon, 21 Sep 2026 04:37:20 +0000</pubDate>
      <link>https://dev.to/toolexo/regex-testers-can-freeze-the-browser-safer-testing-with-timeouts-e0d</link>
      <guid>https://dev.to/toolexo/regex-testers-can-freeze-the-browser-safer-testing-with-timeouts-e0d</guid>
      <description>&lt;p&gt;A regex can look harmless in code review and still lock up a page when someone pastes the wrong input.&lt;/p&gt;

&lt;p&gt;Most of us learn regular expressions as a compact way to validate a field, find a log line, or clean up text. The trouble starts when a pattern gives the engine many ways to interpret the same characters. If the match finally fails near the end of a long string, a backtracking engine may retry a huge number of possibilities before it gives up.&lt;/p&gt;

&lt;p&gt;That is how a small regex turns into a CPU problem.&lt;/p&gt;

&lt;p&gt;OWASP calls this Regular Expression Denial of Service, or ReDoS. The risk is not limited to servers. A bad pattern can block a browser tab, slow a worker, or tie up an API process if untrusted input reaches it. &lt;a href="https://community.owasp.org/attacks/Regular_expression_Denial_of_Service_-_ReDoS" rel="noopener noreferrer"&gt;OWASP's ReDoS overview&lt;/a&gt; includes examples of patterns that grow dramatically slower on carefully chosen non-matching input.&lt;/p&gt;

&lt;h2&gt;
  
  
  The pattern that looks innocent
&lt;/h2&gt;

&lt;p&gt;Consider this regex:&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="o"&gt;/^&lt;/span&gt;&lt;span class="p"&gt;(&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="o"&gt;+&lt;/span&gt;&lt;span class="nx"&gt;$&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It accepts one or more &lt;code&gt;a&lt;/code&gt; characters. The nested &lt;code&gt;+&lt;/code&gt; quantifiers create the problem.&lt;/p&gt;

&lt;p&gt;For a normal input, it seems fine:&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="o"&gt;/^&lt;/span&gt;&lt;span class="p"&gt;(&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="o"&gt;+&lt;/span&gt;&lt;span class="nx"&gt;$&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;test&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;aaaaaa&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// true&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now try a near miss:&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;pattern&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sr"&gt;/^&lt;/span&gt;&lt;span class="se"&gt;(&lt;/span&gt;&lt;span class="sr"&gt;a+&lt;/span&gt;&lt;span class="se"&gt;)&lt;/span&gt;&lt;span class="sr"&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;input&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;a&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;repeat&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;30&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;!&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="nx"&gt;pattern&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;test&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;input&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The final &lt;code&gt;!&lt;/code&gt; prevents a match. Before the engine can return &lt;code&gt;false&lt;/code&gt;, it may try many ways to divide the preceding &lt;code&gt;a&lt;/code&gt; characters across the inner and outer repetitions.&lt;/p&gt;

&lt;p&gt;The issue is not that &lt;code&gt;+&lt;/code&gt; is always dangerous. The issue is ambiguity. If two repeated parts can consume the same characters, the engine has multiple routes to explore when the match fails.&lt;/p&gt;

&lt;p&gt;OWASP lists nested repetition and overlapping alternatives as common warning signs:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;(a+)+
(a*)*
(a|aa)+
([a-zA-Z]+)*$
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A warning is not a proof that a regex is exploitable. It is a signal to test the pattern with realistic and hostile inputs before it reaches production.&lt;/p&gt;

&lt;h2&gt;
  
  
  The browser can be part of the failure
&lt;/h2&gt;

&lt;p&gt;JavaScript regular expressions run synchronously when you call methods such as &lt;code&gt;test&lt;/code&gt;, &lt;code&gt;exec&lt;/code&gt;, &lt;code&gt;match&lt;/code&gt;, or &lt;code&gt;replace&lt;/code&gt;. If a costly match runs on the main thread, the UI cannot repaint or respond to input until that work finishes.&lt;/p&gt;

&lt;p&gt;A test tool should isolate this work where possible. A Web Worker can run the regex outside the page's main UI thread. If the test does not finish within a bounded time, the page can terminate that worker and show a useful error instead of waiting forever.&lt;/p&gt;

&lt;p&gt;That protects the testing page. It does not prove the regex is safe in your application.&lt;/p&gt;

&lt;p&gt;A Node.js process, a Java service, a Python worker, and a browser tab may use different engines, time limits, input limits, and resource controls. Test the pattern where it will actually run.&lt;/p&gt;

&lt;h2&gt;
  
  
  Rewrite the pattern around the rule you mean
&lt;/h2&gt;

&lt;p&gt;The best fix is usually to remove the ambiguity.&lt;/p&gt;

&lt;p&gt;Suppose you want to allow words separated by spaces.&lt;/p&gt;

&lt;p&gt;This pattern has nested repetition:&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="o"&gt;/^&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="err"&gt;\&lt;/span&gt;&lt;span class="nx"&gt;w&lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="err"&gt;\&lt;/span&gt;&lt;span class="nx"&gt;s&lt;/span&gt;&lt;span class="p"&gt;?)&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="nx"&gt;$&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A clearer version describes the structure directly:&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="o"&gt;/^&lt;/span&gt;&lt;span class="err"&gt;\&lt;/span&gt;&lt;span class="nx"&gt;w&lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="p"&gt;(?:&lt;/span&gt;&lt;span class="err"&gt;\&lt;/span&gt;&lt;span class="nx"&gt;s&lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="err"&gt;\&lt;/span&gt;&lt;span class="nx"&gt;w&lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="nx"&gt;$&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This says:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Match one word.&lt;/li&gt;
&lt;li&gt;Then match zero or more occurrences of one or more spaces followed by another word.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The second version also makes the intended rule easier to explain in a code review.&lt;/p&gt;

&lt;p&gt;Other practical safeguards help:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Put a maximum length on input before matching it.&lt;/li&gt;
&lt;li&gt;Prefer fixed, reviewed patterns over patterns assembled from user input.&lt;/li&gt;
&lt;li&gt;Test inputs that almost match but fail near the end.&lt;/li&gt;
&lt;li&gt;Avoid nested unbounded quantifiers when the repeated parts overlap.&lt;/li&gt;
&lt;li&gt;Apply server-side limits too. Client-side validation does not protect an API.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The JavaScript &lt;code&gt;RegExp&lt;/code&gt; API is flexible, especially when patterns are built dynamically with the &lt;code&gt;RegExp()&lt;/code&gt; constructor. That flexibility deserves care when either the pattern or the text comes from an untrusted source. &lt;a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/RegExp" rel="noopener noreferrer"&gt;MDN's RegExp reference&lt;/a&gt; documents the API and its constructor behavior.&lt;/p&gt;

&lt;h2&gt;
  
  
  Test the happy path and the near miss
&lt;/h2&gt;

&lt;p&gt;A regex test case should include more than an example that matches.&lt;/p&gt;

&lt;p&gt;For a username pattern, test:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;good_name
good-name
too short
a very long value
a value that fails at the final character
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;For a log parser, test long lines with one bad suffix. For a URL validator, test long repeated fragments and malformed delimiters. The inputs that fail late are often more informative than the inputs that pass immediately.&lt;/p&gt;

&lt;h2&gt;
  
  
  A timeout is a guardrail, not a certificate
&lt;/h2&gt;

&lt;p&gt;I built the &lt;a href="https://toolexo.net/regex-tester/" rel="noopener noreferrer"&gt;ToolExo Regex Tester&lt;/a&gt; to run JavaScript regex evaluations in a disposable Web Worker. The tool terminates work that exceeds 500 milliseconds and flags a few risky shapes, such as nested repetition and repeated alternatives with overlapping branches.&lt;/p&gt;

&lt;p&gt;The timeout keeps one experiment from trapping the tester. It does not certify a pattern as safe, and it does not transfer its protection to another runtime.&lt;/p&gt;

&lt;p&gt;Use the tool to inspect matches, capture groups, replacements, and difficult test cases. Then test the same pattern with your production input limits and runtime configuration.&lt;/p&gt;

&lt;h2&gt;
  
  
  Keep the rule simple enough to defend
&lt;/h2&gt;

&lt;p&gt;A regular expression is easier to maintain when you can explain what each repeated section consumes and why it cannot compete with another section for the same input.&lt;/p&gt;

&lt;p&gt;If that explanation becomes hard, split the job:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Use a simple regex to check broad syntax.&lt;/li&gt;
&lt;li&gt;Parse or validate the meaningful parts in ordinary code.&lt;/li&gt;
&lt;li&gt;Enforce length and business rules separately.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;This approach often produces clearer error messages as well. A user learns whether a value is too long, has an invalid character, or fails a business rule. They do not get a generic "invalid input" message from a pattern nobody wants to edit.&lt;/p&gt;

&lt;p&gt;Regex remains useful. It just needs the same care we give database queries, file parsers, and every other piece of code that processes untrusted input.&lt;/p&gt;

</description>
      <category>javascript</category>
      <category>security</category>
      <category>testing</category>
      <category>webdev</category>
    </item>
    <item>
      <title>How to format and validate JSON without uploading it anywhere</title>
      <dc:creator>Guo</dc:creator>
      <pubDate>Sat, 12 Sep 2026 12:06:27 +0000</pubDate>
      <link>https://dev.to/toolexo/how-to-format-and-validate-json-without-uploading-it-anywhere-37fm</link>
      <guid>https://dev.to/toolexo/how-to-format-and-validate-json-without-uploading-it-anywhere-37fm</guid>
      <description>&lt;p&gt;A JSON blob is often easiest to read after it has been formatted. That sounds harmless until the blob is an API response with customer data, a production configuration file, or a test fixture copied from somewhere you should not casually share.&lt;/p&gt;

&lt;p&gt;The usual workflow is simple: paste it into a formatter, make it readable, copy it back.&lt;/p&gt;

&lt;p&gt;The problem is that the formatter may be another service in the data path.&lt;/p&gt;

&lt;p&gt;For ordinary sample data, that may not matter. For production payloads, credentials, tokens, private URLs, or customer records, it should make you stop before pasting. A local formatter is useful because parsing and formatting can happen in the browser instead of requiring a request to a server. It is still not a license to paste secrets everywhere. Clipboard history, browser extensions, screenshots, and the system you later paste into remain part of the risk.&lt;/p&gt;

&lt;p&gt;This article covers a practical local JSON workflow, what browser validation actually proves, and where its limits begin.&lt;/p&gt;

&lt;h2&gt;
  
  
  Start with strict JSON, not JavaScript-like JSON
&lt;/h2&gt;

&lt;p&gt;JSON and JavaScript object literals look similar. They are not the same format.&lt;/p&gt;

&lt;p&gt;This is valid JavaScript:&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;settings&lt;/span&gt; &lt;span class="o"&gt;=&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="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;dark&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;retryCount&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;3&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;It is not valid JSON. JSON requires double-quoted property names and string values. It also does not allow trailing commas.&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;"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;"retryCount"&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="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;That distinction matters when you are preparing data for an API, a &lt;code&gt;package.json&lt;/code&gt; file, or another strict JSON consumer. The JSON specification defines a limited data model: objects, arrays, strings, numbers, booleans, and &lt;code&gt;null&lt;/code&gt;. It does not include comments, functions, &lt;code&gt;undefined&lt;/code&gt;, &lt;code&gt;NaN&lt;/code&gt;, &lt;code&gt;Infinity&lt;/code&gt;, or JavaScript shorthand syntax. &lt;a href="https://www.rfc-editor.org/rfc/rfc8259" rel="noopener noreferrer"&gt;RFC 8259&lt;/a&gt; is the useful reference when a format dispute turns into a production bug.&lt;/p&gt;

&lt;p&gt;Do not use &lt;code&gt;eval()&lt;/code&gt; to "fix" JSON-like text. It turns data handling into code execution. If the input is JSON5, YAML, or a JavaScript configuration module, identify that format and use a parser intended for it.&lt;/p&gt;

&lt;h2&gt;
  
  
  What formatting does, and what it does not do
&lt;/h2&gt;

&lt;p&gt;A formatter normally does two things:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Parse the text into a value.&lt;/li&gt;
&lt;li&gt;Serialize that value again with chosen indentation.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;In browser JavaScript, that often means &lt;code&gt;JSON.parse()&lt;/code&gt; followed by &lt;code&gt;JSON.stringify()&lt;/code&gt;. MDN documents both APIs and their edge cases, including serialization behavior for values that JSON cannot represent directly. &lt;a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/JSON/parse" rel="noopener noreferrer"&gt;JSON.parse()&lt;/a&gt; and &lt;a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/JSON/stringify" rel="noopener noreferrer"&gt;JSON.stringify()&lt;/a&gt; are worth reading if you regularly move data between JavaScript and APIs.&lt;/p&gt;

&lt;p&gt;Formatting makes structure visible:&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;"service"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="s2"&gt;"billing"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="nl"&gt;"enabled"&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;"limits"&lt;/span&gt;&lt;span class="p"&gt;:{&lt;/span&gt;&lt;span class="nl"&gt;"daily"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;250&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="nl"&gt;"monthly"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;5000&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;becomes:&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;"service"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"billing"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"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;"limits"&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;"daily"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;250&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"monthly"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;5000&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;That makes missing brackets, unexpected nesting, and confusing field names easier to spot.&lt;/p&gt;

&lt;p&gt;It does not prove that the data is correct for your application.&lt;/p&gt;

&lt;p&gt;A valid JSON document can still have a wrong field name, an expired identifier, a string where an API expects a number, or an unsafe permission setting. Syntax validation is the first check, not the last one.&lt;/p&gt;

&lt;h2&gt;
  
  
  A safer local workflow
&lt;/h2&gt;

&lt;p&gt;For non-sensitive JSON, I use this sequence:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Remove secrets before copying anything. That includes API keys, bearer tokens, passwords, signed URLs, session values, and customer data.&lt;/li&gt;
&lt;li&gt;Validate the original text before changing it.&lt;/li&gt;
&lt;li&gt;Format it only after it passes strict parsing.&lt;/li&gt;
&lt;li&gt;Compare important values against the source, especially IDs, URLs, amounts, flags, and ordered arrays.&lt;/li&gt;
&lt;li&gt;Test the final JSON with the actual destination system.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The first step is not optional just because a tool claims local processing. OWASP advises against placing sensitive data in browser storage, and browser-side handling has risks that go beyond network transmission. &lt;a href="https://owasp.org/www-project-web-security-testing-guide/latest/4-Web_Application_Security_Testing/11-Client-side_Testing/12-Testing_Browser_Storage" rel="noopener noreferrer"&gt;OWASP Web Storage guidance&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;For a browser-based workspace, I built &lt;a href="https://toolexo.net/json-formatter/" rel="noopener noreferrer"&gt;ToolExo JSON Formatter and Validator&lt;/a&gt;. It uses the browser's strict JSON parser to validate, format, minify, or optionally sort object keys. It reports invalid syntax instead of silently adding quotes, removing comments, or guessing what malformed input was meant to say.&lt;/p&gt;

&lt;p&gt;That last part is deliberate. Silent repair can hide the bug that generated the bad JSON in the first place.&lt;/p&gt;

&lt;h2&gt;
  
  
  Common errors worth recognizing
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Trailing commas
&lt;/h3&gt;

&lt;p&gt;This is one of the most common cases where JavaScript habits leak into JSON:&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;"environment"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"staging"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"debug"&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="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 comma after &lt;code&gt;false&lt;/code&gt; is invalid because no next property follows it.&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;"environment"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"staging"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"debug"&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Single quotes
&lt;/h3&gt;

&lt;p&gt;JSON strings use double quotes only.&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;'status':&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;'ready'&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;Correct JSON:&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;"status"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"ready"&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;h3&gt;
  
  
  Unquoted property names
&lt;/h3&gt;

&lt;p&gt;This is valid in a JavaScript object literal but invalid in JSON:&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="p"&gt;{&lt;/span&gt; &lt;span class="nl"&gt;timeout&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;3000&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Correct JSON:&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;"timeout"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;3000&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;h3&gt;
  
  
  Comments
&lt;/h3&gt;

&lt;p&gt;JSON does not support comments:&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;used&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;by&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;the&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;nightly&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;job&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"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="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 the destination expects JSON, move that explanation into documentation or a field with an agreed meaning. Do not assume a comment-tolerant editor means the receiving system will accept it.&lt;/p&gt;

&lt;h2&gt;
  
  
  Be careful when sorting keys
&lt;/h2&gt;

&lt;p&gt;Sorting object keys can make reviews and diffs easier. It should not change the meaning of a normal JSON object, but it changes the text.&lt;/p&gt;

&lt;p&gt;That matters if another system signs the original bytes, compares snapshots as text, or expects a defined canonicalization scheme. Array order is different. Arrays are ordered, so a formatter should not reorder their elements just because it can sort object keys.&lt;/p&gt;

&lt;p&gt;If you are about to replace a file used in production, keep the original, save the formatted version as a new file, and test the new file where it will actually run.&lt;/p&gt;

&lt;h2&gt;
  
  
  Large integers and duplicate keys need extra attention
&lt;/h2&gt;

&lt;p&gt;JavaScript numbers use IEEE 754 floating-point representation. A JSON integer may be syntactically valid but lose precision when parsed into a JavaScript &lt;code&gt;Number&lt;/code&gt;. For identifiers, account numbers, or exact counters, use a documented string representation if precision matters.&lt;/p&gt;

&lt;p&gt;Duplicate object keys are another interoperability trap:&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;"region"&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="nl"&gt;"region"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"eu-west-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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Different parsers can handle duplicate names differently. Many JavaScript workflows keep the later value, but relying on that behavior makes the data ambiguous. Reject or fix duplicate names at the source.&lt;/p&gt;

&lt;h2&gt;
  
  
  Formatting is a debugging aid, not a schema
&lt;/h2&gt;

&lt;p&gt;A JSON formatter answers a narrow question: "Can this text be parsed as JSON?"&lt;/p&gt;

&lt;p&gt;It cannot answer:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Does this payload match the API schema?&lt;/li&gt;
&lt;li&gt;Are all required fields present?&lt;/li&gt;
&lt;li&gt;Is this user allowed to submit the request?&lt;/li&gt;
&lt;li&gt;Does the number use the correct unit?&lt;/li&gt;
&lt;li&gt;Is the URL safe to fetch?&lt;/li&gt;
&lt;li&gt;Does the data make sense for the business rule?&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Use schema validation, server-side authorization, field validation, and integration tests for those questions.&lt;/p&gt;

&lt;p&gt;Formatting earns its place earlier in the workflow. It makes a compact response readable, exposes a syntax error before you chase the wrong problem, and helps you review the shape of data without sending it to a third-party formatter.&lt;/p&gt;

&lt;p&gt;That is enough to make it a useful tool. It just should not be confused with a security boundary or a full validation system.&lt;/p&gt;

</description>
      <category>javascript</category>
      <category>webdev</category>
      <category>security</category>
      <category>productivity</category>
    </item>
  </channel>
</rss>
