<?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: Tim Nguyen</title>
    <description>The latest articles on DEV Community by Tim Nguyen (@deko39).</description>
    <link>https://dev.to/deko39</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%2F1104417%2F201ab886-cbc6-4ee8-b671-562d7620fded.png</url>
      <title>DEV Community: Tim Nguyen</title>
      <link>https://dev.to/deko39</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/deko39"/>
    <language>en</language>
    <item>
      <title>Unicode, UTF-8, UTF-16, surrogate pairs, and why JavaScript `length` lies</title>
      <dc:creator>Tim Nguyen</dc:creator>
      <pubDate>Fri, 10 Jul 2026 06:25:27 +0000</pubDate>
      <link>https://dev.to/deko39/unicode-utf-8-utf-16-surrogate-pairs-and-why-javascript-length-lies-28l6</link>
      <guid>https://dev.to/deko39/unicode-utf-8-utf-16-surrogate-pairs-and-why-javascript-length-lies-28l6</guid>
      <description>&lt;h1&gt;
  
  
  Introduction
&lt;/h1&gt;

&lt;p&gt;If you've ever written this in 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="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="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;length&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// 2 🤔&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;or&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;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="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;length&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// 11 😱&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;you've probably wondered:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"How can one visible emoji have a length of 11?"&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;This article explains everything you need to know—from Unicode to UTF encodings, surrogate pairs, grapheme clusters, and the correct way to count characters in JavaScript.&lt;/p&gt;




&lt;h1&gt;
  
  
  Unicode: The Universal Character Set
&lt;/h1&gt;

&lt;p&gt;Unicode is &lt;strong&gt;not an encoding&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Unicode is simply a standard that assigns every character a unique number called a &lt;strong&gt;code point&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Examples:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Character&lt;/th&gt;
&lt;th&gt;Unicode Code Point&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;A&lt;/td&gt;
&lt;td&gt;U+0041&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;你&lt;/td&gt;
&lt;td&gt;U+4F60&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;😀&lt;/td&gt;
&lt;td&gt;U+1F600&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;🐣&lt;/td&gt;
&lt;td&gt;U+1F423&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Think of Unicode as a giant dictionary.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;A  -&amp;gt; U+0041
B  -&amp;gt; U+0042
你 -&amp;gt; U+4F60
🐣 -&amp;gt; U+1F423
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Notice Unicode only defines the number.&lt;/p&gt;

&lt;p&gt;It does &lt;strong&gt;not&lt;/strong&gt; define how those numbers are stored in memory.&lt;/p&gt;




&lt;h1&gt;
  
  
  UTF-8, UTF-16 and UTF-32
&lt;/h1&gt;

&lt;p&gt;UTF means &lt;strong&gt;Unicode Transformation Format&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Each UTF is simply a different way of encoding Unicode code points.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Character&lt;/th&gt;
&lt;th&gt;Unicode&lt;/th&gt;
&lt;th&gt;UTF-8&lt;/th&gt;
&lt;th&gt;UTF-16&lt;/th&gt;
&lt;th&gt;UTF-32&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;A&lt;/td&gt;
&lt;td&gt;U+0041&lt;/td&gt;
&lt;td&gt;1 byte&lt;/td&gt;
&lt;td&gt;2 bytes&lt;/td&gt;
&lt;td&gt;4 bytes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;你&lt;/td&gt;
&lt;td&gt;U+4F60&lt;/td&gt;
&lt;td&gt;3 bytes&lt;/td&gt;
&lt;td&gt;2 bytes&lt;/td&gt;
&lt;td&gt;4 bytes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;🐣&lt;/td&gt;
&lt;td&gt;U+1F423&lt;/td&gt;
&lt;td&gt;4 bytes&lt;/td&gt;
&lt;td&gt;4 bytes (2 code units)&lt;/td&gt;
&lt;td&gt;4 bytes&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h2&gt;
  
  
  UTF-8
&lt;/h2&gt;

&lt;p&gt;UTF-8 uses between &lt;strong&gt;1 and 4 bytes&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Advantages:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Most efficient for English text&lt;/li&gt;
&lt;li&gt;Backward compatible with ASCII&lt;/li&gt;
&lt;li&gt;Standard for the web&lt;/li&gt;
&lt;li&gt;Used by JSON, HTML, Linux, HTTP...&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Today, UTF-8 dominates the internet.&lt;/p&gt;




&lt;h2&gt;
  
  
  UTF-16
&lt;/h2&gt;

&lt;p&gt;UTF-16 stores data in &lt;strong&gt;16-bit code units&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Characters inside the Basic Multilingual Plane (BMP) use &lt;strong&gt;one code unit&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Characters outside the BMP use &lt;strong&gt;two code units&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;JavaScript strings are UTF-16.&lt;/p&gt;

&lt;p&gt;That's why:&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="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;ABC&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt;
&lt;span class="c1"&gt;// 3&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;and&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="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;length&lt;/span&gt;
&lt;span class="c1"&gt;// 1&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;work as expected.&lt;/p&gt;




&lt;h2&gt;
  
  
  UTF-32
&lt;/h2&gt;

&lt;p&gt;UTF-32 stores every Unicode code point using &lt;strong&gt;exactly 4 bytes&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Advantages:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Very simple indexing&lt;/li&gt;
&lt;li&gt;Every code point occupies one slot&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Disadvantages:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Huge memory usage&lt;/li&gt;
&lt;li&gt;4× larger than UTF-8 for English text&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Because of this, UTF-32 is rarely used for general-purpose applications.&lt;/p&gt;




&lt;h1&gt;
  
  
  Why Emoji Break &lt;code&gt;length&lt;/code&gt;
&lt;/h1&gt;

&lt;p&gt;Consider:&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="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;🐣&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



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

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;U+1F423
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This value is larger than &lt;code&gt;0xFFFF&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;A single UTF-16 code unit can only represent:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight nasm"&gt;&lt;code&gt;&lt;span class="err"&gt;0&lt;/span&gt;&lt;span class="nf"&gt;x0000&lt;/span&gt;
&lt;span class="nf"&gt;...&lt;/span&gt;
&lt;span class="err"&gt;0&lt;/span&gt;&lt;span class="nf"&gt;xFFFF&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;So UTF-16 needs &lt;strong&gt;two code units&lt;/strong&gt;.&lt;/p&gt;




&lt;h1&gt;
  
  
  Surrogate Pairs
&lt;/h1&gt;

&lt;p&gt;Unicode reserved two special ranges:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;High Surrogates

D800 - DBFF

Low Surrogates

DC00 - DFFF
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;An emoji becomes:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;🐣

↓

D83D DC23
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Therefore JavaScript stores:&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="err"&gt;D&lt;/span&gt;&lt;span class="mi"&gt;83&lt;/span&gt;&lt;span class="err"&gt;D&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="err"&gt;DC&lt;/span&gt;&lt;span class="mi"&gt;23&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;So:&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="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;length&lt;/span&gt;
&lt;span class="c1"&gt;// 2&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;because JavaScript counts UTF-16 code units.&lt;/p&gt;




&lt;h1&gt;
  
  
  Why &lt;code&gt;for...of&lt;/code&gt; Works Better
&lt;/h1&gt;

&lt;p&gt;Strings are iterable.&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="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;ch&lt;/span&gt; &lt;span class="k"&gt;of&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="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;ch&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;prints&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;🐣
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;not&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;D83D
DC23
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The iterator automatically detects surrogate pairs.&lt;/p&gt;

&lt;p&gt;Therefore&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="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;length&lt;/span&gt;
&lt;span class="c1"&gt;// 1&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;works.&lt;/p&gt;




&lt;h1&gt;
  
  
  But &lt;code&gt;for...of&lt;/code&gt; Still Isn't Correct
&lt;/h1&gt;

&lt;p&gt;Now look at this emoji:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;👨‍👩‍👧‍👦
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



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

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;👨‍👩‍👧‍👦
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



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

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;👨
ZWJ
👩
ZWJ
👧
ZWJ
👦
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;There are &lt;strong&gt;7 Unicode code points&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;So&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="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;length&lt;/span&gt;
&lt;span class="c1"&gt;// 7&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Still wrong.&lt;/p&gt;




&lt;h1&gt;
  
  
  What is ZWJ?
&lt;/h1&gt;

&lt;p&gt;ZWJ stands for &lt;strong&gt;Zero Width Joiner&lt;/strong&gt;.&lt;/p&gt;

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

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;U+200D
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It is an invisible character.&lt;/p&gt;

&lt;p&gt;Its job is:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Join the surrounding characters into one displayed glyph.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Example:&lt;/p&gt;

&lt;p&gt;Without ZWJ:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;👩 💻
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;With ZWJ:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;👩‍💻
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



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

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;👩
+
ZWJ
+
💻
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The rendering engine displays:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;👩‍💻
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;as a single emoji.&lt;/p&gt;




&lt;h1&gt;
  
  
  Grapheme Clusters
&lt;/h1&gt;

&lt;p&gt;Humans don't think in Unicode code points.&lt;/p&gt;

&lt;p&gt;We think in visible characters.&lt;/p&gt;

&lt;p&gt;Unicode calls those &lt;strong&gt;grapheme clusters&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Examples:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Text&lt;/th&gt;
&lt;th&gt;Code Points&lt;/th&gt;
&lt;th&gt;Visible Characters&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;A&lt;/td&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;你&lt;/td&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;🐣&lt;/td&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;🇻🇳&lt;/td&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;👍🏽&lt;/td&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;é&lt;/td&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;👨‍👩‍👧‍👦&lt;/td&gt;
&lt;td&gt;7&lt;/td&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;This is what users expect when they see:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Maximum 100 characters&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h1&gt;
  
  
  How Does JavaScript Know?
&lt;/h1&gt;

&lt;p&gt;Modern JavaScript provides&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;Intl&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Segmenter&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It implements the Unicode Text Segmentation algorithm (UAX #29).&lt;/p&gt;

&lt;p&gt;Instead of asking:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"How many UTF-16 code units?"&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;or&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"How many Unicode code points?"&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;it asks:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"Where are the grapheme boundaries?"&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Unicode maintains a database of character properties.&lt;/p&gt;

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

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;👨

Extended_Pictographic

ZWJ

Zero Width Joiner

🏽

Extend

🇻

Regional Indicator
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The algorithm walks through the string and decides whether each boundary should be broken.&lt;/p&gt;

&lt;p&gt;Very roughly:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Read next code point

↓

Look up Unicode properties

↓

Apply Unicode rules

↓

Break?

or

Continue current grapheme?
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;No machine learning.&lt;/p&gt;

&lt;p&gt;No AI.&lt;/p&gt;

&lt;p&gt;Just deterministic Unicode rules.&lt;/p&gt;




&lt;h1&gt;
  
  
  The Right Way to Count Characters
&lt;/h1&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;segmenter&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nx"&gt;Intl&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Segmenter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;undefined&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;granularity&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;grapheme&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;characterCount&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;str&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;count&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&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;_&lt;/span&gt; &lt;span class="k"&gt;of&lt;/span&gt; &lt;span class="nx"&gt;segmenter&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;segment&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;str&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nx"&gt;count&lt;/span&gt;&lt;span class="o"&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;count&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;Examples:&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;characterCount&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&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="c1"&gt;// 5&lt;/span&gt;

&lt;span class="nf"&gt;characterCount&lt;/span&gt;&lt;span class="p"&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="c1"&gt;// 2&lt;/span&gt;

&lt;span class="nf"&gt;characterCount&lt;/span&gt;&lt;span class="p"&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="c1"&gt;// 1&lt;/span&gt;

&lt;span class="nf"&gt;characterCount&lt;/span&gt;&lt;span class="p"&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="c1"&gt;// 1&lt;/span&gt;

&lt;span class="nf"&gt;characterCount&lt;/span&gt;&lt;span class="p"&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="c1"&gt;// 1&lt;/span&gt;

&lt;span class="nf"&gt;characterCount&lt;/span&gt;&lt;span class="p"&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="c1"&gt;// 1&lt;/span&gt;

&lt;span class="nf"&gt;characterCount&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;é&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="c1"&gt;// 1&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This works correctly across languages, emoji, combining marks, flags, and ZWJ sequences.&lt;/p&gt;




&lt;h1&gt;
  
  
  Comparison
&lt;/h1&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Method&lt;/th&gt;
&lt;th&gt;Counts&lt;/th&gt;
&lt;th&gt;Correct for Emoji&lt;/th&gt;
&lt;th&gt;Correct for Flags&lt;/th&gt;
&lt;th&gt;Correct for ZWJ&lt;/th&gt;
&lt;th&gt;Correct for Combining Marks&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;str.length&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;UTF-16 code units&lt;/td&gt;
&lt;td&gt;❌&lt;/td&gt;
&lt;td&gt;❌&lt;/td&gt;
&lt;td&gt;❌&lt;/td&gt;
&lt;td&gt;❌&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;code&gt;for...of&lt;/code&gt; / &lt;code&gt;Array.from()&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;Unicode code points&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;❌&lt;/td&gt;
&lt;td&gt;❌&lt;/td&gt;
&lt;td&gt;❌&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;Intl.Segmenter&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Grapheme clusters&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;




&lt;h1&gt;
  
  
  Key Takeaways
&lt;/h1&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Unicode&lt;/strong&gt; defines characters.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;UTF-8&lt;/strong&gt;, &lt;strong&gt;UTF-16&lt;/strong&gt;, and &lt;strong&gt;UTF-32&lt;/strong&gt; are different encodings of Unicode.&lt;/li&gt;
&lt;li&gt;JavaScript strings are UTF-16.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;String.length&lt;/code&gt; counts UTF-16 code units, &lt;strong&gt;not characters&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;for...of&lt;/code&gt; iterates Unicode code points, which is better but still not enough.&lt;/li&gt;
&lt;li&gt;User-visible characters are called &lt;strong&gt;grapheme clusters&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;Intl.Segmenter&lt;/code&gt; is the correct solution for counting characters in modern JavaScript.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If you're building user-facing features like character limits, text editors, or social apps, &lt;code&gt;Intl.Segmenter&lt;/code&gt; is the API you should be using.&lt;/p&gt;

</description>
      <category>javascript</category>
      <category>node</category>
      <category>webdev</category>
      <category>unicode</category>
    </item>
    <item>
      <title>You copied curl, using that to request to cloudfront through WAF and got strange error that request couldn't be satisfied? Here is what to concern...</title>
      <dc:creator>Tim Nguyen</dc:creator>
      <pubDate>Tue, 12 May 2026 05:19:22 +0000</pubDate>
      <link>https://dev.to/deko39/you-copied-curl-using-that-to-request-to-cloudfront-through-waf-and-got-strange-error-that-request-3h5l</link>
      <guid>https://dev.to/deko39/you-copied-curl-using-that-to-request-to-cloudfront-through-waf-and-got-strange-error-that-request-3h5l</guid>
      <description>&lt;p&gt;Here is the day as usual, you got the bug report from FE team or QA about request can not be processed from BE side. As a BE engineer, you requested a &lt;code&gt;curl&lt;/code&gt; as proper way to reproduce the bug. You pasted that &lt;code&gt;curl&lt;/code&gt; to postman and started requesting, but got this strange error:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="cp"&gt;&amp;lt;!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;HTML&amp;gt;&amp;lt;HEAD&amp;gt;&amp;lt;META&lt;/span&gt; &lt;span class="na"&gt;HTTP-EQUIV=&lt;/span&gt;&lt;span class="s"&gt;"Content-Type"&lt;/span&gt; &lt;span class="na"&gt;CONTENT=&lt;/span&gt;&lt;span class="s"&gt;"text/html; charset=iso-8859-1"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;TITLE&amp;gt;&lt;/span&gt;ERROR: The request could not be satisfied&lt;span class="nt"&gt;&amp;lt;/TITLE&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/HEAD&amp;gt;&amp;lt;BODY&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;H1&amp;gt;&lt;/span&gt;400 ERROR&lt;span class="nt"&gt;&amp;lt;/H1&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;H2&amp;gt;&lt;/span&gt;The request could not be satisfied.&lt;span class="nt"&gt;&amp;lt;/H2&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;HR&lt;/span&gt; &lt;span class="na"&gt;noshade&lt;/span&gt; &lt;span class="na"&gt;size=&lt;/span&gt;&lt;span class="s"&gt;"1px"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
Bad request.
We can't connect to the server for this app or website at this time. There might be too much traffic or a configuration error. Try again later, or contact the app or website owner.
&lt;span class="nt"&gt;&amp;lt;BR&lt;/span&gt; &lt;span class="na"&gt;clear=&lt;/span&gt;&lt;span class="s"&gt;"all"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
If you provide content to customers through CloudFront, you can find steps to troubleshoot and help prevent this error by reviewing the CloudFront documentation.
&lt;span class="nt"&gt;&amp;lt;BR&lt;/span&gt; &lt;span class="na"&gt;clear=&lt;/span&gt;&lt;span class="s"&gt;"all"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;HR&lt;/span&gt; &lt;span class="na"&gt;noshade&lt;/span&gt; &lt;span class="na"&gt;size=&lt;/span&gt;&lt;span class="s"&gt;"1px"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;PRE&amp;gt;&lt;/span&gt;
Generated by cloudfront (CloudFront)
Request ID: 6ErPcNvTBbZ2q174iyLonbqphQBNHsRCRiOupa3eVZw3y9ZRB6v0PA==
&lt;span class="nt"&gt;&amp;lt;/PRE&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;ADDRESS&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/ADDRESS&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/BODY&amp;gt;&amp;lt;/HTML&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;As a senior BE engineer, you started triaging this from the request ID and found out: &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Backend still operates as usual&lt;/li&gt;
&lt;li&gt;No backend got not log for that request ID&lt;/li&gt;
&lt;li&gt;Cloudfront got no log for that request ID&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;What happened?&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;First of all, request ID is generated from edge location and might not yet come to WAF or cloudfront log, so the request ID is not trustable in this case&lt;/li&gt;
&lt;li&gt;Second, the request got blocked from WAF, as in the strange response said&lt;/li&gt;
&lt;li&gt;Last resource, taking a look to curl itself, there is a header that silently drop the request at edge location and no proper error return, there is 'content-length'&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Content-Length&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Content length is the header that be calculated on the client side to hint the server about the size of request payload&lt;/li&gt;
&lt;li&gt;Content length can be inspected by WAF or Cloudfront to mitigate the HTTP Request Smuggling (malform HTTP request to make server resources like CPU/RAM overload)&lt;/li&gt;
&lt;li&gt;CloudFront, ALB, nginx, HAProxy, Envoy, etc. are very strict about malformed framing headers specifically because of this attack class.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;So, next time, better clean the &lt;code&gt;curl&lt;/code&gt; request first by dropping some false positive headers, in this case: &lt;code&gt;Content-Length&lt;/code&gt;&lt;/p&gt;

</description>
      <category>aws</category>
      <category>backend</category>
      <category>networking</category>
      <category>security</category>
    </item>
    <item>
      <title>COUNT(column) vs COUNT(*) in SQL — With INNER, LEFT, RIGHT &amp; FULL JOIN Explained</title>
      <dc:creator>Tim Nguyen</dc:creator>
      <pubDate>Tue, 24 Feb 2026 08:57:09 +0000</pubDate>
      <link>https://dev.to/deko39/countcolumn-vs-count-in-sql-with-inner-left-right-full-join-explained-1a1f</link>
      <guid>https://dev.to/deko39/countcolumn-vs-count-in-sql-with-inner-left-right-full-join-explained-1a1f</guid>
      <description>&lt;h1&gt;
  
  
  Counting seems simple in SQL… until joins enter the picture.
&lt;/h1&gt;

&lt;p&gt;One of the most common bugs in reporting queries comes from misunderstanding:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;COUNT(*) vs COUNT(column)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  1. The Core Rule
&lt;/h3&gt;

&lt;p&gt;&lt;code&gt;✅ COUNT(*) -&amp;gt; Counts rows&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;&lt;code&gt;✅ COUNT(column) -&amp;gt; Counts non-NULL values in that column&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;That’s it.&lt;br&gt;
But joins create NULLs — and that’s where things change.&lt;/p&gt;
&lt;h3&gt;
  
  
  2. Example setup
&lt;/h3&gt;

&lt;p&gt;Customer&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;id&lt;/th&gt;
&lt;th&gt;name&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;Alice&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;td&gt;Bob&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;3&lt;/td&gt;
&lt;td&gt;Carol&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Orders&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;id&lt;/th&gt;
&lt;th&gt;customer_id&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;10&lt;/td&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;11&lt;/td&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;12&lt;/td&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;a. &lt;strong&gt;INNER JOIN&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;SELECT c.id, COUNT(*), COUNT(o.id)
FROM customers c
INNER JOIN orders o ON c.id = o.customer_id
GROUP BY c.id;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;id&lt;/th&gt;
&lt;th&gt;COUNT(*)&lt;/th&gt;
&lt;th&gt;COUNT(o.id)&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Why?&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;INNER JOIN&lt;/strong&gt; removes non-matching rows.&lt;br&gt;
No NULL rows are produced.&lt;/p&gt;

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

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;COUNT(*) == COUNT(o.id)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;No surprises here.&lt;/p&gt;

&lt;p&gt;b. &lt;strong&gt;LEFT JOIN (Where Most Bugs Happen)&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;SELECT c.id, COUNT(*), COUNT(o.id)
FROM customers c
LEFT JOIN orders o ON c.id = o.customer_id
GROUP BY c.id;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;id&lt;/th&gt;
&lt;th&gt;COUNT(*)&lt;/th&gt;
&lt;th&gt;COUNT(o.id)&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;3&lt;/td&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;0&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Why is Carol different?&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;LEFT JOIN&lt;/strong&gt; keeps all customers.&lt;br&gt;
For Carol: A row exists + o.id is NULL&lt;/p&gt;

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

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;COUNT(*) counts the row → 1
COUNT(o.id) ignores NULL → 0
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  3. Usage
&lt;/h3&gt;

&lt;p&gt;When you have to count multiple joined tables with different expressions, mind to use count + join properly and wisely&lt;/p&gt;

&lt;h3&gt;
  
  
  4. Final thoughts
&lt;/h3&gt;

&lt;p&gt;Most production reporting bugs involving SQL counts come from misunderstanding how NULL behaves in outer joins.&lt;/p&gt;

&lt;p&gt;Once you internalize:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;COUNT(*) counts rows
COUNT(column) counts values
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You’ll stop shipping incorrect dashboards.&lt;/p&gt;

</description>
      <category>beginners</category>
      <category>database</category>
      <category>sql</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>PostgreSQL MVCC vs MySQL Key-Next Locking: How Transaction Isolation Affects Concurrency</title>
      <dc:creator>Tim Nguyen</dc:creator>
      <pubDate>Tue, 09 Dec 2025 03:14:16 +0000</pubDate>
      <link>https://dev.to/deko39/postgresql-mvcc-vs-mysql-key-next-locking-how-transaction-isolation-affects-concurrency-3a37</link>
      <guid>https://dev.to/deko39/postgresql-mvcc-vs-mysql-key-next-locking-how-transaction-isolation-affects-concurrency-3a37</guid>
      <description>&lt;p&gt;When developers talk about ACID databases, two giants dominate: &lt;strong&gt;PostgreSQL&lt;/strong&gt; and &lt;strong&gt;MySQL (InnoDB)&lt;/strong&gt;. Both claim full MVCC (Multi-Version Concurrency Control) support, but their implementations are fundamentally different.&lt;/p&gt;

&lt;p&gt;This post explains the key differences, how transaction isolation levels behave in each, and why PostgreSQL is truly non-blocking for readers while MySQL often isn't.&lt;/p&gt;

&lt;h2&gt;
  
  
  TL;DR Summary
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Feature&lt;/th&gt;
&lt;th&gt;PostgreSQL&lt;/th&gt;
&lt;th&gt;MySQL InnoDB&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;MVCC Implementation&lt;/td&gt;
&lt;td&gt;Versioned tuples (true MVCC)&lt;/td&gt;
&lt;td&gt;Undo logs + next-key locking (hybrid)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Readers block writers?&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;td&gt;Yes (in REPEATABLE READ)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Writers block readers?&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;td&gt;Yes (in REPEATABLE READ)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Default isolation level&lt;/td&gt;
&lt;td&gt;READ COMMITTED&lt;/td&gt;
&lt;td&gt;REPEATABLE READ&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Non-blocking READ COMMITTED&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;No (uses semi-consistent reads)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Phantom reads in default mode&lt;/td&gt;
&lt;td&gt;Possible (READ COMMITTED)&lt;/td&gt;
&lt;td&gt;Prevented (REPEATABLE READ + gap locks)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Lock escalation / gap locks&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;td&gt;Yes (notorious source of deadlocks)&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h1&gt;
  
  
  1. PostgreSQL's True MVCC: How It Achieves Non-Blocking Reads &amp;amp; Writes
&lt;/h1&gt;

&lt;p&gt;PostgreSQL is famous for its rock-solid ACID compliance and incredible concurrency. The secret sauce? &lt;strong&gt;True Multi-Version Concurrency Control (MVCC)&lt;/strong&gt; — an implementation that lets readers and writers fly past each other without ever blocking.&lt;/p&gt;

&lt;p&gt;Let’s dive deep into how it actually works under the hood.&lt;/p&gt;

&lt;h2&gt;
  
  
  What "True MVCC" Really Means
&lt;/h2&gt;

&lt;p&gt;Every time a row is &lt;code&gt;UPDATE&lt;/code&gt; or &lt;code&gt;DELETE&lt;/code&gt;, PostgreSQL &lt;strong&gt;does not overwrite&lt;/strong&gt; the old row. Instead:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A &lt;strong&gt;new version (tuple)&lt;/strong&gt; of the row is created.&lt;/li&gt;
&lt;li&gt;Old versions get metadata:

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;xmin&lt;/code&gt; → transaction ID that created this version&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;xmax&lt;/code&gt; → transaction ID that deleted/updated it (or still active)&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;Each transaction gets a &lt;strong&gt;snapshot&lt;/strong&gt; (via the transaction ID snapshot) that determines which row versions are visible to it.&lt;/li&gt;

&lt;li&gt;Dead tuples are cleaned up later by &lt;strong&gt;VACUUM&lt;/strong&gt;.&lt;/li&gt;

&lt;/ul&gt;

&lt;p&gt;Result: &lt;strong&gt;No read locks are ever needed&lt;/strong&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="c1"&gt;-- This SELECT will NEVER block, even if another transaction is updating the row right now&lt;/span&gt;
&lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;orders&lt;/span&gt; &lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="n"&gt;customer_id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;123&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Visibility Rules — The Heart of PostgreSQL MVCC
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Condition&lt;/th&gt;
&lt;th&gt;Visible to my transaction?&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Row's xmin committed and ≤ my snapshot&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Row's xmax committed and &amp;gt; my snapshot&lt;/td&gt;
&lt;td&gt;No (deleted/updated after I started)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Row's xmax is still active (in progress)&lt;/td&gt;
&lt;td&gt;No (not yet committed)&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h2&gt;
  
  
  How Isolation Levels Change Snapshot Behavior
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Level&lt;/th&gt;
&lt;th&gt;Snapshot Taken&lt;/th&gt;
&lt;th&gt;Non-Repeatable Reads?&lt;/th&gt;
&lt;th&gt;Phantom Reads?&lt;/th&gt;
&lt;th&gt;Blocking?&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;READ COMMITTED&lt;/td&gt;
&lt;td&gt;New snapshot per statement&lt;/td&gt;
&lt;td&gt;Possible&lt;/td&gt;
&lt;td&gt;Possible&lt;/td&gt;
&lt;td&gt;Never&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;REPEATABLE READ&lt;/td&gt;
&lt;td&gt;Snapshot at first statement of tx&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;td&gt;Possible&lt;/td&gt;
&lt;td&gt;Never&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;SERIALIZABLE&lt;/td&gt;
&lt;td&gt;Snapshot at tx start + SSI (predicate checks)&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;td&gt;Never (aborts instead of deadlocks)&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Yes — even in &lt;code&gt;SERIALIZABLE&lt;/code&gt;, PostgreSQL never blocks; it aborts one transaction if a conflict is detected.&lt;/p&gt;

&lt;h2&gt;
  
  
  Real Example: Zero Blocking
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="c1"&gt;-- Session 1 (long-running report)&lt;/span&gt;
&lt;span class="k"&gt;BEGIN&lt;/span&gt; &lt;span class="n"&gt;TRANSACTION&lt;/span&gt; &lt;span class="k"&gt;ISOLATION&lt;/span&gt; &lt;span class="k"&gt;LEVEL&lt;/span&gt; &lt;span class="k"&gt;REPEATABLE&lt;/span&gt; &lt;span class="k"&gt;READ&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="k"&gt;COUNT&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="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;users&lt;/span&gt; &lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="n"&gt;age&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;30&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;  &lt;span class="c1"&gt;-- runs for 20 seconds&lt;/span&gt;

&lt;span class="c1"&gt;-- Session 2 (concurrent write)&lt;/span&gt;
&lt;span class="k"&gt;UPDATE&lt;/span&gt; &lt;span class="n"&gt;users&lt;/span&gt; &lt;span class="k"&gt;SET&lt;/span&gt; &lt;span class="n"&gt;age&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;35&lt;/span&gt; &lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="n"&gt;id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;999&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;  &lt;span class="c1"&gt;-- finishes instantly!&lt;/span&gt;

&lt;span class="c1"&gt;-- Session 1 continues happily with its old snapshot&lt;/span&gt;
&lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="k"&gt;COUNT&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="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;users&lt;/span&gt; &lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="n"&gt;age&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;30&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;  &lt;span class="c1"&gt;-- still sees pre-update data&lt;/span&gt;
&lt;span class="k"&gt;COMMIT&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;No waiting. No deadlocks. Pure magic.&lt;/p&gt;

&lt;h2&gt;
  
  
  Benifits and Trade-offs
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Benefits: 

&lt;ul&gt;
&lt;li&gt;Readers never block writers&lt;/li&gt;
&lt;li&gt;Writers never block readers&lt;/li&gt;
&lt;li&gt;Almost zero deadlocks (only from explicit FOR UPDATE/LOCK TABLE)&lt;/li&gt;
&lt;li&gt;Great for analytics + OLTP mixed workloads&lt;/li&gt;
&lt;li&gt;VACUUM and autovacuum keep bloat under control&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;Trade-offs:

&lt;ul&gt;
&lt;li&gt;Table bloat if VACUUM lags (solved with good autovacuum tuning)&lt;/li&gt;
&lt;li&gt;Slightly higher write amplification (new tuple per update)&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;

&lt;h1&gt;
  
  
  2. MySQL InnoDB: MVCC + Next-Key Locking (The Hidden Truth)
&lt;/h1&gt;

&lt;h1&gt;
  
  
  MySQL InnoDB’s MVCC Is Not What You Think: The Truth About Next-Key Locks
&lt;/h1&gt;

&lt;p&gt;Everyone says "InnoDB has MVCC". That’s technically true — but it’s &lt;strong&gt;not&lt;/strong&gt; the same as PostgreSQL’s non-blocking MVCC.&lt;/p&gt;

&lt;p&gt;InnoDB combines MVCC with &lt;strong&gt;row locks&lt;/strong&gt; and &lt;strong&gt;gap/next-key locks&lt;/strong&gt;, and the default isolation level (&lt;code&gt;REPEATABLE READ&lt;/code&gt;) turns innocent &lt;code&gt;SELECT&lt;/code&gt;s into blocking operations.&lt;/p&gt;

&lt;p&gt;Let’s uncover what really happens.&lt;/p&gt;

&lt;h2&gt;
  
  
  InnoDB’s Hybrid Approach
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Old row versions are stored in &lt;strong&gt;undo logs&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;Each transaction gets a &lt;strong&gt;read view&lt;/strong&gt; (similar to a snapshot)&lt;/li&gt;
&lt;li&gt;But… to prevent phantom reads in &lt;code&gt;REPEATABLE READ&lt;/code&gt;, InnoDB uses &lt;strong&gt;next-key locking&lt;/strong&gt;:

&lt;ul&gt;
&lt;li&gt;Record lock (on existing row)&lt;/li&gt;
&lt;li&gt;Gap lock (on the "gap" between index entries)&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;

&lt;p&gt;This means: &lt;strong&gt;plain SELECTs can acquire locks and block INSERTs/UPDATEs&lt;/strong&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Infamous Gap Lock Example
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="c1"&gt;-- Session 1&lt;/span&gt;
&lt;span class="k"&gt;SET&lt;/span&gt; &lt;span class="n"&gt;TRANSACTION&lt;/span&gt; &lt;span class="k"&gt;ISOLATION&lt;/span&gt; &lt;span class="k"&gt;LEVEL&lt;/span&gt; &lt;span class="k"&gt;REPEATABLE&lt;/span&gt; &lt;span class="k"&gt;READ&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;users&lt;/span&gt; &lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="n"&gt;age&lt;/span&gt; &lt;span class="k"&gt;BETWEEN&lt;/span&gt; &lt;span class="mi"&gt;20&lt;/span&gt; &lt;span class="k"&gt;AND&lt;/span&gt; &lt;span class="mi"&gt;30&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;  &lt;span class="c1"&gt;-- scans index on age&lt;/span&gt;

&lt;span class="c1"&gt;-- This query places GAP LOCKS on age ranges (...,20], (20,25], (25,30], (30,...) &lt;/span&gt;

&lt;span class="c1"&gt;-- Session 2 (gets BLOCKED!)&lt;/span&gt;
&lt;span class="k"&gt;INSERT&lt;/span&gt; &lt;span class="k"&gt;INTO&lt;/span&gt; &lt;span class="n"&gt;users&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;age&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;VALUES&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'Alice'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;27&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="c1"&gt;-- Waits... forever... until Session 1 commits/rollbacks&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A harmless &lt;code&gt;SELECT&lt;/code&gt; just blocked an &lt;code&gt;INSERT&lt;/code&gt; on a non-existing row!&lt;/p&gt;

&lt;h2&gt;
  
  
  How Isolation Levels Really Behave in InnoDB
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Level&lt;/th&gt;
&lt;th&gt;Snapshot?&lt;/th&gt;
&lt;th&gt;Gap Locks?&lt;/th&gt;
&lt;th&gt;Readers Block Writers?&lt;/th&gt;
&lt;th&gt;Writers Block Readers?&lt;/th&gt;
&lt;th&gt;Deadlocks Common?&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;READ UNCOMMITTED&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;Rare&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;READ COMMITTED&lt;/td&gt;
&lt;td&gt;Partial&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;Less&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;REPEATABLE READ (default)&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;Very common&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;SERIALIZABLE&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;Yes + stricter&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;Extremely common&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Yes — in the default &lt;code&gt;REPEATABLE READ&lt;/code&gt;, reads block writes and writes block reads.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why InnoDB Chose This Design
&lt;/h2&gt;

&lt;p&gt;Goal: Prevent phantom reads without predicate locking (like PostgreSQL’s SSI).&lt;br&gt;
Solution: Lock the gaps in indexes → no phantoms, but at the cost of blocking.&lt;/p&gt;

&lt;h2&gt;
  
  
  Real-World Pain Points
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;A background analytics query can freeze your entire app&lt;/li&gt;
&lt;li&gt;Random deadlocks at high concurrency&lt;/li&gt;
&lt;li&gt;Hard to debug: &lt;code&gt;SHOW ENGINE INNODB STATUS&lt;/code&gt; shows giant lock wait chains&lt;/li&gt;
&lt;li&gt;Scaling writes often requires partitioning or read replicas&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  When InnoDB’s Approach Is Acceptable
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Low-to-medium concurrency&lt;/li&gt;
&lt;li&gt;Mostly primary key lookups (no range scans → fewer gap locks)&lt;/li&gt;
&lt;li&gt;You’re already deep in the MySQL ecosystem&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  How to Reduce Pain (if you’re stuck with MySQL)
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="c1"&gt;-- Use READ COMMITTED + row-based replication&lt;/span&gt;
&lt;span class="k"&gt;SET&lt;/span&gt; &lt;span class="k"&gt;SESSION&lt;/span&gt; &lt;span class="n"&gt;TRANSACTION&lt;/span&gt; &lt;span class="k"&gt;ISOLATION&lt;/span&gt; &lt;span class="k"&gt;LEVEL&lt;/span&gt; &lt;span class="k"&gt;READ&lt;/span&gt; &lt;span class="k"&gt;COMMITTED&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="c1"&gt;-- Avoid SELECT ... FOR UPDATE/SHARE unless necessary&lt;/span&gt;
&lt;span class="c1"&gt;-- Use primary key lookups instead of range scans when possible&lt;/span&gt;
&lt;span class="c1"&gt;-- Consider MariaDB (its default is often less aggressive)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h1&gt;
  
  
  3. Conclusion
&lt;/h1&gt;

&lt;p&gt;InnoDB’s "MVCC" is a hybrid of versioning + locking.&lt;br&gt;
It gives you strong consistency (no phantoms by default) but sacrifices true non-blocking concurrency.&lt;br&gt;
If you ever wondered why your MySQL app randomly deadlocks at scale even though "you’re just reading" — now you know: your reads are silently locking gaps.&lt;br&gt;
PostgreSQL proved decades ago that you can have both MVCC and no blocking. MySQL chose a different trade-off.&lt;br&gt;
Choose your database accordingly.&lt;/p&gt;

</description>
      <category>postgres</category>
      <category>mysql</category>
      <category>webdev</category>
      <category>programming</category>
    </item>
    <item>
      <title>Why Your `@Transform` Decorator Doesn’t Run in class-transformer (and How to Fix It)</title>
      <dc:creator>Tim Nguyen</dc:creator>
      <pubDate>Sun, 07 Sep 2025 11:57:59 +0000</pubDate>
      <link>https://dev.to/deko39/why-your-transform-decorator-doesnt-run-in-class-transformer-and-how-to-fix-it-3ka6</link>
      <guid>https://dev.to/deko39/why-your-transform-decorator-doesnt-run-in-class-transformer-and-how-to-fix-it-3ka6</guid>
      <description>&lt;p&gt;If you’ve ever used &lt;a href="https://github.com/typestack/class-transformer" rel="noopener noreferrer"&gt;&lt;code&gt;class-transformer&lt;/code&gt;&lt;/a&gt; with &lt;code&gt;@Transform&lt;/code&gt;, you might have run into this weird situation:&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="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;reflect-metadata&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;plainToInstance&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;Transform&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;class-transformer&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;IsEmail&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;IsNotEmpty&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;class-validator&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;MobileSignupBody&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;IsNotEmpty&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
  &lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;IsEmail&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
  &lt;span class="nx"&gt;email&lt;/span&gt;&lt;span class="o"&gt;!&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="nd"&gt;Transform&lt;/span&gt;&lt;span class="p"&gt;(({&lt;/span&gt; &lt;span class="nx"&gt;obj&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;obj&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;email&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="nx"&gt;normalized_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="p"&gt;}&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;dto&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;plainToInstance&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;MobileSignupBody&lt;/span&gt;&lt;span class="p"&gt;,&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="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;abc@test.com&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;dto&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// MobileSignupBody { email: 'abc@test.com' }&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;dto&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;normalized_email&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// ❌ undefined&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Wait… why is normalized_email undefined even though I clearly added a @Transform?&lt;/p&gt;

&lt;h1&gt;
  
  
  The Gotcha
&lt;/h1&gt;

&lt;p&gt;By default, class-transformer only runs @Transform on properties that exist in the plain object input.&lt;/p&gt;

&lt;p&gt;In this case, the plain object only had:&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;"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;"abc@test.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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Since there was no normalized_email key, class-transformer skipped that property completely.&lt;br&gt;
That’s why the transform never fired.&lt;/p&gt;
&lt;h1&gt;
  
  
  The Fix: Use &lt;code&gt;@Expose&lt;/code&gt;
&lt;/h1&gt;

&lt;p&gt;You need to tell class-transformer: “always include this property, even if it’s not in the plain object.”&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;Expose&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;Transform&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;class-transformer&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;MobileSignupBody&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;Expose&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="c1"&gt;// Will always include in class/plain object -&amp;gt; be transformed&lt;/span&gt;
  &lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;IsNotEmpty&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
  &lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;IsEmail&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
  &lt;span class="nx"&gt;email&lt;/span&gt;&lt;span class="o"&gt;!&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="nd"&gt;Expose&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
  &lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;Transform&lt;/span&gt;&lt;span class="p"&gt;(({&lt;/span&gt; &lt;span class="nx"&gt;obj&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;obj&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;email&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="nx"&gt;normalized_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="p"&gt;}&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;dto&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;plainToInstance&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;MobileSignupBody&lt;/span&gt;&lt;span class="p"&gt;,&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="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;abc@test.com&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;dto&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;normalized_email&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// ✅ "abc@test.com"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now it works because &lt;code&gt;@Expose&lt;/code&gt; forces &lt;code&gt;normalized_email&lt;/code&gt; into the transform pipeline.&lt;/p&gt;

&lt;h2&gt;
  
  
  Global Config Options
&lt;/h2&gt;

&lt;p&gt;If you don’t want to sprinkle &lt;code&gt;@Expose()&lt;/code&gt; everywhere, you have a couple of global strategies:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;code&gt;excludeExtraneousValues&lt;/code&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;dto&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;plainToInstance&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;MobileSignupBody&lt;/span&gt;&lt;span class="p"&gt;,&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="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;abc@test.com&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="na"&gt;excludeExtraneousValues&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In this mode, only properties with @Expose() are kept.&lt;br&gt;
Downside: you must decorate all properties you want to keep.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;code&gt;strategy: 'exposeAll'&lt;/code&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;Expose&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;strategy&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;exposeAll&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt;
&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;MobileSignupBody&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;email&lt;/span&gt;&lt;span class="o"&gt;!&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="nd"&gt;Expose&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
  &lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;Transform&lt;/span&gt;&lt;span class="p"&gt;(({&lt;/span&gt; &lt;span class="nx"&gt;obj&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;obj&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;email&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="nx"&gt;normalized_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="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;This makes all properties exposed by default, and you only need @Expose() where you want transforms.&lt;/p&gt;
&lt;h2&gt;
  
  
  Alternative: Use a Getter
&lt;/h2&gt;

&lt;p&gt;Sometimes, the simplest solution is just a computed property:&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="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;MobileSignupBody&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;email&lt;/span&gt;&lt;span class="o"&gt;!&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="kd"&gt;get&lt;/span&gt; &lt;span class="nf"&gt;normalized_email&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="kc"&gt;undefined&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;email&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;No @Expose, no @Transform, always works.&lt;/p&gt;

&lt;h2&gt;
  
  
  Takeaways
&lt;/h2&gt;

&lt;p&gt;&lt;code&gt;@Transform&lt;/code&gt; only runs if the property exists in the plain object.&lt;br&gt;
Use &lt;code&gt;@Expose&lt;/code&gt; to force properties into the transformation pipeline.&lt;br&gt;
For global behavior, use excludeExtraneousValues or strategy: 'exposeAll'. Or skip it all and use getters if you just want a derived property.&lt;/p&gt;

&lt;h2&gt;
  
  
  🔗 Useful refs:
&lt;/h2&gt;

&lt;p&gt;class-transformer &lt;a href="https://github.com/typestack/class-transformer" rel="noopener noreferrer"&gt;docs&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;class-validator &lt;a href="https://github.com/typestack/class-validator" rel="noopener noreferrer"&gt;docs&lt;/a&gt;&lt;/p&gt;




&lt;p&gt;Have you ever hit this @Transform gotcha? How do you usually handle derived fields in DTOs — getters, transforms, or something else? Let me know 👇&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>programming</category>
      <category>javascript</category>
      <category>typescript</category>
    </item>
    <item>
      <title>😬 The Silent Gotcha in MySQL: ENUMs and Non-Strict SQL Mode</title>
      <dc:creator>Tim Nguyen</dc:creator>
      <pubDate>Tue, 01 Jul 2025 15:31:45 +0000</pubDate>
      <link>https://dev.to/deko39/the-silent-gotcha-in-mysql-enums-and-non-strict-sql-mode-38c6</link>
      <guid>https://dev.to/deko39/the-silent-gotcha-in-mysql-enums-and-non-strict-sql-mode-38c6</guid>
      <description>&lt;p&gt;If you're using MySQL and rely on ENUM columns for validation, you might be silently inserting bad data without realizing it. Let's talk about what happens when SQL mode is not strict.&lt;/p&gt;

&lt;h3&gt;
  
  
  🧪 The Setup
&lt;/h3&gt;

&lt;p&gt;You define a table like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;CREATE&lt;/span&gt; &lt;span class="k"&gt;TABLE&lt;/span&gt; &lt;span class="n"&gt;users&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="n"&gt;marital_status&lt;/span&gt; &lt;span class="nb"&gt;ENUM&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="s1"&gt;'single'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="s1"&gt;'married'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="s1"&gt;'unmarried'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="s1"&gt;'divorced'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="s1"&gt;'separated'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="s1"&gt;'widowed'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="s1"&gt;'never_married'&lt;/span&gt;
  &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;NOT&lt;/span&gt; &lt;span class="k"&gt;NULL&lt;/span&gt;
&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Looks solid, right? Now, let’s try inserting an invalid value:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;INSERT&lt;/span&gt; &lt;span class="k"&gt;INTO&lt;/span&gt; &lt;span class="n"&gt;users&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;marital_status&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;VALUES&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'unknown'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  😱 What Happens in Non-Strict Mode?
&lt;/h3&gt;

&lt;p&gt;If you're not in strict SQL mode, MySQL won't throw an error. It will:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Insert the value as an empty string ''&lt;/li&gt;
&lt;li&gt;Silently trigger a warning&lt;/li&gt;
&lt;li&gt;Store it as the internal ENUM value 0&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Yep, your invalid input just became... nothing.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;users&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="c1"&gt;-- Output:&lt;/span&gt;
&lt;span class="c1"&gt;-- | marital_status |&lt;/span&gt;
&lt;span class="c1"&gt;-- |----------------|&lt;/span&gt;
&lt;span class="c1"&gt;-- |                |  &amp;lt;-- EMPTY STRING!&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  🔍 Why This Happens
&lt;/h3&gt;

&lt;p&gt;Internally, MySQL maps ENUM values to integers (and enum in MySQL has index starting at 0 &lt;a href="https://dev.mysql.com/doc/refman/8.4/en/enum.html" rel="noopener noreferrer"&gt;docs&lt;/a&gt;):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Value      Index (internal)
''(empty)  0
'single'   1
'married'  2
...        ...
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;When you insert an invalid value, MySQL stores 0, which corresponds to ''.&lt;/p&gt;

&lt;h3&gt;
  
  
  🔥 How to Avoid It: Enable Strict Mode
&lt;/h3&gt;

&lt;p&gt;Use strict SQL mode to make MySQL behave properly:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;SET&lt;/span&gt; &lt;span class="n"&gt;sql_mode&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'STRICT_TRANS_TABLES'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now try that bad insert again:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;INSERT&lt;/span&gt; &lt;span class="k"&gt;INTO&lt;/span&gt; &lt;span class="n"&gt;users&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;marital_status&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;VALUES&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'unknown'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="c1"&gt;-- ❌ ERROR 1265 (01000): Data truncated for column 'marital_status' at row 1&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Perfect. You’re now protected from silent failures.&lt;/p&gt;

&lt;h3&gt;
  
  
  💡 Pro Tip: Make It Permanent
&lt;/h3&gt;

&lt;p&gt;Edit your MySQL config file:&lt;br&gt;
On Linux (/etc/mysql/my.cnf) or macOS:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ini"&gt;&lt;code&gt;&lt;span class="nn"&gt;[mysqld]&lt;/span&gt;
&lt;span class="py"&gt;sql_mode&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;STRICT_TRANS_TABLES&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then restart MySQL.&lt;/p&gt;

&lt;p&gt;Read more: &lt;a href="https://dev.mysql.com/doc/refman/8.4/en/sql-mode.html#sql-mode-strict" rel="noopener noreferrer"&gt;SQL strict mode&lt;/a&gt;&lt;/p&gt;

</description>
      <category>mysql</category>
      <category>webdev</category>
      <category>prisma</category>
      <category>programming</category>
    </item>
    <item>
      <title>Avoid This Silent Timeout Bug with Axios in Lambda as proxy, ALB, and Content-Length</title>
      <dc:creator>Tim Nguyen</dc:creator>
      <pubDate>Thu, 19 Jun 2025 06:21:05 +0000</pubDate>
      <link>https://dev.to/deko39/avoid-this-silent-timeout-bug-with-axios-in-lambda-as-proxy-alb-and-content-length-1fi5</link>
      <guid>https://dev.to/deko39/avoid-this-silent-timeout-bug-with-axios-in-lambda-as-proxy-alb-and-content-length-1fi5</guid>
      <description>&lt;p&gt;Have you ever encountered a &lt;strong&gt;504 Gateway Timeout&lt;/strong&gt; from your &lt;strong&gt;AWS Application Load Balancer (ALB)&lt;/strong&gt; when proxying requests via &lt;strong&gt;Axios&lt;/strong&gt;, even though everything looks right on the surface?&lt;/p&gt;

&lt;p&gt;Here’s a tricky issue I ran into — and how to avoid it in the future.&lt;/p&gt;




&lt;h2&gt;
  
  
  💥 The Setup
&lt;/h2&gt;

&lt;p&gt;You're using AWS Lambda using a Node.js proxy to forward a &lt;code&gt;POST&lt;/code&gt; request to a backend behind ALB:&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;await&lt;/span&gt; &lt;span class="nx"&gt;axios&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;post&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;http://my-backend&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="na"&gt;timestamp&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;2025-06-10 10:49:46&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;token&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;fEjck23HJkLlrRkDKtNWVI:APA91bHphS5as...&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="na"&gt;headers&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;Content-Type&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;keep&lt;/span&gt; &lt;span class="nx"&gt;it&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="nx"&gt;original&lt;/span&gt; &lt;span class="nx"&gt;request&lt;/span&gt;&lt;span class="o"&gt;&amp;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;Content-Length&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kd"&gt;get&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="nx"&gt;original&lt;/span&gt; &lt;span class="nx"&gt;request&lt;/span&gt;&lt;span class="o"&gt;&amp;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;Looks harmless, but suddenly:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;504 Gateway Timeout from ALB
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And the backend logs show:&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="nx"&gt;SyntaxError&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Unterminated&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt; &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="nx"&gt;JSON&lt;/span&gt; &lt;span class="nx"&gt;at&lt;/span&gt; &lt;span class="nx"&gt;position&lt;/span&gt; &lt;span class="nx"&gt;xxx&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;🧠 Root Cause&lt;br&gt;
The error comes from a mismatch between the Content-Length and the actual body size.&lt;/p&gt;

&lt;p&gt;What actually happens:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Axios serializes the data object to JSON.&lt;/li&gt;
&lt;li&gt;You manually set Content-Length: xxx or forward it from orignial request, but the serialized body is longer.&lt;/li&gt;
&lt;li&gt;The backend receives only the first xxx bytes.&lt;/li&gt;
&lt;li&gt;It tries to parse, but the JSON is incomplete.&lt;/li&gt;
&lt;li&gt;It waits for the rest of the body, but it never comes.&lt;/li&gt;
&lt;li&gt;Eventually, the connection times out → ALB returns a 504.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;❌ Why Not a 400?&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Normally, a malformed JSON gives a 400 Bad Request.&lt;/li&gt;
&lt;li&gt;But here:

&lt;ul&gt;
&lt;li&gt;The backend doesn't know the full body has been sent.&lt;/li&gt;
&lt;li&gt;It thinks the rest is still coming.&lt;/li&gt;
&lt;li&gt;So it never sends a response → ALB gives up and returns 504.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;

&lt;p&gt;✅ The Fix&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Just don’t manually set Content-Length. Let Axios compute it.
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;axios&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;post&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;http://my-backend&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="na"&gt;timestamp&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;2025-06-10 10:49:46&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;token&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;fEjck23HJkLlrRkDKtNWVI:APA91b...&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="na"&gt;headers&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;Content-Type&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;keep&lt;/span&gt; &lt;span class="nx"&gt;it&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="nx"&gt;original&lt;/span&gt; &lt;span class="nx"&gt;request&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="c1"&gt;// ✅ Do not manually set Content-Length&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;Axios will:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Serialize the object to JSON&lt;/li&gt;
&lt;li&gt;Compute the correct length&lt;/li&gt;
&lt;li&gt;Set the header automatically&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Have fun coding!&lt;/p&gt;

</description>
      <category>lambda</category>
      <category>loadbalancer</category>
      <category>axios</category>
      <category>http</category>
    </item>
    <item>
      <title>CloudFront + S3 + Static domain routing</title>
      <dc:creator>Tim Nguyen</dc:creator>
      <pubDate>Wed, 11 Dec 2024 14:43:25 +0000</pubDate>
      <link>https://dev.to/deko39/cloudfront-s3-static-domain-routing-1afh</link>
      <guid>https://dev.to/deko39/cloudfront-s3-static-domain-routing-1afh</guid>
      <description>&lt;h2&gt;
  
  
  What are existing:
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;S3 bucket 1: &lt;code&gt;xxx.s3.us-east-1.amazonaws.com&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;S3 bucket 2: &lt;code&gt;yyy.s3.us-east-1.amazonaws.com&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Domain name: &lt;code&gt;abc.com&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  What is requiring:
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Using only 1 static domain name: &lt;code&gt;static.abc.com&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Using only 1 CloudFront distribution to serve both S3 buckets above with prefix (S3 bucket 1 - prefix &lt;code&gt;admin&lt;/code&gt; and S3 bucket 2 - prefix &lt;code&gt;public&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;Eg: What is served with &lt;code&gt;https://xxx.s3.us-east-1.amazonaws.com/img.jpg&lt;/code&gt; can be served with &lt;code&gt;https://static.abc.com/admin/img.jpg&lt;/code&gt; and what is served with &lt;code&gt;https://yyy.s3.us-east-1.amazonaws.com/img.png&lt;/code&gt; can be served with &lt;code&gt;https://static.abc.com/public/img.png&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Idea
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Create a distribution in CloudFront with 2 origins are 2 bucket above&lt;/li&gt;
&lt;li&gt;Create 3 distribution behaviors:

&lt;ul&gt;
&lt;li&gt;/admin/* - To Distribution's Origin of S3 Bucket 1&lt;/li&gt;
&lt;li&gt;/public/* - To Distribution's Origin of S3 Bucket 2&lt;/li&gt;
&lt;li&gt;Default(*) - To either Origin of S3 Bucket 1 or Origin of S3 Bucket 2&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;Modify 3 distribution behaviors to have function associations to Lambda@Edge function (this will be in another post, but the document for create one &lt;a href="https://docs.aws.amazon.com/AmazonCloudFront/latest/DeveloperGuide/edge-functions.html" rel="noopener noreferrer"&gt;here&lt;/a&gt;)&lt;/li&gt;

&lt;/ul&gt;

</description>
    </item>
    <item>
      <title>Node.js + ioredis + elasticache</title>
      <dc:creator>Tim Nguyen</dc:creator>
      <pubDate>Thu, 05 Dec 2024 02:36:22 +0000</pubDate>
      <link>https://dev.to/deko39/nodejs-ioredis-elasticache-e7l</link>
      <guid>https://dev.to/deko39/nodejs-ioredis-elasticache-e7l</guid>
      <description>&lt;p&gt;For who is struggling with ioredis connection to elasticache (cluster enable)&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;const REDIS_COMMAND_TIMEOUT = 10000;
const MAX_REDIS_ATTEMPS = 3;
const REDIS_BACKOFF_RETRY = 2000;

const REDIS_HOST = process.env.REDIS_HOST || '127.0.0.1';
const REDIS_PORT = Number(process.env.REDIS_PORT) || 6379;

// https://github.com/redis/lettuce/wiki/Redis-URI-and-connection-details
const REDIS_SCHEME = REDIS_CLUSTER_ENABLED ? `rediss` : `redis`;

const redis = new Redis(`${REDIS_SCHEME}://${REDIS_HOST}:${REDIS_PORT}`, {
  commandTimeout: REDIS_COMMAND_TIMEOUT,
  retryStrategy: (times: number): number | null =&amp;gt; {
    if (times &amp;gt; MAX_REDIS_ATTEMPS) {
      return null; // Stop retrying
    }
    return REDIS_BACKOFF_RETRY; // Retry after some time
  },
  tls: REDIS_CLUSTER_ENABLED ? {} : undefined,
});
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



</description>
      <category>elasticache</category>
      <category>ioredis</category>
      <category>node</category>
    </item>
    <item>
      <title>MySQL syntaxes</title>
      <dc:creator>Tim Nguyen</dc:creator>
      <pubDate>Tue, 25 Jun 2024 08:23:44 +0000</pubDate>
      <link>https://dev.to/deko39/mysql-syntaxes-1moi</link>
      <guid>https://dev.to/deko39/mysql-syntaxes-1moi</guid>
      <description>&lt;h1&gt;
  
  
  What
&lt;/h1&gt;

&lt;p&gt;👎 &lt;code&gt;JSON_UNQUOTE + JSON_EXTRACT&lt;/code&gt; vs 👍 &lt;code&gt;-&amp;gt;&amp;gt;&lt;/code&gt;&lt;/p&gt;


&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;

&lt;p&gt;SELECT JSON_UNQUOTE(JSON_EXTRACT(metadata, 'field')) 👎&lt;br&gt;
SELECT metadata-&amp;gt;&amp;gt;'$.field' 👍&lt;/p&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;h1&gt;
&lt;br&gt;
  &lt;br&gt;
  &lt;br&gt;
  Why it matters?&lt;br&gt;
&lt;/h1&gt;

&lt;p&gt;&lt;code&gt;JSON_EXTRACT&lt;/code&gt; vs &lt;code&gt;-&amp;gt;&lt;/code&gt; only return the json result&lt;br&gt;
&lt;code&gt;JSON_UNQUOTE + JSON_EXTRACT&lt;/code&gt; vs &lt;code&gt;-&amp;gt;&amp;gt;&lt;/code&gt; return the &lt;code&gt;parsed&lt;/code&gt; result (you can think of the JSON.parse method in JS)&lt;/p&gt;

</description>
      <category>syntaxes</category>
      <category>mysql</category>
    </item>
    <item>
      <title>Lambda configurations for ALB to reach</title>
      <dc:creator>Tim Nguyen</dc:creator>
      <pubDate>Fri, 07 Jun 2024 04:27:54 +0000</pubDate>
      <link>https://dev.to/deko39/lambda-permission-for-alb-to-reach-4a1n</link>
      <guid>https://dev.to/deko39/lambda-permission-for-alb-to-reach-4a1n</guid>
      <description>&lt;p&gt;We need&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A lambda function:

&lt;ul&gt;
&lt;li&gt;No need Function URL enabled&lt;/li&gt;
&lt;li&gt;VPC configs to be in the same VPC and subnet with main VPC used by ALB&lt;/li&gt;
&lt;li&gt;Security group with inbound from main VPC CIDR block (eg: 10.1.0.0/16) and outbound to internet (eg: 0.0.0.0/0) &lt;/li&gt;
&lt;li&gt;Permission set to accept both &lt;strong&gt;ALB arn and Target group arn&lt;/strong&gt;
&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;
&lt;li&gt;A target group set to lambda function&lt;/li&gt;
&lt;li&gt;An application load balancer has a listener to lambda target group above&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>webdev</category>
      <category>programming</category>
      <category>aws</category>
    </item>
    <item>
      <title>AWS Lambda - Socket hang up Nodejs</title>
      <dc:creator>Tim Nguyen</dc:creator>
      <pubDate>Thu, 05 Oct 2023 06:53:32 +0000</pubDate>
      <link>https://dev.to/deko39/aws-lambda-socket-hang-up-nodejs-ind</link>
      <guid>https://dev.to/deko39/aws-lambda-socket-hang-up-nodejs-ind</guid>
      <description>&lt;h2&gt;
  
  
  Context: Lambda error said 'socket hang up' within nodejs code when performing http/https request to 3rd party backend
&lt;/h2&gt;

&lt;p&gt;Let's describe my tries on solving the problem&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Increase the timeout of lambda -&amp;gt; Not help&lt;/li&gt;
&lt;li&gt;Increase the timeout of httpClient -&amp;gt; Not help&lt;/li&gt;
&lt;li&gt;Figure out that lambda having some problem with connection -&amp;gt; Put HttpAgent to keepalive the connection -&amp;gt; Work perfectly&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Code demo:
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;new JwksClient({
      jwksUri: 'xxx' + '/oidc/2/certs',
      timeout: 10000,
      requestAgent: new Agent({
        keepAlive: true
      })
    })
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



</description>
      <category>node</category>
      <category>lambda</category>
      <category>http</category>
      <category>https</category>
    </item>
  </channel>
</rss>
