<?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: Janmejai Singh</title>
    <description>The latest articles on DEV Community by Janmejai Singh (@janmejaisingh).</description>
    <link>https://dev.to/janmejaisingh</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%2F1486995%2F034daef9-19d9-44df-b201-fbcf10ebb4ca.jpg</url>
      <title>DEV Community: Janmejai Singh</title>
      <link>https://dev.to/janmejaisingh</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/janmejaisingh"/>
    <language>en</language>
    <item>
      <title>Callbacks in JavaScript: Why They Exist</title>
      <dc:creator>Janmejai Singh</dc:creator>
      <pubDate>Wed, 29 Jul 2026 17:31:31 +0000</pubDate>
      <link>https://dev.to/janmejaisingh/callbacks-in-javascript-why-they-exist-4l5f</link>
      <guid>https://dev.to/janmejaisingh/callbacks-in-javascript-why-they-exist-4l5f</guid>
      <description>&lt;h1&gt;
  
  
  Callbacks in JavaScript: Why They Exist
&lt;/h1&gt;

&lt;p&gt;JavaScript treats functions like values. You can store them, pass them to another function, and call them later. That flexibility is the reason &lt;strong&gt;callbacks&lt;/strong&gt; exist.&lt;/p&gt;

&lt;p&gt;This article moves from simple function arguments to asynchronous programming and callback nesting.&lt;/p&gt;

&lt;h2&gt;
  
  
  Functions are values
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;sayHello&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="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="p"&gt;}&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;greet&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;sayHello&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="nf"&gt;greet&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt; &lt;span class="c1"&gt;// Hello!&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;sayHello&lt;/code&gt; is the function itself. &lt;code&gt;sayHello()&lt;/code&gt; runs it.&lt;/p&gt;

&lt;h2&gt;
  
  
  What is a callback?
&lt;/h2&gt;

&lt;p&gt;A callback is a function passed into another function so that it can be run later.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;welcome&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;afterWelcome&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="s2"&gt;`Welcome, &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;name&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="nf"&gt;afterWelcome&lt;/span&gt;&lt;span class="p"&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;showMenu&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="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Here is today's menu.&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="nf"&gt;welcome&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Vasu&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;showMenu&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;flowchart LR
  A[Call welcome] --&amp;gt; B[Print greeting]
  B --&amp;gt; C[Run callback]
  C --&amp;gt; D[Show menu]
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We pass &lt;code&gt;showMenu&lt;/code&gt;, not &lt;code&gt;showMenu()&lt;/code&gt;, because &lt;code&gt;welcome&lt;/code&gt; decides when to call it.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why callbacks are useful
&lt;/h2&gt;

&lt;p&gt;Callbacks make code reusable. One function can provide data while another function decides what to do with it.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;processNumber&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;number&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;action&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="nf"&gt;action&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;number&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="nf"&gt;processNumber&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;,&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="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;value&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt; &lt;span class="c1"&gt;// 10&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="nf"&gt;processNumber&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;,&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="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;value&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt; &lt;span class="c1"&gt;// 25&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Common callback scenarios
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Arrays
&lt;/h3&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;prices&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;100&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="mi"&gt;80&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;discounted&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;prices&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;map&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;price&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;price&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mf"&gt;0.9&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;expensive&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;prices&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;filter&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;price&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;price&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;100&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;map&lt;/code&gt; and &lt;code&gt;filter&lt;/code&gt; call your callback for each array item.&lt;/p&gt;

&lt;h3&gt;
  
  
  Click events
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;button&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;addEventListener&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;click&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="o"&gt;=&amp;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="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Button clicked&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The browser runs this callback only when a user clicks.&lt;/p&gt;

&lt;h3&gt;
  
  
  Timers
&lt;/h3&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;Order received&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="nf"&gt;setTimeout&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="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;Food is ready&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="mi"&gt;2000&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="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Preparing payment&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;The timeout callback runs later, so JavaScript can continue with other work first.&lt;/p&gt;

&lt;h2&gt;
  
  
  Callbacks and asynchronous JavaScript
&lt;/h2&gt;

&lt;p&gt;Slow tasks—network calls, timers, files, database work—should not freeze the app. JavaScript starts the task, keeps running other code, and executes a callback when the task finishes.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;flowchart LR
  A[Start async task] --&amp;gt; B[Continue other work]
  B --&amp;gt; C[Task completes]
  C --&amp;gt; D[Callback runs]
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Here is a classic Node.js-style callback:&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;readFile&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;notes.txt&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;utf8&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;content&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;error&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;error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="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;content&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;Many Node.js APIs use the “error-first” callback pattern: check the &lt;code&gt;error&lt;/code&gt; first, then use the result.&lt;/p&gt;

&lt;h2&gt;
  
  
  The drawback: callback nesting
&lt;/h2&gt;

&lt;p&gt;When several asynchronous steps depend on each other, callbacks can become deeply nested:&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;getUser&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;userId&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;user&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nf"&gt;getOrders&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;user&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;orders&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nf"&gt;getPaymentStatus&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;orders&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;status&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nf"&gt;sendNotification&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;status&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="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="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;All done&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="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;This is known as &lt;strong&gt;callback hell&lt;/strong&gt;. The issue is readability, repeated error handling, and difficulty changing the flow—not that callbacks are inherently bad.&lt;/p&gt;

&lt;p&gt;Modern JavaScript often uses Promises and &lt;code&gt;async&lt;/code&gt;/&lt;code&gt;await&lt;/code&gt; for these sequences:&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;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;notifyAboutFirstOrder&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;userId&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;user&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;getUser&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;userId&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;orders&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;getOrders&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;user&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&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;status&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;getPaymentStatus&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;orders&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;sendNotification&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;status&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Final takeaway
&lt;/h2&gt;

&lt;p&gt;Callbacks let JavaScript say: &lt;strong&gt;“When this work is done, run this next function.”&lt;/strong&gt; They power array methods, browser events, timers, and asynchronous APIs.&lt;/p&gt;

&lt;p&gt;Learn callbacks first—the ideas behind Promises and &lt;code&gt;async&lt;/code&gt;/&lt;code&gt;await&lt;/code&gt; become much easier afterward.&lt;/p&gt;

</description>
      <category>javascript</category>
      <category>chaiaurcode</category>
      <category>asynchronous</category>
    </item>
    <item>
      <title>Kafka Explained Like You're 5: Events, Partitions, and Consumer Groups</title>
      <dc:creator>Janmejai Singh</dc:creator>
      <pubDate>Wed, 29 Jul 2026 17:27:39 +0000</pubDate>
      <link>https://dev.to/janmejaisingh/kafka-explained-like-youre-5-events-partitions-and-consumer-groups-28k4</link>
      <guid>https://dev.to/janmejaisingh/kafka-explained-like-youre-5-events-partitions-and-consumer-groups-28k4</guid>
      <description>&lt;h1&gt;
  
  
  Kafka Explained Like You're 5: Events, Partitions, and Consumer Groups
&lt;/h1&gt;

&lt;p&gt;&lt;strong&gt;How does a large application process millions of events every day?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Picture a food-delivery app at dinner time. Customers place orders, restaurants accept them, riders get assigned, payments go through, users get alerts, and analytics teams measure everything—at the same time.&lt;/p&gt;

&lt;p&gt;If each service had to call every other service directly, the app would become slow, fragile, and hard to grow. &lt;strong&gt;Apache Kafka&lt;/strong&gt; gives every service a shared, reliable way to announce what happened and let the right systems react.&lt;/p&gt;

&lt;p&gt;This guide explains Kafka through four ideas: &lt;strong&gt;events → topics → partitions → consumer groups&lt;/strong&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why systems need event streaming
&lt;/h2&gt;

&lt;p&gt;Small apps often use request-response calls:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Checkout service → Payment service → Notification service
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;As the product grows, checkout may also need inventory, delivery assignment, fraud checks, analytics, email, SMS, and recommendations. Now it depends on all of them. That is tight coupling.&lt;/p&gt;

&lt;p&gt;With event streaming, checkout only announces a fact: &lt;strong&gt;“An order was created.”&lt;/strong&gt; Every interested service can react independently.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;flowchart LR
  A[Checkout] --&amp;gt;|Order created| K[Kafka]
  K --&amp;gt; P[Payment]
  K --&amp;gt; N[Notifications]
  K --&amp;gt; I[Inventory]
  K --&amp;gt; X[Analytics]
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  What is Kafka?
&lt;/h2&gt;

&lt;p&gt;Apache Kafka is a distributed &lt;strong&gt;event-streaming platform&lt;/strong&gt;. Think of it as a durable shared diary where applications write events and other applications read them.&lt;/p&gt;

&lt;p&gt;Unlike a basic queue, Kafka retains events for a configured period. Several applications can read the same event stream, and an application can replay older events when needed.&lt;/p&gt;

&lt;p&gt;Common uses include analytics pipelines, payment events, notifications, activity tracking, fraud detection, and communication between microservices.&lt;/p&gt;

&lt;h2&gt;
  
  
  Events: the smallest Kafka unit
&lt;/h2&gt;

&lt;p&gt;An &lt;strong&gt;event&lt;/strong&gt; is a record that says something happened.&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;"eventType"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"OrderCreated"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"orderId"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"ORD-1042"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"customerId"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"CUS-79"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"createdAt"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"2026-07-29T17:30:00Z"&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;A &lt;strong&gt;producer&lt;/strong&gt; sends an event.&lt;/li&gt;
&lt;li&gt;Kafka stores it.&lt;/li&gt;
&lt;li&gt;A &lt;strong&gt;consumer&lt;/strong&gt; reads and processes it.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;For an order, the flow might be: a user places an order → the Order service produces &lt;code&gt;OrderCreated&lt;/code&gt; → Kafka stores it → payment, inventory, notifications, and analytics consume it independently.&lt;/p&gt;

&lt;h2&gt;
  
  
  Topics: organized streams
&lt;/h2&gt;

&lt;p&gt;A &lt;strong&gt;topic&lt;/strong&gt; is a named stream of related events—like a labelled shelf in the event diary.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Topic&lt;/th&gt;
&lt;th&gt;Example events&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;orders&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;created, cancelled, delivered&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;payments&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;initiated, completed, failed&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;user-activity&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;login, click, search&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;notifications&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;email, SMS, push requests&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Multiple producers may write to one topic, and multiple consumers may read from it.&lt;/p&gt;

&lt;h2&gt;
  
  
  Partitions: how Kafka scales
&lt;/h2&gt;

&lt;p&gt;One busy topic is split into &lt;strong&gt;partitions&lt;/strong&gt;. Each partition is an ordered, append-only log.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;flowchart TB
  P[Producer] --&amp;gt; T[orders topic]
  T --&amp;gt; A[Partition 0]
  T --&amp;gt; B[Partition 1]
  T --&amp;gt; C[Partition 2]
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Partitions enable parallel processing and let Kafka spread work across brokers. Kafka guarantees order &lt;strong&gt;inside one partition&lt;/strong&gt;, not across every partition in a topic.&lt;/p&gt;

&lt;p&gt;Use a key such as &lt;code&gt;orderId&lt;/code&gt; when related events must stay ordered. Events with the same key go to the same partition, preserving sequences like:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;OrderCreated → PaymentCompleted → RiderAssigned → OrderDelivered
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Why Kafka is fast
&lt;/h2&gt;

&lt;p&gt;Kafka is designed for high throughput:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;New events are written sequentially.&lt;/li&gt;
&lt;li&gt;Partitions are append-only logs.&lt;/li&gt;
&lt;li&gt;Sequential disk work is efficient.&lt;/li&gt;
&lt;li&gt;Kafka batches reads and writes to reduce overhead.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This is why Kafka can handle large, continuous streams without treating every event like a slow, isolated database operation.&lt;/p&gt;

&lt;h2&gt;
  
  
  Consumer groups: teamwork without duplicate work
&lt;/h2&gt;

&lt;p&gt;A &lt;strong&gt;consumer group&lt;/strong&gt; is a team of consumers reading the same topic. In one group, each partition is assigned to only one consumer at a time.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;flowchart LR
  P0[Partition 0] --&amp;gt; C1[Consumer 1]
  P1[Partition 1] --&amp;gt; C2[Consumer 2]
  P2[Partition 2] --&amp;gt; C3[Consumer 3]
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This means a Billing service can run several instances without processing each order repeatedly. A separate Analytics consumer group can still read every order independently.&lt;/p&gt;

&lt;p&gt;If a consumer fails, Kafka reassigns its partitions to healthy consumers. This is called &lt;strong&gt;rebalancing&lt;/strong&gt;. Consumer groups track their reading position using &lt;strong&gt;offsets&lt;/strong&gt;, so they can continue from the correct point.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;More consumers than partitions do not increase active parallelism. A topic with three partitions can have at most three active consumers in one group.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Kafka architecture
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Component&lt;/th&gt;
&lt;th&gt;Purpose&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Producer&lt;/td&gt;
&lt;td&gt;Publishes events&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Broker&lt;/td&gt;
&lt;td&gt;Kafka server that stores data&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Topic&lt;/td&gt;
&lt;td&gt;Named event stream&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Partition&lt;/td&gt;
&lt;td&gt;Ordered, scalable slice of a topic&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Consumer&lt;/td&gt;
&lt;td&gt;Reads events&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Consumer group&lt;/td&gt;
&lt;td&gt;Shares partitions across consumers&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;flowchart LR
  PR[Producers] --&amp;gt; B[Kafka brokers]
  B --&amp;gt; TP[Topic partitions]
  TP --&amp;gt; CG[Consumer groups]
  CG --&amp;gt; CO[Consumers]
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Kafka vs traditional queues
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Capability&lt;/th&gt;
&lt;th&gt;Traditional queue&lt;/th&gt;
&lt;th&gt;Kafka&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Main model&lt;/td&gt;
&lt;td&gt;Give a job to a worker&lt;/td&gt;
&lt;td&gt;Store a stream of facts&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;After reading&lt;/td&gt;
&lt;td&gt;Often removed&lt;/td&gt;
&lt;td&gt;Retained for a period&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Replay&lt;/td&gt;
&lt;td&gt;Usually limited&lt;/td&gt;
&lt;td&gt;Built in with offsets&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Multiple apps reading&lt;/td&gt;
&lt;td&gt;Needs extra routing&lt;/td&gt;
&lt;td&gt;Each group reads independently&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Scaling&lt;/td&gt;
&lt;td&gt;Add workers&lt;/td&gt;
&lt;td&gt;Add partitions and consumers&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Kafka can behave like a queue inside one consumer group, while still allowing other groups to consume the same retained data.&lt;/p&gt;

&lt;h2&gt;
  
  
  Real-world Kafka applications
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;E-commerce:&lt;/strong&gt; order, inventory, shipment, and payment events.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Analytics:&lt;/strong&gt; clicks, searches, and product views flowing into dashboards.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Notifications:&lt;/strong&gt; emails and push messages triggered by business events.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Payments:&lt;/strong&gt; reconciliation and fraud checks after payment events.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Recommendations:&lt;/strong&gt; likes, views, and purchases becoming model signals.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Social platforms:&lt;/strong&gt; posts, comments, follows, and feed activity.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;For example, a &lt;code&gt;PaymentCompleted&lt;/code&gt; event can trigger warehouse packing, a customer notification, revenue analytics, and a loyalty-points update—without the Payment service calling each system directly.&lt;/p&gt;

&lt;h2&gt;
  
  
  The five-year-old summary
&lt;/h2&gt;

&lt;p&gt;Imagine a school notice board:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;An &lt;strong&gt;event&lt;/strong&gt; is a new notice: “Lunch is ready!”&lt;/li&gt;
&lt;li&gt;A &lt;strong&gt;topic&lt;/strong&gt; is the “Cafeteria” part of the board.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Partitions&lt;/strong&gt; are several columns so many notices can be handled quickly.&lt;/li&gt;
&lt;li&gt;A &lt;strong&gt;consumer group&lt;/strong&gt; is a team of helpers, where each helper reads a different column.&lt;/li&gt;
&lt;li&gt;Different teams can read the same notices for their own jobs.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Final takeaway
&lt;/h2&gt;

&lt;p&gt;Kafka is a foundation for reliable, scalable event-driven systems:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Events describe what happened. Topics organize them. Partitions scale them. Consumer groups distribute the work.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;What would you build with Kafka first—an order pipeline, notifications, analytics, or user activity tracking?&lt;/p&gt;

</description>
      <category>kafka</category>
      <category>systemdesign</category>
      <category>microservices</category>
      <category>chaiaurcode</category>
    </item>
    <item>
      <title>URL Parameters vs Query Strings in Express.js (With Real Examples)</title>
      <dc:creator>Janmejai Singh</dc:creator>
      <pubDate>Wed, 29 Jul 2026 17:19:45 +0000</pubDate>
      <link>https://dev.to/janmejaisingh/url-parameters-vs-query-strings-in-expressjs-with-real-examples-4629</link>
      <guid>https://dev.to/janmejaisingh/url-parameters-vs-query-strings-in-expressjs-with-real-examples-4629</guid>
      <description>&lt;h1&gt;
  
  
  URL Parameters vs Query Strings in Express.js (With Real Examples)
&lt;/h1&gt;

&lt;p&gt;If you've built more than one Express route, you've probably typed &lt;code&gt;req.params&lt;/code&gt; and &lt;code&gt;req.query&lt;/code&gt; without stopping to ask &lt;em&gt;why&lt;/em&gt; one is used here and the other is used there. They both pull data out of a URL, so it's easy to treat them as interchangeable — until your API design falls apart because a "filter" is hard-coded into the route path, or a required resource ID is quietly optional.&lt;/p&gt;

&lt;p&gt;This guide breaks down URL parameters and query strings from first principles, shows you exactly how to read them in Express, and gives you a simple rule for deciding which one to reach for.&lt;/p&gt;

&lt;h2&gt;
  
  
  The URL, Broken Down
&lt;/h2&gt;

&lt;p&gt;Before comparing the two, look at where each one actually lives 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;https://api.example.com/users/42/posts?sort=latest&amp;amp;limit=10
                         └──┬──┘             └───────┬───────┘
                    URL Parameter              Query String
                    (:id → "42")          (sort=latest&amp;amp;limit=10)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;Everything before the &lt;code&gt;?&lt;/code&gt; is the &lt;strong&gt;path&lt;/strong&gt; — and any dynamic piece of that path is a &lt;strong&gt;URL parameter&lt;/strong&gt; (also called a route parameter).&lt;/li&gt;
&lt;li&gt;Everything after the &lt;code&gt;?&lt;/code&gt; is the &lt;strong&gt;query string&lt;/strong&gt; — a set of key-value pairs separated by &lt;code&gt;&amp;amp;&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;They sit in different parts of the URL because they serve different purposes.&lt;/p&gt;

&lt;h2&gt;
  
  
  What Are URL Parameters?
&lt;/h2&gt;

&lt;p&gt;URL parameters (route parameters) are &lt;strong&gt;dynamic segments of the path itself&lt;/strong&gt;. They identify a specific, named resource. In Express, you declare them in your route definition with a colon:&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;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/users/:id&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;req&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;send&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`Fetching user with ID: &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;req&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="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A request to &lt;code&gt;/users/42&lt;/code&gt; matches this route, and Express extracts &lt;code&gt;42&lt;/code&gt; as &lt;code&gt;req.params.id&lt;/code&gt;. Without that segment, the route doesn't match at all — there's no such thing as an "optional" URL parameter unless you explicitly design one.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Think of URL parameters as identifiers.&lt;/strong&gt; They answer the question: &lt;em&gt;which specific thing am I asking for?&lt;/em&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;/users/42&lt;/code&gt; → the user whose ID is 42&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;/products/laptop-x200&lt;/code&gt; → the product with that slug&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;/orders/9981/items/3&lt;/code&gt; → item 3, inside order 9981&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  What Are Query Parameters?
&lt;/h2&gt;

&lt;p&gt;Query parameters (the query string) come after the &lt;code&gt;?&lt;/code&gt; and are made up of key-value pairs. They don't identify a resource — they &lt;strong&gt;modify or filter&lt;/strong&gt; the request you're already making.&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;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/products&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;req&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;category&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;sort&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;page&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;query&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;send&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`Category: &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;category&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;, Sort: &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;sort&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;, Page: &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;page&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A request to &lt;code&gt;/products?category=shoes&amp;amp;sort=price&amp;amp;page=2&lt;/code&gt; gives you:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;query&lt;/span&gt;
&lt;span class="c1"&gt;// { category: 'shoes', sort: 'price', page: '2' }&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Think of query parameters as filters or modifiers.&lt;/strong&gt; They answer the question: &lt;em&gt;how do I want this data shaped, sorted, or narrowed down?&lt;/em&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;/products?category=shoes&lt;/code&gt; → filter products by category&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;/articles?sort=newest&amp;amp;limit=5&lt;/code&gt; → sort and limit results&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;/search?q=express+js&amp;amp;page=2&lt;/code&gt; → a search term plus pagination&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  The Core Difference
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;&lt;/th&gt;
&lt;th&gt;URL Parameter&lt;/th&gt;
&lt;th&gt;Query String&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Location&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Part of the path&lt;/td&gt;
&lt;td&gt;After the &lt;code&gt;?&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Purpose&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Identifies a specific resource&lt;/td&gt;
&lt;td&gt;Filters, sorts, or modifies a request&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Required?&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Usually yes — the route won't match without it&lt;/td&gt;
&lt;td&gt;Usually optional&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Express access&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;req.params&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;req.query&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Example&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;/users/42&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;/users?role=admin&amp;amp;active=true&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Analogy&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;"Which record?"&lt;/td&gt;
&lt;td&gt;"How do you want it?"&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;The distinction isn't just cosmetic. If something is essential to identifying &lt;em&gt;what&lt;/em&gt; you're fetching, it belongs in the path as a parameter. If it's optional, or there could be several combined at once, it belongs in the query string.&lt;/p&gt;

&lt;h2&gt;
  
  
  Accessing Route Parameters in Express
&lt;/h2&gt;

&lt;p&gt;Express makes this straightforward with &lt;code&gt;req.params&lt;/code&gt;. You can define multiple parameters in a single route:&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;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/users/:userId/posts/:postId&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;req&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;userId&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;postId&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;req&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="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;send&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`User &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;userId&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;, Post &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;postId&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A request to &lt;code&gt;/users/7/posts/103&lt;/code&gt; gives you:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;params&lt;/span&gt;
&lt;span class="c1"&gt;// { userId: '7', postId: '103' }&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; all values in &lt;code&gt;req.params&lt;/code&gt; are strings. If you're expecting a number, convert it explicitly:&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;userId&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;parseInt&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;req&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="nx"&gt;userId&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Accessing Query Strings in Express
&lt;/h2&gt;

&lt;p&gt;Query parameters are available on &lt;code&gt;req.query&lt;/code&gt;, which Express parses automatically from the URL — no extra middleware required:&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;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/search&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;req&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;q&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;page&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;limit&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;query&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
    &lt;span class="na"&gt;searchTerm&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;q&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;page&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;Number&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;page&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="na"&gt;limit&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;Number&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;limit&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;A request to &lt;code&gt;/search?q=nodejs&amp;amp;page=2&lt;/code&gt; returns:&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;"searchTerm"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"nodejs"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"page"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"limit"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;10&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;Notice the default values for &lt;code&gt;page&lt;/code&gt; and &lt;code&gt;limit&lt;/code&gt; — this is standard practice, since query parameters are optional by nature. You should always assume a query parameter &lt;em&gt;might not be there&lt;/em&gt; and handle that case gracefully.&lt;/p&gt;

&lt;h2&gt;
  
  
  When to Use Params vs Query
&lt;/h2&gt;

&lt;p&gt;Here's the mental shortcut that removes the guesswork:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Use a URL parameter when the value identifies a specific resource, and the route is meaningless without it.&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;GET /users/:id          → one specific user
GET /orders/:orderId    → one specific order
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Use a query string when the value filters, sorts, paginates, or optionally modifies a collection of results.&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;GET /users?role=admin&amp;amp;status=active
GET /orders?sortBy=date&amp;amp;limit=20
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A useful test: &lt;em&gt;if you removed this value, would the endpoint still make sense?&lt;/em&gt; If removing it breaks the entire meaning of the request (no user ID means no user to fetch), it's a parameter. If removing it just gives you the default, unfiltered view, it's a query string.&lt;/p&gt;

&lt;p&gt;You'll often combine both in the same route:&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;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/users/:id/orders&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;req&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;id&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;req&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="c1"&gt;// which user&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;status&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;sort&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;query&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;      &lt;span class="c1"&gt;// how to filter/sort their orders&lt;/span&gt;
  &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;send&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`Orders for user &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;, filtered by &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;status&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;, sorted by &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;sort&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;GET /users/42/orders?status=shipped&amp;amp;sort=newest&lt;/code&gt; — the user is identified by a parameter, while the filtering is handled entirely through the query string. This is exactly how well-designed REST APIs are structured.&lt;/p&gt;

&lt;h2&gt;
  
  
  Wrapping Up
&lt;/h2&gt;

&lt;p&gt;URL parameters and query strings both extract data from a URL, but they're not interchangeable:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;URL parameters (&lt;code&gt;req.params&lt;/code&gt;)&lt;/strong&gt; identify &lt;em&gt;what&lt;/em&gt; resource you're working with. They live in the path and are usually required.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Query strings (&lt;code&gt;req.query&lt;/code&gt;)&lt;/strong&gt; describe &lt;em&gt;how&lt;/em&gt; you want that resource — filtered, sorted, paginated. They live after the &lt;code&gt;?&lt;/code&gt; and are usually optional.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Once you internalize "params identify, query modifies," designing clean, predictable Express routes becomes second nature — and your API consumers will thank you for it.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Found this useful? Follow for more practical Node.js and Express.js breakdowns.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>node</category>
      <category>express</category>
      <category>chaiaurcode</category>
    </item>
    <item>
      <title>Getting Started with cURL: How to Talk to a Server From Your Terminal</title>
      <dc:creator>Janmejai Singh</dc:creator>
      <pubDate>Wed, 29 Jul 2026 17:14:50 +0000</pubDate>
      <link>https://dev.to/janmejaisingh/getting-started-with-curl-how-to-talk-to-a-server-from-your-terminal-cln</link>
      <guid>https://dev.to/janmejaisingh/getting-started-with-curl-how-to-talk-to-a-server-from-your-terminal-cln</guid>
      <description>&lt;h1&gt;
  
  
  Getting Started with cURL: How to Talk to a Server From Your Terminal
&lt;/h1&gt;

&lt;p&gt;If you've ever opened your browser's Network tab, seen a wall of requests flying back and forth, and wondered &lt;em&gt;"how do people do this stuff without a browser?"&lt;/em&gt; — this article is for you.&lt;/p&gt;

&lt;p&gt;By the end of this post, you'll understand what cURL is, why almost every backend developer keeps it open in a terminal tab, and you'll have made your very first request. No jargon-heavy detours, no 40-flag cheat sheets. Just the basics, explained the way they should have been explained to you the first time.&lt;/p&gt;




&lt;h2&gt;
  
  
  First, What Is a Server — and Why Do We Even Need to Talk to It?
&lt;/h2&gt;

&lt;p&gt;Imagine a restaurant. You (the customer) don't walk into the kitchen and cook your own food. Instead, you tell a waiter what you want, the waiter carries that request to the kitchen, the kitchen prepares it, and the waiter brings the result back to your table.&lt;/p&gt;

&lt;p&gt;A &lt;strong&gt;server&lt;/strong&gt; is the kitchen. It's a computer somewhere (maybe down the hall, maybe on the other side of the planet) that stores data, runs logic, and hands back results when someone asks nicely.&lt;/p&gt;

&lt;p&gt;Every time you open a website, check a weather app, or log into an account, your device is sending a &lt;em&gt;request&lt;/em&gt; to a server and waiting for a &lt;em&gt;response&lt;/em&gt;. Your browser has been playing waiter for you this whole time — quietly making these requests and rendering the results as a webpage so you never had to think about the conversation happening underneath.&lt;/p&gt;

&lt;p&gt;cURL lets you become the waiter yourself.&lt;/p&gt;

&lt;h2&gt;
  
  
  What Is cURL, in Very Simple Terms
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;cURL&lt;/strong&gt; (pronounced "curl", short for &lt;strong&gt;Client URL&lt;/strong&gt;) is a command-line tool that lets you send requests to a server directly from your terminal, without needing a browser at all.&lt;/p&gt;

&lt;p&gt;Think of it as a way to type out a message, address it to a server, hit send, and read the reply — all in plain text, right where you're already writing code.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl https://example.com
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That one line does exactly what your browser does when you type a URL into the address bar and hit Enter — except instead of rendering a pretty page, cURL just shows you the raw response.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why Programmers Need cURL
&lt;/h2&gt;

&lt;p&gt;You might be thinking: &lt;em&gt;"I already have a browser. Why do I need this?"&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Here's why cURL earns a permanent spot in every backend developer's toolkit:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Testing APIs without building a UI first.&lt;/strong&gt; You can check if your backend endpoint works before a single line of frontend code exists.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Debugging in the exact environment your app runs in.&lt;/strong&gt; Servers, CI pipelines, and Docker containers usually don't have browsers — but they almost always have cURL.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Precise control.&lt;/strong&gt; You decide exactly what headers, data, and method get sent — nothing extra, nothing hidden.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Speed.&lt;/strong&gt; No clicking through UI panels. Just one line in the terminal and you have your answer.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;It's everywhere.&lt;/strong&gt; cURL ships with most operating systems and is often the first tool used to verify "is my server even reachable?"&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;In short: cURL is how developers have a direct, no-nonsense conversation with a server.&lt;/p&gt;

&lt;h3&gt;
  
  
  The Big Picture: cURL → Server → Response
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt; ┌────────────┐        request         ┌────────────┐
 │            │ ─────────────────────► │            │
 │   cURL     │                        │   Server   │
 │ (terminal) │ ◄───────────────────── │            │
 └────────────┘        response        └────────────┘
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That's really the whole story. You send something, the server processes it, and it sends something back. Everything else in this post is just filling in the details of that arrow.&lt;/p&gt;

&lt;h2&gt;
  
  
  Making Your First Request Using cURL
&lt;/h2&gt;

&lt;p&gt;Let's keep this as simple as physically possible. Open your terminal (cURL comes pre-installed on macOS and most Linux distributions; on Windows 10+, it's built into PowerShell/CMD too) and type:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl https://api.github.com
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Hit Enter. You'll see a block of text scroll by — that's the server's response, written in a format called &lt;strong&gt;JSON&lt;/strong&gt;. You just made your first request without touching a browser.&lt;/p&gt;

&lt;p&gt;Want to see a normal webpage instead of JSON? Try:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl https://example.com
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You'll see raw HTML — the same HTML your browser would normally turn into a nicely styled page for you.&lt;/p&gt;

&lt;h2&gt;
  
  
  Understanding Request and Response
&lt;/h2&gt;

&lt;p&gt;Every time you use cURL (or a browser, or an app), you're really sending a package with a few key parts, and getting a package back with a few key parts.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;A request typically has:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;A URL&lt;/strong&gt; — where you're sending it (&lt;code&gt;https://api.github.com/users/octocat&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;A method&lt;/strong&gt; — what kind of action you want (more on this below)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Headers&lt;/strong&gt; — extra information about the request, like what format you want the response in&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;A body&lt;/strong&gt; &lt;em&gt;(sometimes)&lt;/em&gt; — data you're sending along, like a new user's name and email&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;A response typically has:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;A status code&lt;/strong&gt; — a short number telling you what happened (&lt;code&gt;200&lt;/code&gt; means success, &lt;code&gt;404&lt;/code&gt; means "not found", &lt;code&gt;500&lt;/code&gt; means the server had a problem)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Headers&lt;/strong&gt; — metadata about the response&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;A body&lt;/strong&gt; — the actual data or content you asked for
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;REQUEST                          RESPONSE
--------                         --------
Method:  GET                     Status:  200 OK
URL:     /users/octocat          Headers: Content-Type: application/json
Headers: Accept: application/json
Body:    (none for GET)          Body:    { "login": "octocat", ... }
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You can see the status code and headers directly with cURL using the &lt;code&gt;-i&lt;/code&gt; flag:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl &lt;span class="nt"&gt;-i&lt;/span&gt; https://api.github.com
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is the single most useful habit to build early: whenever something goes wrong, check the status code first. It tells you, in one number, roughly whose fault it is — yours, or the server's.&lt;/p&gt;

&lt;h2&gt;
  
  
  Browser Request vs cURL Request (Conceptually)
&lt;/h2&gt;

&lt;p&gt;A browser and cURL are doing the &lt;em&gt;same fundamental thing&lt;/em&gt; — sending a request and receiving a response — they just differ in what happens to that response afterward.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;        You type a URL
               │
               ▼
      ┌──────────────────┐
      │   Browser         │──► sends request ──► Server
      │  (renders result  │◄── gets response ───
      │   as a webpage)   │
      └──────────────────┘

      ┌──────────────────┐
      │   cURL             │──► sends request ──► Server
      │ (prints raw result │◄── gets response ───
      │   to terminal)     │
      └──────────────────┘
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A browser takes the response and paints it as buttons, images, and text. cURL takes the same response and just prints it, raw, exactly as the server sent it. That's the whole difference — and it's precisely why cURL is so useful for developers: you see the &lt;em&gt;real&lt;/em&gt; data, with nothing hidden behind styling.&lt;/p&gt;

&lt;h2&gt;
  
  
  Using cURL to Talk to APIs
&lt;/h2&gt;

&lt;p&gt;Most real-world use of cURL isn't fetching webpages — it's talking to &lt;strong&gt;APIs&lt;/strong&gt; (Application Programming Interfaces), which are servers designed specifically to exchange data, not pretty pages.&lt;/p&gt;

&lt;p&gt;To keep things beginner-friendly, let's focus on just the two methods you'll use 90% of the time starting out:&lt;/p&gt;

&lt;h3&gt;
  
  
  GET — "Give me some data"
&lt;/h3&gt;

&lt;p&gt;&lt;code&gt;GET&lt;/code&gt; is for &lt;em&gt;reading&lt;/em&gt; information. It's the default method cURL uses if you don't specify one.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl https://api.github.com/users/octocat
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This asks the GitHub API for public information about the user &lt;code&gt;octocat&lt;/code&gt; and returns it as JSON.&lt;/p&gt;

&lt;h3&gt;
  
  
  POST — "Here's some data, please save it"
&lt;/h3&gt;

&lt;p&gt;&lt;code&gt;POST&lt;/code&gt; is for &lt;em&gt;sending&lt;/em&gt; information — creating something new, submitting a form, logging in, and so on.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl &lt;span class="nt"&gt;-X&lt;/span&gt; POST https://httpbin.org/post &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s2"&gt;"Content-Type: application/json"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="s1"&gt;'{"name": "Ada", "role": "Developer"}'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Let's break that down:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;-X POST&lt;/code&gt; tells cURL which method to use&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;-H "Content-Type: application/json"&lt;/code&gt; tells the server "the data I'm sending is JSON"&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;-d '...'&lt;/code&gt; is the actual data (the request body) you're sending&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;That's genuinely 90% of what you need to start exploring APIs confidently. &lt;code&gt;GET&lt;/code&gt; to read, &lt;code&gt;POST&lt;/code&gt; to send — everything else can wait until you actually need it.&lt;/p&gt;

&lt;h3&gt;
  
  
  Where cURL Fits in Backend Development
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt; Frontend            cURL (you, testing)         Backend API           Database
 ┌────────┐          ┌────────────────┐          ┌───────────┐        ┌──────────┐
 │  App /  │          │  Terminal       │          │  Server   │        │  Data     │
 │  Browser│          │  request/       │ ───────► │  (routes, │ ─────► │  storage  │
 │         │          │  response check │ ◄─────── │  logic)   │ ◄───── │           │
 └────────┘          └────────────────┘          └───────────┘        └──────────┘
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Before your frontend ever touches an API, developers usually verify it with cURL first — is the endpoint live, does it accept the right data, does it return what's expected. It becomes your first line of defense against bugs, long before a UI is involved.&lt;/p&gt;

&lt;h2&gt;
  
  
  Common Mistakes Beginners Make With cURL
&lt;/h2&gt;

&lt;p&gt;A few small stumbles trip up almost everyone early on — here's how to skip them:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Forgetting quotes around JSON data.&lt;/strong&gt; Your shell can misinterpret special characters like &lt;code&gt;{&lt;/code&gt;, &lt;code&gt;}&lt;/code&gt;, and &lt;code&gt;"&lt;/code&gt; if they aren't wrapped properly. Always wrap your &lt;code&gt;-d&lt;/code&gt; payload in single quotes.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Sending JSON without the &lt;code&gt;Content-Type&lt;/code&gt; header.&lt;/strong&gt; If you send a JSON body but forget &lt;code&gt;-H "Content-Type: application/json"&lt;/code&gt;, many servers won't parse it correctly — and you'll get a confusing error that has nothing to do with your actual data.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Assuming &lt;code&gt;GET&lt;/code&gt; when you meant &lt;code&gt;POST&lt;/code&gt; (or vice versa).&lt;/strong&gt; cURL defaults to &lt;code&gt;GET&lt;/code&gt;. If you're trying to create or send data, you must explicitly add &lt;code&gt;-X POST&lt;/code&gt;.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Ignoring the status code.&lt;/strong&gt; A response with no visible error doesn't mean it worked. Always check the status with &lt;code&gt;-i&lt;/code&gt; — a &lt;code&gt;404&lt;/code&gt; or &lt;code&gt;500&lt;/code&gt; buried in a big response body is easy to miss otherwise.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Not realizing HTTPS vs HTTP matters.&lt;/strong&gt; Some servers reject plain &lt;code&gt;http://&lt;/code&gt; and expect &lt;code&gt;https://&lt;/code&gt;. If your request silently fails, double-check the protocol.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Testing against the wrong environment.&lt;/strong&gt; It's easy to accidentally hit a production API when you meant to test locally (&lt;code&gt;localhost:3000&lt;/code&gt;). Always double-check your URL before running commands that send or modify data.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Wrapping Up
&lt;/h2&gt;

&lt;p&gt;cURL isn't complicated — it just looks unfamiliar the first time you see it. At its core, it's nothing more than a way to send a message to a server and read what comes back, the exact same conversation your browser has been having on your behalf all along.&lt;/p&gt;

&lt;p&gt;Start with plain &lt;code&gt;GET&lt;/code&gt; requests. Get comfortable reading status codes. Then move on to &lt;code&gt;POST&lt;/code&gt;. Everything else — headers, authentication tokens, file uploads — builds naturally on this same request-and-response foundation.&lt;/p&gt;

&lt;p&gt;The next time someone says "just cURL the endpoint," you'll know exactly what they mean — and exactly how to do it.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Found this helpful? Try running a few of these commands yourself right now — the best way to get comfortable with cURL is to break something with it and figure out why.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>curl</category>
      <category>webdev</category>
      <category>api</category>
      <category>chaiaurcode</category>
    </item>
    <item>
      <title>Where RAG Fails: Understanding the Limitations of Retrieval-Augmented Generation</title>
      <dc:creator>Janmejai Singh</dc:creator>
      <pubDate>Sun, 19 Jul 2026 10:07:34 +0000</pubDate>
      <link>https://dev.to/janmejaisingh/where-rag-fails-understanding-the-limitations-of-retrieval-augmented-generation-5021</link>
      <guid>https://dev.to/janmejaisingh/where-rag-fails-understanding-the-limitations-of-retrieval-augmented-generation-5021</guid>
      <description>&lt;h1&gt;
  
  
  Where RAG Fails: Understanding the Limitations of Retrieval-Augmented Generation
&lt;/h1&gt;

&lt;p&gt;If you've built anything with large language models in the last two years, you've almost certainly run into &lt;strong&gt;RAG&lt;/strong&gt; — Retrieval-Augmented Generation. It's become the default answer to "how do I make my LLM know about &lt;em&gt;my&lt;/em&gt; data?"&lt;/p&gt;

&lt;p&gt;But here's the part that doesn't get talked about enough: &lt;strong&gt;RAG fails a lot more often than the tutorials suggest.&lt;/strong&gt; Not because it's a bad idea, but because most teams treat it like a plug-and-play feature instead of a system with real engineering trade-offs.&lt;/p&gt;

&lt;p&gt;This article walks through what RAG actually is, why it exists, how it works under the hood, and — most importantly — where and why it breaks down in practice.&lt;/p&gt;




&lt;h2&gt;
  
  
  The Problem RAG Was Built to Solve
&lt;/h2&gt;

&lt;p&gt;Large language models are trained on a fixed snapshot of data. Once training ends, the model's knowledge is frozen. This creates two very real problems:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Staleness&lt;/strong&gt; — the model has no idea about anything that happened after its training cutoff, and no idea about your company's internal documents, product specs, or support tickets.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Hallucination under uncertainty&lt;/strong&gt; — when an LLM doesn't know something, it doesn't reliably say "I don't know." It often generates a fluent, confident-sounding answer that's simply wrong.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Imagine asking a brilliant new employee a question about your company's refund policy on their very first day, with no access to the employee handbook. They might guess intelligently based on general business knowledge — but they'd likely get specifics wrong. That's an LLM without external knowledge.&lt;/p&gt;

&lt;p&gt;You could try to fix this by cramming your entire knowledge base into the prompt every time. But that's expensive, slow, and quickly runs into a hard wall: the model's &lt;strong&gt;context window&lt;/strong&gt; — the maximum amount of text it can consider at once. This is exactly the gap RAG was designed to close.&lt;/p&gt;




&lt;h2&gt;
  
  
  What Is RAG, Really?
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Retrieval-Augmented Generation (RAG)&lt;/strong&gt; is a technique that gives an LLM access to relevant external information &lt;em&gt;before&lt;/em&gt; it generates an answer — instead of relying purely on what it memorized during training.&lt;/p&gt;

&lt;p&gt;Rather than asking the model to answer from memory, you:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Search a knowledge base for information relevant to the user's question.&lt;/li&gt;
&lt;li&gt;Insert that information into the prompt as context.&lt;/li&gt;
&lt;li&gt;Ask the LLM to answer &lt;em&gt;using that context&lt;/em&gt;.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Think of it like an open-book exam instead of a closed-book one. The model isn't smarter — it just has the right page open in front of it.&lt;/p&gt;




&lt;h2&gt;
  
  
  How a Basic RAG Pipeline Works
&lt;/h2&gt;

&lt;p&gt;A simple RAG system has four moving parts: the user's question, a retrieval step, the LLM, and the final response.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;┌─────────────┐      ┌───────────────┐      ┌─────────────┐      ┌──────────────┐
│  User Query │ ───▶ │   Retrieval    │ ───▶ │     LLM     │ ───▶ │   Response   │
│ "What's our │      │ (search a      │      │ (reads query │      │ (grounded,   │
│  refund     │      │  vector DB /   │      │  + retrieved │      │  hopefully   │
│  policy?"   │      │  document      │      │  chunks)     │      │  accurate)   │
│             │      │  store)        │      │              │      │              │
└─────────────┘      └───────────────┘      └─────────────┘      └──────────────┘
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Behind the "Retrieval" box, a few things usually happen:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Your documents are split into smaller pieces, called &lt;strong&gt;chunks&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;Each chunk is converted into a numeric representation, called an &lt;strong&gt;embedding&lt;/strong&gt;, that captures its meaning.&lt;/li&gt;
&lt;li&gt;These embeddings are stored in a &lt;strong&gt;vector database&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;When a query comes in, it's also converted into an embedding, and the system finds the chunks whose embeddings are most similar — i.e., most semantically relevant.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Those top-matching chunks are inserted into the prompt, and the LLM generates its answer using that grounded context instead of guessing from memory.&lt;/p&gt;




&lt;h2&gt;
  
  
  Where RAG Works Really Well
&lt;/h2&gt;

&lt;p&gt;Before we get into the failure modes, it's worth being clear about where RAG genuinely shines:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Customer support bots&lt;/strong&gt; answering from a product's help docs.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Internal knowledge assistants&lt;/strong&gt; searching company wikis, Slack exports, or Notion pages.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Legal or research assistants&lt;/strong&gt; pulling relevant clauses or papers before summarizing.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Code assistants&lt;/strong&gt; referencing a specific codebase or internal API documentation.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;In all these cases, the answer &lt;em&gt;exists somewhere in a document&lt;/em&gt;, the documents are reasonably well-structured, and the question maps fairly directly to a chunk of text. This is RAG's sweet spot.&lt;/p&gt;

&lt;p&gt;The trouble starts when any of those assumptions break down — which, in the real world, happens constantly.&lt;/p&gt;




&lt;h2&gt;
  
  
  Why RAG Sometimes Gives Incorrect Answers
&lt;/h2&gt;

&lt;p&gt;RAG improves the &lt;em&gt;odds&lt;/em&gt; of a correct answer. It does not guarantee one. Every step in the pipeline — retrieval, chunking, context assembly, and generation — is a place where things can quietly go wrong, and the failure is often invisible until a user catches it.&lt;/p&gt;

&lt;p&gt;Let's go through the biggest culprits.&lt;/p&gt;

&lt;h3&gt;
  
  
  1. Poor Retrieval and Missing Context
&lt;/h3&gt;

&lt;p&gt;The entire system depends on one assumption: that the retrieval step finds the &lt;em&gt;right&lt;/em&gt; information. If it doesn't, everything downstream is built on sand.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Good Retrieval                          Poor Retrieval
───────────────                         ───────────────
Query: "cancellation fee                Query: "cancellation fee
for annual plans"                       for annual plans"

Retrieved chunks:                       Retrieved chunks:
✅ "Annual plan cancellation             ❌ "Monthly plan billing cycle"
    incurs a 10% fee..."                ❌ "How to cancel a free trial"
✅ "Fees are prorated based             ❌ "General refund process
    on remaining months..."                 overview"

Result: Accurate, specific              Result: LLM answers using
answer grounded in the                  loosely related or generic
right policy.                           info — or guesses to fill
                                         the gap.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Retrieval can fail for mundane reasons: the user's phrasing doesn't semantically match the document's wording, the embedding model isn't great at your domain (legal, medical, code), the knowledge base has duplicate or conflicting documents, or the top-k results simply don't contain the answer at all. When that happens, the LLM is left generating a response from incomplete or irrelevant context — and it usually won't tell you that's what happened.&lt;/p&gt;

&lt;h3&gt;
  
  
  2. Poor Chunking and Its Impact on Responses
&lt;/h3&gt;

&lt;p&gt;How you split documents matters more than most people expect. Chunk too small, and you lose surrounding context. Chunk too large, and you dilute relevance and waste context window space.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Original paragraph:
"Refunds are available within 30 days of purchase.
However, annual subscribers are only eligible for a
prorated refund, and enterprise customers must contact
their account manager directly."

Bad chunking (cuts mid-thought):
Chunk A: "Refunds are available within 30 days of purchase.
          However, annual subscribers are only eligible"
Chunk B: "for a prorated refund, and enterprise customers
          must contact their account manager directly."

Good chunking (preserves complete ideas):
Chunk A: "Refunds are available within 30 days of purchase."
Chunk B: "Annual subscribers are only eligible for a
          prorated refund."
Chunk C: "Enterprise customers must contact their account
          manager directly for refunds."
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In the "bad chunking" example, a query about annual refunds might retrieve only Chunk A or only Chunk B — each missing half the relevant sentence. The model then has to guess at the missing piece, and it often guesses wrong, confidently.&lt;/p&gt;

&lt;h3&gt;
  
  
  3. Context Window Limitations
&lt;/h3&gt;

&lt;p&gt;Even with perfect retrieval, there's a ceiling on how much information you can hand the model at once.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Context Window (fixed size)
┌────────────────────────────────────────────────┐
│  System prompt                                  │
│  ────────────────────────                       │
│  Retrieved chunk 1                               │
│  Retrieved chunk 2                               │
│  Retrieved chunk 3                               │
│  ...                                             │
│  Retrieved chunk N   ◄── may get cut off or      │
│                          crowd out other chunks  │
│  ────────────────────────                        │
│  User question                                   │
└────────────────────────────────────────────────┘
        ▲
        └── Fixed capacity. More chunks ≠ better,
            past a certain point — relevant info
            can get buried or squeezed out entirely.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Stuffing in more chunks "just in case" doesn't reliably help. Models can lose track of information buried in the middle of a long context (a well-documented effect sometimes called "lost in the middle"), and irrelevant chunks can actively distract the model from the ones that matter. More context isn't automatically better context.&lt;/p&gt;

&lt;h3&gt;
  
  
  4. Hallucinations Even With RAG
&lt;/h3&gt;

&lt;p&gt;This surprises a lot of people: RAG &lt;em&gt;reduces&lt;/em&gt; hallucination, but it doesn't eliminate it. The LLM can still:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Blend retrieved information with its own pretrained assumptions.&lt;/li&gt;
&lt;li&gt;Fill gaps between two retrieved chunks with a plausible-sounding but invented connection.&lt;/li&gt;
&lt;li&gt;Misread the retrieved context and state something the source doesn't actually say.&lt;/li&gt;
&lt;li&gt;Answer confidently even when the retrieved chunks don't actually contain the answer.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Giving the model the right book doesn't guarantee it reads the right page carefully. Grounding lowers the risk of hallucination — it doesn't remove it.&lt;/p&gt;

&lt;h3&gt;
  
  
  5. Keeping the Knowledge Base Up to Date
&lt;/h3&gt;

&lt;p&gt;RAG is only as good as the data it retrieves from. If your knowledge base isn't maintained, you get confidently wrong answers instead of vague ones:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Outdated documents get retrieved and treated as current fact.&lt;/li&gt;
&lt;li&gt;Deleted or superseded policies still sit in the vector store unless explicitly removed.&lt;/li&gt;
&lt;li&gt;New information isn't available until someone re-runs the ingestion pipeline.&lt;/li&gt;
&lt;li&gt;Conflicting versions of the same document can cause the retriever to pull contradictory chunks for the same query.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Unlike a static LLM, a RAG system needs &lt;em&gt;ongoing operational care&lt;/em&gt; — re-indexing, deduplication, versioning — or its accuracy quietly decays over time.&lt;/p&gt;




&lt;h2&gt;
  
  
  When RAG Is Not the Right Solution
&lt;/h2&gt;

&lt;p&gt;RAG is a fantastic tool, but it's not universal. It tends to struggle when:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;The answer requires reasoning across many documents at once&lt;/strong&gt;, not just retrieving a few relevant snippets (e.g., "summarize trends across our last 12 months of tickets").&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The knowledge base is small enough to just fit in the context window&lt;/strong&gt; — in that case, RAG adds complexity without much benefit.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The task is computational or logical rather than knowledge-based&lt;/strong&gt; — RAG won't help an LLM do arithmetic or multi-step planning correctly.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Precision requirements are extremely high&lt;/strong&gt;, such as medical or legal advice, where a single retrieval miss can be genuinely harmful — these cases usually need stricter verification layers on top of RAG, not RAG alone.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Data changes faster than your ingestion pipeline can keep up with&lt;/strong&gt;, making "up-to-date" retrieval unreliable by design.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;In these situations, alternatives like fine-tuning, structured tool calls to live systems (databases, APIs), or hybrid approaches combining RAG with reasoning agents often serve better than RAG on its own.&lt;/p&gt;




&lt;h2&gt;
  
  
  Wrapping Up
&lt;/h2&gt;

&lt;p&gt;RAG solves a real and important problem: it gives LLMs access to knowledge beyond their training data, grounded in your actual documents. Used well, it's the difference between a chatbot that guesses and one that can point to a source.&lt;/p&gt;

&lt;p&gt;But RAG is a pipeline of independent, fallible steps — retrieval, chunking, context assembly, and generation — and a weak link anywhere in that chain produces a wrong answer that still &lt;em&gt;sounds&lt;/em&gt; right. Understanding where it breaks — poor retrieval, bad chunking, context window limits, residual hallucination, and stale knowledge bases — is what separates a RAG system that's genuinely trustworthy from one that just looks impressive in a demo.&lt;/p&gt;

&lt;p&gt;The goal isn't to avoid RAG. It's to know exactly what you're trusting it to do, and to design around its limits rather than being surprised by them.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;If you're building RAG systems in JavaScript or exploring GenAI tooling, I'd love to hear what failure modes you've run into — drop a comment below.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>genai</category>
      <category>cohort2026</category>
      <category>chaiaurcode</category>
      <category>rag</category>
    </item>
    <item>
      <title>What is Middleware in Express and How It Works</title>
      <dc:creator>Janmejai Singh</dc:creator>
      <pubDate>Tue, 30 Jun 2026 16:59:04 +0000</pubDate>
      <link>https://dev.to/janmejaisingh/what-is-middleware-in-express-and-how-it-works-12ml</link>
      <guid>https://dev.to/janmejaisingh/what-is-middleware-in-express-and-how-it-works-12ml</guid>
      <description>&lt;h1&gt;
  
  
  What Is Middleware in Express, and How Does It Actually Work?
&lt;/h1&gt;

&lt;p&gt;If you've spent any time with Express.js, you've probably written code like &lt;code&gt;app.use(something)&lt;/code&gt; dozens of times without fully stopping to ask: what &lt;em&gt;is&lt;/em&gt; that, exactly? It's not a route. It's not quite a controller. It just... sits there, quietly doing something to every request that passes through.&lt;/p&gt;

&lt;p&gt;That "something" is &lt;strong&gt;middleware&lt;/strong&gt;, and it's arguably the single most important concept to understand if you want Express to stop feeling like a black box. Once it clicks, you'll start seeing the entire framework differently — as a pipeline you control, one checkpoint at a time.&lt;/p&gt;

&lt;h2&gt;
  
  
  Middleware as a Checkpoint Between Request and Response
&lt;/h2&gt;

&lt;p&gt;Here's the simplest way to picture it: imagine an airport. A passenger (the &lt;strong&gt;request&lt;/strong&gt;) doesn't walk straight from the front door to their airplane seat (the &lt;strong&gt;response&lt;/strong&gt;). They pass through a series of &lt;strong&gt;checkpoints&lt;/strong&gt; along the way — check-in, security screening, passport control, boarding gate. Each checkpoint inspects, modifies, logs, or sometimes outright stops the passenger before letting them continue to the next one.&lt;/p&gt;

&lt;p&gt;Middleware in Express is exactly that: a function sitting between the incoming request and the final response, given a chance to inspect or modify things, run some logic, and then either pass the request along to the next checkpoint or stop it right there.&lt;/p&gt;

&lt;p&gt;Formally, an Express middleware function is just a function with this shape:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;myMiddleware&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;next&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="c1"&gt;// do something with req or res&lt;/span&gt;
  &lt;span class="nf"&gt;next&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt; &lt;span class="c1"&gt;// pass control to the next checkpoint&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Three ingredients, every time: the &lt;strong&gt;request&lt;/strong&gt; object, the &lt;strong&gt;response&lt;/strong&gt; object, and a special function called &lt;code&gt;next&lt;/code&gt;. That third one is doing more work than it looks like, and we'll come back to it shortly.&lt;/p&gt;

&lt;h2&gt;
  
  
  Where Middleware Sits in the Request Lifecycle
&lt;/h2&gt;

&lt;p&gt;When a request hits your Express app, it doesn't go straight to a route handler. It travels through a &lt;strong&gt;pipeline&lt;/strong&gt; — a sequence of middleware functions, registered in the order you defined them, each one getting a turn before the request (assuming it survives that long) finally reaches the route handler responsible for sending a response.&lt;/p&gt;

&lt;p&gt;Picture the flow like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Incoming Request
      |
      v
 [ Middleware 1 ]  --(calls next())--&amp;gt;
      |
      v
 [ Middleware 2 ]  --(calls next())--&amp;gt;
      |
      v
 [ Route Handler ] --(sends response)
      |
      v
   Response sent back to client
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is why Express is often described as having a &lt;strong&gt;request pipeline&lt;/strong&gt; — your request flows through a chain of functions, and each one gets to decide: keep going, stop here, or branch off entirely (say, by sending an error response early).&lt;/p&gt;

&lt;h2&gt;
  
  
  Types of Middleware in Express
&lt;/h2&gt;

&lt;p&gt;Not all middleware is registered the same way or applies to the same scope. Express groups middleware into a few practical categories.&lt;/p&gt;

&lt;h3&gt;
  
  
  Application-Level Middleware
&lt;/h3&gt;

&lt;p&gt;This is middleware attached directly to your main &lt;code&gt;app&lt;/code&gt; object using &lt;code&gt;app.use()&lt;/code&gt; or &lt;code&gt;app.METHOD()&lt;/code&gt; (like &lt;code&gt;app.get()&lt;/code&gt;). It applies broadly — often to &lt;em&gt;every&lt;/em&gt; request that comes into your app, unless you scope it to a specific path.&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;express&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;express&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;app&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;express&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

&lt;span class="nx"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;use&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;next&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;method&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt; request to &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;req&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="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="nf"&gt;next&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 is your go-to for things that should happen on basically every request — logging, setting common headers, parsing request bodies, and so on.&lt;/p&gt;

&lt;h3&gt;
  
  
  Router-Level Middleware
&lt;/h3&gt;

&lt;p&gt;Sometimes you don't want middleware applying app-wide — you want it scoped to a specific group of routes. That's where &lt;code&gt;express.Router()&lt;/code&gt; comes in. You attach middleware to a router instance instead of the whole app, and it only runs for requests handled by that router.&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;router&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;express&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Router&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

&lt;span class="nx"&gt;router&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;use&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;next&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="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="s1"&gt;A request hit the /users router&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="nf"&gt;next&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="nx"&gt;router&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/users/:id&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;req&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;send&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;User profile&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is especially handy for feature-specific logic — like requiring authentication only on &lt;code&gt;/admin&lt;/code&gt; routes, without affecting your public-facing pages at all.&lt;/p&gt;

&lt;h3&gt;
  
  
  Built-In Middleware
&lt;/h3&gt;

&lt;p&gt;Express also ships with a small set of middleware functions out of the box, so you don't have to reinvent common functionality. The most commonly used one is &lt;code&gt;express.json()&lt;/code&gt;, which parses incoming JSON request bodies and makes them available on &lt;code&gt;req.body&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="nx"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;use&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;express&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;There's also &lt;code&gt;express.static()&lt;/code&gt;, which serves static files (images, CSS, client-side JS) directly from a folder, without you needing to write any custom file-serving logic yourself.&lt;/p&gt;

&lt;h2&gt;
  
  
  Execution Order of Middleware
&lt;/h2&gt;

&lt;p&gt;This part trips up a lot of beginners, so it's worth saying plainly: &lt;strong&gt;middleware runs in the exact order you register it, top to bottom.&lt;/strong&gt; Express doesn't reorder, prioritize, or guess — it simply walks down your stack of &lt;code&gt;app.use()&lt;/code&gt; and route definitions, one at a time.&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;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;use&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;next&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="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="s1"&gt;1: Logger&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="nf"&gt;next&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="nx"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;use&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;next&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="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="s1"&gt;2: Auth check&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="nf"&gt;next&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="nx"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/dashboard&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;req&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="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="s1"&gt;3: Route handler&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;send&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Welcome to your dashboard&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;For a request to &lt;code&gt;/dashboard&lt;/code&gt;, the console output will always read &lt;code&gt;1&lt;/code&gt;, then &lt;code&gt;2&lt;/code&gt;, then &lt;code&gt;3&lt;/code&gt; — never out of order. This is exactly like the airport checkpoints: you can't reach passport control before you've gone through security, simply because of the order the checkpoints are physically placed in.&lt;/p&gt;

&lt;p&gt;This ordering matters a lot in practice. If you register a body-parsing middleware &lt;em&gt;after&lt;/em&gt; a route handler that depends on &lt;code&gt;req.body&lt;/code&gt;, that route will see &lt;code&gt;undefined&lt;/code&gt; — because the parser never got a chance to run first.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Role of &lt;code&gt;next()&lt;/code&gt;
&lt;/h2&gt;

&lt;p&gt;Now, back to that mysterious third argument. The &lt;code&gt;next()&lt;/code&gt; function is what makes the pipeline actually move. Calling &lt;code&gt;next()&lt;/code&gt; tells Express: "I'm done with my part — pass this request on to whatever's next in line."&lt;/p&gt;

&lt;p&gt;If a middleware function &lt;strong&gt;never calls &lt;code&gt;next()&lt;/code&gt;&lt;/strong&gt; (and never sends a response either), the request simply hangs forever. The client's browser or app sits there waiting, because nothing ever told Express to move on or respond.&lt;/p&gt;

&lt;p&gt;This gives middleware real power, though — it can also choose &lt;strong&gt;not&lt;/strong&gt; to call &lt;code&gt;next()&lt;/code&gt;, and instead end the chain itself:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;requireAuth&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;next&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;headers&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;authorization&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;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;status&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;401&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;send&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Unauthorized&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// chain stops here&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="nf"&gt;next&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt; &lt;span class="c1"&gt;// only continues if authorized&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is the checkpoint analogy in its purest form: a security checkpoint that decides a passenger isn't allowed through simply doesn't let them proceed — it stops them right there, instead of waving them on to the gate.&lt;/p&gt;

&lt;h2&gt;
  
  
  Real-World Examples of Middleware in Action
&lt;/h2&gt;

&lt;p&gt;Theory aside, here's where middleware earns its keep in almost every real Express app.&lt;/p&gt;

&lt;h3&gt;
  
  
  Logging
&lt;/h3&gt;

&lt;p&gt;Tracking what's happening in your app — which routes are getting hit, by what method, when — is a textbook middleware job, since it needs to run on every request without cluttering up your actual route logic.&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;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;use&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;next&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`[&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Date&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;toISOString&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;req&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;method&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;req&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="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="nf"&gt;next&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;h3&gt;
  
  
  Authentication
&lt;/h3&gt;

&lt;p&gt;Checking whether a user is logged in (or has the right permissions) before letting them reach a protected route is one of the most common real-world uses of middleware, exactly like the &lt;code&gt;requireAuth&lt;/code&gt; example above. Instead of repeating that check inside every single route handler, you write it once and apply it wherever it's needed.&lt;/p&gt;

&lt;h3&gt;
  
  
  Request Validation
&lt;/h3&gt;

&lt;p&gt;Before your route handler even touches incoming data, middleware can check that it's actually shaped the way you expect — required fields present, correct types, no garbage data sneaking through.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;validateSignup&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;next&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="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;password&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;body&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;email&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;password&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;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;status&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;400&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;send&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Email and password are required&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="nf"&gt;next&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nx"&gt;app&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;/signup&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;validateSignup&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;send&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Account created!&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Notice that last example — middleware doesn't only get registered with &lt;code&gt;app.use()&lt;/code&gt;. You can also slot it directly into a single route's argument list, applying it to just that one path.&lt;/p&gt;

&lt;h2&gt;
  
  
  Visualizing It All Together
&lt;/h2&gt;

&lt;p&gt;Put the pieces together, and a typical request through a well-structured Express app looks like a chain:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Request
  → Logging middleware (records the request)
  → express.json() (parses the body)
  → Auth middleware (checks if user is allowed through)
  → Validation middleware (checks the data shape)
  → Route handler (does the actual work, sends response)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Each link in that chain does exactly one job, calls &lt;code&gt;next()&lt;/code&gt; when it's satisfied, and trusts the next link to handle its own responsibility. That's the real elegance of middleware — it lets you break a complex request-handling process into small, focused, reusable pieces instead of one giant tangled route handler trying to do everything at once.&lt;/p&gt;

&lt;h2&gt;
  
  
  Wrapping Up
&lt;/h2&gt;

&lt;p&gt;Middleware is Express's way of turning request handling into a pipeline of checkpoints rather than one big monolithic function. Application-level middleware applies broadly, router-level middleware applies to a specific group of routes, and built-in middleware handles common needs out of the box — and all of it runs strictly in the order you register it, moving forward only when &lt;code&gt;next()&lt;/code&gt; is called. Once you start thinking in terms of "what checkpoint does this logic belong at," patterns like logging, authentication, and validation stop feeling like separate, special-case features and start feeling like exactly what they are: just more middleware in the chain.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Curious about error-handling middleware, or how middleware works differently in newer frameworks built on Express? Drop a comment below — I read every one.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>node</category>
      <category>express</category>
      <category>webdev</category>
      <category>javascript</category>
    </item>
    <item>
      <title>Sessions vs JWT vs Cookies: Understanding Authentication Approaches</title>
      <dc:creator>Janmejai Singh</dc:creator>
      <pubDate>Tue, 30 Jun 2026 16:52:31 +0000</pubDate>
      <link>https://dev.to/janmejaisingh/sessions-vs-jwt-vs-cookies-understanding-authentication-approaches-m60</link>
      <guid>https://dev.to/janmejaisingh/sessions-vs-jwt-vs-cookies-understanding-authentication-approaches-m60</guid>
      <description>&lt;h1&gt;
  
  
  Sessions vs JWT vs Cookies: Understanding Authentication Approaches
&lt;/h1&gt;

&lt;p&gt;Every app with a login page has to solve the same quiet little problem over and over: a user logs in once, but HTTP itself has no memory. Every request is a stranger knocking on the door, with no idea if it's the same person who knocked five seconds ago. So how does your app remember "yep, this is still that logged-in user" across dozens of requests?&lt;/p&gt;

&lt;p&gt;That's the whole job of authentication systems built around &lt;strong&gt;sessions&lt;/strong&gt;, &lt;strong&gt;cookies&lt;/strong&gt;, and &lt;strong&gt;JWTs&lt;/strong&gt; — three terms that get thrown around together so often that beginners (reasonably) assume they're competing options. They're not quite that simple. Let's untangle them one at a time, then see how they actually relate to each other.&lt;/p&gt;

&lt;h2&gt;
  
  
  What Cookies Are
&lt;/h2&gt;

&lt;p&gt;Let's start with the simplest piece: a &lt;strong&gt;cookie&lt;/strong&gt; is just a small piece of data that a server asks the browser to store, and the browser then automatically sends back with every future request to that same site.&lt;/p&gt;

&lt;p&gt;Think of a cookie like a &lt;strong&gt;wristband at a festival&lt;/strong&gt;. Once you get scanned at the entrance, staff snap a wristband on you. You don't have to show your ticket again at every stage — you just flash the wristband, and every checkpoint trusts it. The browser is the wrist wearing it, and it shows that wristband (the cookie) automatically on every request, without you doing anything manually.&lt;/p&gt;

&lt;p&gt;Cookies themselves don't decide &lt;em&gt;who you are&lt;/em&gt; — they're just a delivery mechanism. What actually goes inside that cookie is where sessions and JWTs come in.&lt;/p&gt;

&lt;h2&gt;
  
  
  What Sessions Are
&lt;/h2&gt;

&lt;p&gt;A &lt;strong&gt;session&lt;/strong&gt; is server-side memory about a logged-in user. When you log in successfully, the server creates a record — somewhere in its memory, a database, or a cache like Redis — that essentially says, "this specific session ID belongs to this specific user, and they're authenticated."&lt;/p&gt;

&lt;p&gt;The server then sends a cookie back to the browser containing nothing more than that session ID — a random, meaningless-looking string. On every future request, the browser sends that cookie back, and the server looks up the ID in its own storage to figure out who's making the request.&lt;/p&gt;

&lt;p&gt;Going back to the festival analogy: the wristband itself (the cookie) doesn't have your name printed on it. It just has a barcode. The festival staff (the server) scan that barcode and look it up in &lt;em&gt;their own system&lt;/em&gt; to pull up your details. The wristband is meaningless without the server's internal records.&lt;/p&gt;

&lt;p&gt;This is why session-based authentication is described as &lt;strong&gt;stateful&lt;/strong&gt; — the server has to actively hold onto state (the session data) for every logged-in user.&lt;/p&gt;

&lt;h2&gt;
  
  
  What JWT Tokens Are
&lt;/h2&gt;

&lt;p&gt;A &lt;strong&gt;JWT (JSON Web Token)&lt;/strong&gt; takes a fundamentally different approach. Instead of the server storing user info and handing out a meaningless reference ID, a JWT packs the actual user information directly into the token itself — and then &lt;strong&gt;cryptographically signs&lt;/strong&gt; it, so the server can later verify the token hasn't been tampered with, without needing to look anything up in storage.&lt;/p&gt;

&lt;p&gt;A JWT typically looks like a long string with three dot-separated parts (header, payload, signature), and that payload often contains things like a user ID, role, or expiration time — readable information, not a random reference number.&lt;/p&gt;

&lt;p&gt;Here's the analogy shift: instead of a wristband with a barcode the staff have to scan and look up, imagine a &lt;strong&gt;laminated ID badge&lt;/strong&gt; that already has your name, your access level, and a tamper-proof hologram printed directly on it. Anyone with a badge scanner can verify it's authentic just by checking the hologram — no need to call back to a central database to ask "who is this, again?"&lt;/p&gt;

&lt;p&gt;That's the core idea behind JWTs being &lt;strong&gt;stateless&lt;/strong&gt; — the server doesn't need to store anything about your session. The token carries everything it needs, and the server just verifies the signature is valid.&lt;/p&gt;

&lt;h2&gt;
  
  
  Stateful vs Stateless Authentication
&lt;/h2&gt;

&lt;p&gt;This stateful-vs-stateless distinction is really the heart of the whole comparison, so it's worth sitting with it directly.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Stateful authentication&lt;/strong&gt; (the session approach) means the server is the source of truth, and it actively remembers every logged-in user. Want to instantly log someone out, everywhere, right now? Easy — just delete their session record from storage, and that session ID becomes worthless immediately.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Stateless authentication&lt;/strong&gt; (the JWT approach) means the server doesn't remember anything between requests. Each token is self-contained proof of identity, verified purely through its cryptographic signature. This is great for scaling — any server in a cluster can verify a token without needing shared access to a central session store — but it comes with a trade-off: since the server isn't tracking active tokens, instantly revoking a single token before it naturally expires is genuinely awkward, and usually requires extra infrastructure (like a blocklist) that partially undoes the "stateless" benefit.&lt;/p&gt;

&lt;h2&gt;
  
  
  Session-Based Auth vs JWT: A Side-by-Side Comparison
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;&lt;/th&gt;
&lt;th&gt;&lt;strong&gt;Session-Based Auth&lt;/strong&gt;&lt;/th&gt;
&lt;th&gt;&lt;strong&gt;JWT-Based Auth&lt;/strong&gt;&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Where user data lives&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Stored server-side (memory, database, or cache)&lt;/td&gt;
&lt;td&gt;Packed directly inside the token itself&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;What the cookie holds&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;A random session ID (meaningless on its own)&lt;/td&gt;
&lt;td&gt;Often the full token, or a reference to it&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;State&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Stateful — server actively tracks sessions&lt;/td&gt;
&lt;td&gt;Stateless — server verifies, doesn't store&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Scaling across multiple servers&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Needs a shared session store (e.g., Redis) so every server can look up the same session&lt;/td&gt;
&lt;td&gt;Easy — any server can verify the token's signature independently&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Revoking access instantly&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Simple — delete the session record&lt;/td&gt;
&lt;td&gt;Harder — token stays valid until it expires, unless you add a blocklist&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Typical size sent per request&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Small (just an ID)&lt;/td&gt;
&lt;td&gt;Larger (full encoded payload)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Common use case&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Traditional web apps with server-rendered pages&lt;/td&gt;
&lt;td&gt;APIs, mobile apps, microservices, single-page apps&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h2&gt;
  
  
  When to Use Each Method
&lt;/h2&gt;

&lt;p&gt;Neither approach is "better" in some absolute sense — they fit different shapes of application.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Reach for session-based authentication when:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;You're building a traditional server-rendered web app (think: a classic Express + EJS/Handlebars app, or a monolith).&lt;/li&gt;
&lt;li&gt;You need the ability to instantly revoke a user's access — banning a user, forcing a logout after a password change, etc.&lt;/li&gt;
&lt;li&gt;Your app runs on infrastructure where setting up a shared session store (like Redis) isn't a big lift.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Reach for JWT-based authentication when:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;You're building an API consumed by multiple clients — a mobile app, a single-page app, third-party integrations — where a shared session store would add friction.&lt;/li&gt;
&lt;li&gt;Your backend is split across multiple independent services (microservices) that all need to verify identity without constantly calling back to one central auth server.&lt;/li&gt;
&lt;li&gt;Instant revocation isn't a hard requirement, and short token expiration times (paired with refresh tokens) are an acceptable trade-off.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;A lot of modern full-stack apps actually blend the two: short-lived JWTs for fast, stateless verification, paired with a refresh-token mechanism that behaves a lot like a session behind the scenes. Understanding &lt;em&gt;why&lt;/em&gt; each piece exists makes those hybrid setups far less confusing the next time you encounter one in a real codebase.&lt;/p&gt;

&lt;h2&gt;
  
  
  Visualizing the Two Flows
&lt;/h2&gt;

&lt;p&gt;It helps to picture both flows side by side:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Session authentication flow:&lt;/strong&gt; User logs in → server creates a session record and stores it → server sends back a cookie containing just the session ID → on every future request, the browser sends that cookie → server looks up the ID in its storage → server confirms identity and responds.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;JWT authentication flow:&lt;/strong&gt; User logs in → server creates a signed token containing user info → server sends the token to the client → client stores it and attaches it to future requests (commonly via an &lt;code&gt;Authorization&lt;/code&gt; header) → server verifies the token's signature directly, with no storage lookup needed → server trusts the payload and responds.&lt;/p&gt;

&lt;p&gt;The key visual difference: the session flow has a constant "go check the storage" step on every request. The JWT flow skips that step entirely, trading a storage lookup for a cryptographic verification.&lt;/p&gt;

&lt;h2&gt;
  
  
  Wrapping Up
&lt;/h2&gt;

&lt;p&gt;Cookies are just the delivery truck — they carry data between browser and server automatically. What rides inside that truck is the real decision: a meaningless session ID that points back to server-side memory (sessions, stateful), or a self-contained, signed token carrying its own proof of identity (JWT, stateless). Neither one is the "modern correct" choice over the other — they're tools suited to different architectures, and plenty of production systems lean on both at once.&lt;/p&gt;

&lt;p&gt;Next time you're scaffolding auth for a new project, the real question isn't "sessions or JWT?" in the abstract — it's "does this app need a single source of truth the server can revoke instantly, or does it need to scale independently across services without a shared store?" That question alone will point you toward the right answer.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Curious about refresh tokens, or how OAuth fits into all of this? Drop a comment below — I read every one.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>node</category>
      <category>webdev</category>
      <category>authentication</category>
      <category>beginners</category>
    </item>
    <item>
      <title>DNS Record Types Explained</title>
      <dc:creator>Janmejai Singh</dc:creator>
      <pubDate>Tue, 30 Jun 2026 16:11:18 +0000</pubDate>
      <link>https://dev.to/janmejaisingh/dns-record-types-explained-178</link>
      <guid>https://dev.to/janmejaisingh/dns-record-types-explained-178</guid>
      <description>&lt;h1&gt;
  
  
  DNS Record Types Explained: The Phonebook Behind Every Website You Visit
&lt;/h1&gt;

&lt;p&gt;Here's a question that sounds simple but trips up a lot of beginner developers: when you type &lt;code&gt;example.com&lt;/code&gt; into your browser, how does it actually know which server, sitting in some data center halfway across the world, to talk to?&lt;/p&gt;

&lt;p&gt;The answer is &lt;strong&gt;DNS&lt;/strong&gt; — the Domain Name System — and once you understand it, a whole bunch of "mysterious" web dev concepts (deploying a site, setting up email, verifying a domain for Google or AWS) suddenly stop feeling like magic.&lt;/p&gt;

&lt;p&gt;Let's walk through it slowly, one piece at a time.&lt;/p&gt;

&lt;h2&gt;
  
  
  What DNS Is (In Very Simple Terms)
&lt;/h2&gt;

&lt;p&gt;Computers don't actually use names like &lt;code&gt;example.com&lt;/code&gt; to find each other. Underneath everything, they use numbers — &lt;strong&gt;IP addresses&lt;/strong&gt; — like &lt;code&gt;192.0.2.10&lt;/code&gt;. That's how machines on a network identify each other.&lt;/p&gt;

&lt;p&gt;But humans are terrible at remembering strings of numbers, and great at remembering names. So DNS exists as a translation layer: you type a friendly name, and DNS quietly converts it into the numeric address a computer needs.&lt;/p&gt;

&lt;p&gt;Think of DNS as &lt;strong&gt;the phonebook of the internet&lt;/strong&gt;. You don't memorize your friend's phone number — you look up their name in your contacts, and the phone handles dialing the actual number behind the scenes. DNS does exactly that, except the "contact" is a domain name and the "phone number" is an IP address.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why DNS Records Are Needed
&lt;/h2&gt;

&lt;p&gt;Knowing &lt;em&gt;that&lt;/em&gt; DNS translates names to addresses is only half the picture. The next question is: translates to &lt;em&gt;what&lt;/em&gt;, exactly?&lt;/p&gt;

&lt;p&gt;A single domain isn't just "one address." A real website needs to answer several different questions at once:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Where do I send visitors who type this domain into a browser?&lt;/li&gt;
&lt;li&gt;Where do I send emails addressed to this domain?&lt;/li&gt;
&lt;li&gt;Who is allowed to manage this domain's settings?&lt;/li&gt;
&lt;li&gt;How can a third-party service (like Google Search Console) verify I actually own this domain?&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Each of these questions is answered by a different &lt;strong&gt;DNS record&lt;/strong&gt;. A DNS record is a small instruction stored in a domain's configuration, written in a specific format, that tells the internet how to handle one particular kind of request for that domain.&lt;/p&gt;

&lt;p&gt;A domain almost never has just one record — it has a small collection of them, each doing a specific job. Let's meet them one at a time.&lt;/p&gt;

&lt;h2&gt;
  
  
  NS Record: Who's Responsible for This Domain?
&lt;/h2&gt;

&lt;p&gt;Before any other record matters, something has to answer the question: "Where do I even go to look up records for &lt;code&gt;example.com&lt;/code&gt;?"&lt;/p&gt;

&lt;p&gt;That's what an &lt;strong&gt;NS (Name Server) record&lt;/strong&gt; does. It points to the specific servers that are authoritative for a domain — meaning, those servers hold the master copy of all the other DNS records for that domain.&lt;/p&gt;

&lt;p&gt;A real-life comparison: think of NS records as the &lt;strong&gt;listing in a master directory&lt;/strong&gt; that tells you which local post office handles mail for a particular street. Before the mailman can deliver anything, he first needs to know which post office is responsible for that address. NS records are that "which post office" answer for a domain.&lt;/p&gt;

&lt;p&gt;When you buy a domain and connect it to a hosting provider or DNS service (like Cloudflare or Route 53), you're typically updating its NS records to point at that provider's name servers.&lt;/p&gt;

&lt;h2&gt;
  
  
  A Record: Domain → IPv4 Address
&lt;/h2&gt;

&lt;p&gt;This is the record most people picture when they think "DNS." An &lt;strong&gt;A Record&lt;/strong&gt; maps a domain name directly to an &lt;strong&gt;IPv4 address&lt;/strong&gt; — something like &lt;code&gt;203.0.113.25&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Going back to the phonebook idea: if NS records tell you which post office handles a street, the A record is the actual &lt;strong&gt;house address&lt;/strong&gt; on that street. It's the most direct, literal mapping: "this name = this numeric address, go here."&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;example.com.    A    203.0.113.25
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;When someone types &lt;code&gt;example.com&lt;/code&gt; into their browser, this is very often the record that ultimately tells the browser which server to connect to.&lt;/p&gt;

&lt;h2&gt;
  
  
  AAAA Record: Domain → IPv6 Address
&lt;/h2&gt;

&lt;p&gt;The internet originally ran on IPv4 addresses, but there are only so many of those to go around — about 4.3 billion, which sounds like a lot until you count every phone, laptop, server, and smart fridge on Earth. &lt;strong&gt;IPv6&lt;/strong&gt; was created as a much larger address space to solve that shortage.&lt;/p&gt;

&lt;p&gt;An &lt;strong&gt;AAAA Record&lt;/strong&gt; does exactly what an A record does, except it maps a domain to an &lt;strong&gt;IPv6 address&lt;/strong&gt; instead — something like &lt;code&gt;2001:0db8:85a3::8a2e:0370:7334&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Think of it as having two valid addresses for the same house: an old-style address (IPv4/A record) and a newer-format one (IPv6/AAAA record). Modern networks increasingly prefer IPv6, so many websites maintain both an A and an AAAA record pointing to the same server, just in two different addressing formats.&lt;/p&gt;

&lt;h2&gt;
  
  
  CNAME Record: One Name Pointing to Another Name
&lt;/h2&gt;

&lt;p&gt;Sometimes you don't want to give out a raw IP address at all — you just want to say "this name is really just another name, go look that one up instead." That's what a &lt;strong&gt;CNAME (Canonical Name) Record&lt;/strong&gt; does.&lt;/p&gt;

&lt;p&gt;A CNAME doesn't point to an IP address. It points to &lt;em&gt;another domain name&lt;/em&gt;, which then gets resolved on its own (often eventually landing on an A or AAAA record).&lt;/p&gt;

&lt;p&gt;A relatable analogy: imagine your friend has moved, and instead of giving everyone their new house address directly, they just say, "Mail addressed to my old house? Forward it to my new name in the system, and look that up instead." A CNAME is that forwarding instruction — one name redirecting to another name.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;www.example.com.    CNAME    example.com.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is incredibly common for things like &lt;code&gt;www.example.com&lt;/code&gt; pointing to &lt;code&gt;example.com&lt;/code&gt;, or a custom domain pointing to a service like &lt;code&gt;myshop.shopify.com&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;A common point of confusion:&lt;/strong&gt; &lt;em&gt;A record vs. CNAME.&lt;/em&gt; The simplest way to remember the difference is what's on the right-hand side of the record. An A record always points to an &lt;strong&gt;IP address&lt;/strong&gt; (a number). A CNAME always points to &lt;strong&gt;another domain name&lt;/strong&gt; (text). One is "go to this address," the other is "go ask this other name what its address is."&lt;/p&gt;

&lt;h2&gt;
  
  
  MX Record: How Emails Find Your Mail Server
&lt;/h2&gt;

&lt;p&gt;Visiting a website and receiving an email for that domain are two completely separate jobs — and DNS handles them with two completely separate kinds of records. &lt;strong&gt;MX (Mail Exchange) Records&lt;/strong&gt; are dedicated specifically to email routing: they tell the internet which mail server is responsible for accepting email sent to addresses at that domain.&lt;/p&gt;

&lt;p&gt;Picture it like this: your house has a street address where guests come to visit (that's your A record), but your &lt;em&gt;mail&lt;/em&gt; doesn't get handed directly to you at the door — it goes through a &lt;strong&gt;post office&lt;/strong&gt; that's specifically responsible for sorting and delivering mail for your area. MX records are how the internet finds that post office for your domain.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;example.com.    MX    10 mail.example.com.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That number before the mail server name (&lt;code&gt;10&lt;/code&gt; here) is a &lt;strong&gt;priority&lt;/strong&gt; — if a domain lists multiple mail servers, lower numbers are tried first, with others as backups.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Another common point of confusion:&lt;/strong&gt; &lt;em&gt;NS vs. MX.&lt;/em&gt; NS records answer "who manages this domain's DNS settings overall?" MX records answer a much narrower question: "where should email for this domain be delivered?" A domain can have its DNS managed by one company (NS) while its email is hosted entirely somewhere else, like Google Workspace (MX) — they're independent of each other.&lt;/p&gt;

&lt;h2&gt;
  
  
  TXT Record: Extra Information and Verification
&lt;/h2&gt;

&lt;p&gt;Not every DNS record is about routing traffic somewhere. Sometimes a domain just needs to &lt;strong&gt;say something&lt;/strong&gt; — store a small piece of text that other systems can check. That's the job of a &lt;strong&gt;TXT (Text) Record&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;TXT records are extremely flexible. The two most common real-world uses are:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Domain ownership verification&lt;/strong&gt; — when you connect a domain to a service like Google Search Console, Vercel, or AWS, they'll often ask you to add a TXT record containing a specific code. This proves you actually control the domain's DNS settings, not just that you're claiming to.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Email security&lt;/strong&gt; — records like &lt;strong&gt;SPF&lt;/strong&gt; and &lt;strong&gt;DKIM&lt;/strong&gt;, which are technically just specially formatted TXT records, help receiving mail servers verify that an email claiming to be from your domain is actually legitimate, cutting down on spoofing and spam.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Think of a TXT record as a &lt;strong&gt;sticky note pinned to the front door&lt;/strong&gt; that says "yes, I really do live here" or "only trust mail that's been stamped this particular way." It's not directing traffic anywhere — it's just providing proof or extra context.&lt;/p&gt;

&lt;h2&gt;
  
  
  How All These Records Work Together for One Website
&lt;/h2&gt;

&lt;p&gt;Here's where it all clicks into place. A single, fully functioning domain — say, a small business website with email — typically relies on &lt;em&gt;all&lt;/em&gt; of these records working as a team:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;NS records&lt;/strong&gt; point to the name servers responsible for managing &lt;code&gt;example.com&lt;/code&gt;'s DNS settings.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;A record&lt;/strong&gt; maps &lt;code&gt;example.com&lt;/code&gt; to the IPv4 address of the web server hosting the site.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;AAAA record&lt;/strong&gt; maps the same domain to an IPv6 address, for visitors on modern networks.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;CNAME record&lt;/strong&gt; makes &lt;code&gt;www.example.com&lt;/code&gt; simply forward to &lt;code&gt;example.com&lt;/code&gt;, so both versions of the URL work.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;MX record&lt;/strong&gt; tells the internet that email for &lt;code&gt;@example.com&lt;/code&gt; should be delivered to a specific mail server (maybe Google Workspace or a custom mail host).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;TXT records&lt;/strong&gt; sit quietly in the background, proving domain ownership to third-party services and helping authenticate outgoing email.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;None of these records "compete" with each other — each one answers a different question, and together they form the complete operating instructions for a domain. A browser visit uses the A/AAAA (and possibly CNAME) records. An incoming email uses the MX record. A third-party integration check uses a TXT record. And underneath it all, the NS record made sure everyone was looking in the right place to begin with.&lt;/p&gt;

&lt;h2&gt;
  
  
  Wrapping Up
&lt;/h2&gt;

&lt;p&gt;DNS can feel intimidating because of all the acronyms, but at its core it's just a structured phonebook with a few specialized entry types: NS says who's in charge, A and AAAA give the literal address, CNAME forwards one name to another, MX routes email, and TXT holds extra proof or instructions. Once you can picture each record's specific job — rather than treating DNS as one big mysterious black box — configuring a domain, debugging a "website not loading" issue, or setting up email for a custom domain becomes a lot less scary.&lt;/p&gt;

&lt;p&gt;Next time you register a domain and stare at a confusing DNS dashboard full of records, you'll know exactly what each row is actually doing — and why it's there.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Got a DNS record type you'd like explained next — SRV, CAA, or PTR maybe? Drop a comment below, I read every one.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>dns</category>
      <category>webdev</category>
      <category>networking</category>
      <category>beginners</category>
    </item>
    <item>
      <title>Async Code in Node.js: Callbacks and Promises</title>
      <dc:creator>Janmejai Singh</dc:creator>
      <pubDate>Tue, 30 Jun 2026 16:05:00 +0000</pubDate>
      <link>https://dev.to/janmejaisingh/async-code-in-nodejs-callbacks-and-promises-598h</link>
      <guid>https://dev.to/janmejaisingh/async-code-in-nodejs-callbacks-and-promises-598h</guid>
      <description>&lt;h1&gt;
  
  
  Async Code in Node.js: Callbacks and Promises (And Why You Should Care)
&lt;/h1&gt;

&lt;p&gt;Picture this: you ask a friend to grab coffee while you keep working on your laptop. You don't freeze mid-sentence waiting for them to come back — you just keep typing, and when they return, you grab the cup. That's basically what Node.js does with slow operations like reading files, querying databases, or hitting an API. It doesn't sit around twiddling its thumbs. It keeps the line moving.&lt;/p&gt;

&lt;p&gt;This is the heart of &lt;strong&gt;asynchronous programming&lt;/strong&gt; in Node.js, and once it clicks, a lot of confusing behavior in your code suddenly makes sense.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why Async Code Exists in Node.js
&lt;/h2&gt;

&lt;p&gt;Node.js runs JavaScript on a &lt;strong&gt;single thread&lt;/strong&gt;. That single thread is also responsible for handling every incoming request in your app. If that one thread ever got blocked — say, waiting three seconds for a file to load from disk — &lt;em&gt;every other user&lt;/em&gt; of your app would also have to wait those three seconds, even if their request had nothing to do with that file.&lt;/p&gt;

&lt;p&gt;That's a death sentence for a server.&lt;/p&gt;

&lt;p&gt;So Node.js takes a different approach: instead of blocking the thread while waiting on a slow operation (disk I/O, network calls, timers, database queries), it hands the operation off to the system, keeps doing other work, and gets notified later when the result is ready. This is possible thanks to the &lt;strong&gt;event loop&lt;/strong&gt;, which constantly checks: "Is there a task waiting to be picked back up?"&lt;/p&gt;

&lt;p&gt;The result: a single Node.js process can juggle thousands of concurrent operations without spinning up thousands of threads. That's the whole reason async code exists — it's what makes Node.js fast and scalable despite being single-threaded.&lt;/p&gt;

&lt;h2&gt;
  
  
  Callback-Based Async Execution
&lt;/h2&gt;

&lt;p&gt;Let's make this concrete with a classic scenario: &lt;strong&gt;reading a file from disk&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Reading a file isn't instant — the disk has to locate the data and stream it back. Node.js doesn't want your entire app to freeze while that happens, so the original solution baked into Node's core was the &lt;strong&gt;callback&lt;/strong&gt;: a function you hand over and say, "run this once you're done."&lt;br&gt;
&lt;/p&gt;

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

&lt;span class="nx"&gt;fs&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;readFile&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;data.txt&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;utf8&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;err&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;err&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;error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Something went wrong:&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;err&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="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="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;File contents:&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="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="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;This runs before the file is read!&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;Walking through this step by step:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;code&gt;fs.readFile&lt;/code&gt; is called and immediately returns — it does &lt;strong&gt;not&lt;/strong&gt; wait for the file.&lt;/li&gt;
&lt;li&gt;Node hands the actual file-reading work off to the system and moves on to the next line.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;'This runs before the file is read!'&lt;/code&gt; logs first, because the file read is still in progress.&lt;/li&gt;
&lt;li&gt;Once the disk operation finishes, Node calls your callback function with either an error (&lt;code&gt;err&lt;/code&gt;) or the result (&lt;code&gt;data&lt;/code&gt;).&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;This pattern — &lt;code&gt;(err, data) =&amp;gt; {}&lt;/code&gt; — is so common in Node's core library that it has a name: the &lt;strong&gt;error-first callback&lt;/strong&gt; convention. The error is always the first argument, so you can check for it before trusting the data.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Problem with Nested Callbacks (a.k.a. "Callback Hell")
&lt;/h2&gt;

&lt;p&gt;Callbacks work fine for one async step. The trouble starts when you need to chain several async operations together — read a file, then use that data to query a database, then write a result, then send a notification. Each step depends on the previous one finishing, so naturally, each callback gets nested inside the last.&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;fs&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;readFile&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;user.json&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;utf8&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;err&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;userData&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;err&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;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;err&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;user&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;parse&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;userData&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="nx"&gt;db&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;findOrders&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;user&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;err&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;orders&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;err&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;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;err&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="nf"&gt;calculateTotal&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;orders&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;err&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;total&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;err&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;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;err&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

      &lt;span class="nf"&gt;sendInvoiceEmail&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;user&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;email&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;total&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;err&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;err&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;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;err&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="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Invoice sent successfully!&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="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;This staircase of nested functions is infamously known as &lt;strong&gt;callback hell&lt;/strong&gt; (or the "pyramid of doom"). It's not just ugly — it creates real problems:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Hard to read&lt;/strong&gt; — your eyes have to zigzag rightward with every new indentation level.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Hard to maintain&lt;/strong&gt; — adding a new step means rewrapping everything inside another layer.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Error handling is repetitive&lt;/strong&gt; — you end up writing the same &lt;code&gt;if (err) return ...&lt;/code&gt; check at every level, and it's easy to forget one.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Hard to reason about control flow&lt;/strong&gt; — what happens if two of these steps need to run in parallel instead of in sequence? With raw callbacks, that requires manual bookkeeping (counters, flags) that's easy to get wrong.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Callback hell isn't a sign you're "doing async wrong" — it's a structural limitation of the callback pattern itself once your async logic grows beyond one or two steps.&lt;/p&gt;

&lt;h2&gt;
  
  
  Promise-Based Async Handling
&lt;/h2&gt;

&lt;p&gt;This is the problem &lt;strong&gt;Promises&lt;/strong&gt; were designed to solve. A Promise is an object representing a value that doesn't exist &lt;em&gt;yet&lt;/em&gt; but will at some point — either successfully (&lt;strong&gt;resolved&lt;/strong&gt;) or unsuccessfully (&lt;strong&gt;rejected&lt;/strong&gt;).&lt;/p&gt;

&lt;p&gt;Instead of passing a callback deep into a function, you get back a Promise immediately and chain &lt;code&gt;.then()&lt;/code&gt; calls onto it:&lt;br&gt;
&lt;/p&gt;

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

&lt;span class="nx"&gt;fs&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;readFile&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;user.json&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;utf8&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;then&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;userData&lt;/span&gt; &lt;span class="o"&gt;=&amp;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;user&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;parse&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;userData&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;db&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;findOrders&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;user&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;})&lt;/span&gt;
  &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;then&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;orders&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;calculateTotal&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;orders&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
  &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;then&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;total&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;sendInvoiceEmail&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;user&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;email&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;total&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
  &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;then&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;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="s1"&gt;Invoice sent successfully!&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
  &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="k"&gt;catch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;err&lt;/span&gt; &lt;span class="o"&gt;=&amp;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;error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Something failed:&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;err&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Notice what happened: the pyramid is gone. Each step is flat, chained with &lt;code&gt;.then()&lt;/code&gt;, and there's a &lt;strong&gt;single &lt;code&gt;.catch()&lt;/code&gt;&lt;/strong&gt; at the end that handles errors from &lt;em&gt;any&lt;/em&gt; step in the chain — no more repeating error checks at every level.&lt;/p&gt;

&lt;p&gt;It gets even cleaner with &lt;code&gt;async&lt;/code&gt;/&lt;code&gt;await&lt;/code&gt;, which is just syntactic sugar over Promises:&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;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;sendInvoice&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;try&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;userData&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;fs&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;readFile&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;user.json&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;utf8&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;user&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;parse&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;userData&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;orders&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;db&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;findOrders&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;user&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&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;total&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;calculateTotal&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;orders&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;sendInvoiceEmail&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;user&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;email&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;total&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="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Invoice sent successfully!&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;catch &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;err&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;error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Something failed:&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;err&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;Read that out loud — it almost sounds like synchronous code, even though every &lt;code&gt;await&lt;/code&gt;ed line is asynchronous under the hood. That's the magic of this pattern: it gives you the &lt;em&gt;readability&lt;/em&gt; of sequential code with the &lt;em&gt;performance&lt;/em&gt; of async execution.&lt;/p&gt;

&lt;h2&gt;
  
  
  Benefits of Promises Over Plain Callbacks
&lt;/h2&gt;

&lt;p&gt;Bringing it all together, here's why Promises (and &lt;code&gt;async&lt;/code&gt;/&lt;code&gt;await&lt;/code&gt;) became the standard way to handle async code in modern Node.js:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Flat, readable structure&lt;/strong&gt; — no more pyramid of doom, regardless of how many async steps you chain.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Centralized error handling&lt;/strong&gt; — one &lt;code&gt;.catch()&lt;/code&gt; (or one &lt;code&gt;try/catch&lt;/code&gt; block) covers the whole chain instead of scattering checks everywhere.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Composability&lt;/strong&gt; — Promises play well with utilities like &lt;code&gt;Promise.all()&lt;/code&gt; (run several async tasks in parallel and wait for all of them) and &lt;code&gt;Promise.race()&lt;/code&gt; (resolve as soon as the first one finishes), something that's painful to hand-roll with raw callbacks.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Predictable state&lt;/strong&gt; — a Promise is always in one of three states (pending, fulfilled, or rejected) and, once settled, can never change state again. That predictability removes a whole class of bugs where a callback might accidentally fire twice or never fire at all.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Better stack traces and debugging&lt;/strong&gt; — errors thrown inside an &lt;code&gt;async&lt;/code&gt; function bubble up in a way that's far easier to trace back to its source than an error swallowed inside a deeply nested callback.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Visualizing the Difference
&lt;/h2&gt;

&lt;p&gt;Two mental models are worth sketching out as you internalize this:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Callback execution chain&lt;/strong&gt; — picture a row of dominoes, where each domino can only tip the next one inside its own falling motion. Every new async step has to be physically nested inside the previous one's callback, which is exactly why the code visually nests deeper and deeper.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Promise lifecycle flow&lt;/strong&gt; — picture a single object moving through three clearly defined states: it starts out &lt;strong&gt;pending&lt;/strong&gt;, and then moves to either &lt;strong&gt;fulfilled&lt;/strong&gt; (success — your &lt;code&gt;.then()&lt;/code&gt; runs) or &lt;strong&gt;rejected&lt;/strong&gt; (failure — your &lt;code&gt;.catch()&lt;/code&gt; runs). Once it lands in one of those two end states, it's locked in for good. Chaining &lt;code&gt;.then()&lt;/code&gt; calls doesn't nest dominoes — it just passes the baton from one finished Promise to the next.&lt;/p&gt;

&lt;h2&gt;
  
  
  Wrapping Up
&lt;/h2&gt;

&lt;p&gt;Async code is the backbone of what makes Node.js efficient: a single thread that never sits idle waiting on slow I/O. Callbacks were the original way to tap into that model, but they buckle under their own weight once your logic involves more than a step or two — leading straight into callback hell. Promises (and the &lt;code&gt;async&lt;/code&gt;/&lt;code&gt;await&lt;/code&gt; syntax built on top of them) fix that by flattening your code, centralizing error handling, and giving you predictable, composable building blocks for real-world async logic.&lt;/p&gt;

&lt;p&gt;If you're just getting comfortable with Node.js, the path is usually: understand callbacks first (because you'll still see them in older code and core APIs), then lean on Promises and &lt;code&gt;async&lt;/code&gt;/&lt;code&gt;await&lt;/code&gt; for everything you write going forward.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Got questions about async patterns in Node.js, or want to see a follow-up on async generators or Node's &lt;code&gt;EventEmitter&lt;/code&gt;? Drop a comment below — I read every one.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>node</category>
      <category>javascript</category>
      <category>webdev</category>
      <category>beginners</category>
    </item>
    <item>
      <title>Expo Router vs React Navigation - Which One Should You Use in 2026?</title>
      <dc:creator>Janmejai Singh</dc:creator>
      <pubDate>Tue, 30 Jun 2026 15:52:50 +0000</pubDate>
      <link>https://dev.to/janmejaisingh/expo-router-vs-react-navigation-which-one-should-you-use-in-2026-d70</link>
      <guid>https://dev.to/janmejaisingh/expo-router-vs-react-navigation-which-one-should-you-use-in-2026-d70</guid>
      <description>&lt;h1&gt;
  
  
  Expo Router vs React Navigation: Which One Should You Use in 2026?
&lt;/h1&gt;

&lt;p&gt;Every React Native developer eventually hits the same wall: the app has grown past a handful of screens, the navigation file is 300 lines long, and nobody on the team is quite sure which navigator owns which screen anymore. At that point, a question that used to be a footnote becomes a real architectural decision — &lt;strong&gt;Expo Router or React Navigation?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;This isn't a "pick a side" article. It's a practical breakdown of what routing actually means on mobile, why navigation got complicated in the first place, how Expo Router fixes (and doesn't fix) those problems, and when each tool genuinely makes sense for your team in 2026.&lt;/p&gt;




&lt;h2&gt;
  
  
  1. What "Routing" Actually Means on Mobile
&lt;/h2&gt;

&lt;p&gt;On the web, routing is simple: the URL changes, the browser fetches new HTML, the page updates. Mobile apps don't have that luxury — there's no browser, no automatic refresh, no &lt;code&gt;window.location&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Instead, routing on mobile means &lt;strong&gt;moving between screens while preserving application state&lt;/strong&gt;: what's on the navigation stack, what data a screen needs when you come back to it, what transition animation plays, and how a deep link from outside the app (a push notification, a shared link, a QR code) lands you on the right screen with the right state already loaded.&lt;/p&gt;

&lt;p&gt;In short: routing decides &lt;em&gt;what's visible&lt;/em&gt;, navigation decides &lt;em&gt;how you got there and what happens when you leave&lt;/em&gt;. On mobile, these two concerns are tightly coupled — which is exactly why getting the architecture right matters so much.&lt;/p&gt;




&lt;h2&gt;
  
  
  2. Why Navigation Is a Big Deal in React Native
&lt;/h2&gt;

&lt;p&gt;React Native deliberately ships without a built-in navigation solution — no storage, no camera, no navigation. These are left to the community. That's by design, but it also means navigation architecture is one of the first major decisions every React Native project makes, and one of the hardest to undo later.&lt;/p&gt;

&lt;p&gt;A poorly structured navigation layer doesn't just look messy — it actively slows teams down. Onboarding new engineers takes longer because there's no obvious "front door" to the app's structure. Refactors become risky because navigators are tangled together. Deep linking and authentication flows turn into a maze of conditional logic. Navigation isn't cosmetic; it's the structural backbone of the app.&lt;/p&gt;




&lt;h2&gt;
  
  
  3. A Brief History of React Navigation
&lt;/h2&gt;

&lt;p&gt;React Navigation has been the de facto standard since the early days of React Native. It evolved through several major eras:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;v1–v2&lt;/strong&gt;: Basic stack navigators, JS-driven transitions.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;v3–v4&lt;/strong&gt;: Better type safety, more navigator types (tabs, drawers).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;v5–v6&lt;/strong&gt;: A move to native-driven animations via &lt;code&gt;react-native-screens&lt;/code&gt;, hitting consistent 60fps transitions.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;v7&lt;/strong&gt;: Full React 18 compatibility, stronger TypeScript inference, deeper native stack integration.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;v8 (alpha, early 2026)&lt;/strong&gt;: Native bottom tabs by default on iOS and Android, deep linking enabled out of the box, and React 19 support — including the &lt;code&gt;React.Activity&lt;/code&gt; API to pause inactive screens and cut unnecessary re-renders.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;By 2023–2024, React Navigation was in essentially every serious React Native codebase, backed by a mature ecosystem of integrations and tutorials.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight jsx"&gt;&lt;code&gt;&lt;span class="c1"&gt;// 2017-era setup&lt;/span&gt;
&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;NavigationContainer&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Stack&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Navigator&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Stack&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Screen&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"Home"&lt;/span&gt; &lt;span class="na"&gt;component&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;HomeScreen&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt; &lt;span class="p"&gt;/&amp;gt;&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Stack&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Screen&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"Profile"&lt;/span&gt; &lt;span class="na"&gt;component&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;ProfileScreen&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt; &lt;span class="p"&gt;/&amp;gt;&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Stack&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Screen&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"Settings"&lt;/span&gt; &lt;span class="na"&gt;component&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;SettingsScreen&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt; &lt;span class="p"&gt;/&amp;gt;&lt;/span&gt;
  &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nc"&gt;Stack&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Navigator&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nc"&gt;NavigationContainer&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This looked manageable — for about twenty screens.&lt;/p&gt;




&lt;h2&gt;
  
  
  4. The Problems Developers Actually Ran Into
&lt;/h2&gt;

&lt;p&gt;As apps scaled, recurring pain points showed up across teams:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Manual registration overhead&lt;/strong&gt; — every screen had to be wired into a navigator by hand. Fifty screens meant fifty registrations to maintain.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Nested navigator sprawl&lt;/strong&gt; — tabs inside stacks inside drawers turned into deeply nested configuration objects that were hard to reason about.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Structure mismatch&lt;/strong&gt; — navigation config lived separately from the screens/ folder, so the visual file structure told you nothing about how the app actually flowed.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Deep linking friction&lt;/strong&gt; — URL schemes and platform-specific linking config had to be set up and kept in sync by hand.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Onboarding difficulty&lt;/strong&gt; — new engineers had no obvious map of the app; the navigation tree had to be reverse-engineered from scattered config files.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;None of this meant React Navigation was &lt;em&gt;bad&lt;/em&gt; — it meant the convention layer on top of it was missing.&lt;/p&gt;




&lt;h2&gt;
  
  
  5. Why Expo Router Was Introduced
&lt;/h2&gt;

&lt;p&gt;Expo Router (2023) was built to solve exactly that gap: file-based routing conventions on top of React Navigation, directly inspired by Next.js's &lt;code&gt;app/&lt;/code&gt; directory model.&lt;/p&gt;

&lt;p&gt;The most important thing to understand — and the thing most developers get wrong — is this:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Expo Router does not replace React Navigation. It's a structural layer built on top of it.&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Screens are still pushed, popped, and animated by the same native-driven navigation engine. What changes is &lt;em&gt;how you declare and organize routes&lt;/em&gt;.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;2026 update:&lt;/strong&gt; With the SDK 56 beta, Expo Router forked the specific parts of React Navigation it depends on — &lt;code&gt;expo-router&lt;/code&gt; no longer lists &lt;code&gt;@react-navigation/*&lt;/code&gt; as a direct dependency. The runtime API hasn't changed (&lt;code&gt;&amp;lt;Stack&amp;gt;&lt;/code&gt;, &lt;code&gt;&amp;lt;Tabs&amp;gt;&lt;/code&gt;, and file-based patterns work the same), but imports that used to come from &lt;code&gt;@react-navigation/native-stack&lt;/code&gt; or &lt;code&gt;@react-navigation/drawer&lt;/code&gt; now come from &lt;code&gt;expo-router&lt;/code&gt; directly in Expo Router projects. You can still use React Navigation directly inside an Expo project — the two aren't mutually exclusive.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  6. File-Based Routing, Explained Simply
&lt;/h2&gt;

&lt;p&gt;The core idea: &lt;strong&gt;your folder structure &lt;em&gt;is&lt;/em&gt; your routing table.&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;app/
├── index.tsx          → "/"
├── profile.tsx         → "/profile"
├── settings/
│   └── account.tsx     → "/settings/account"
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Create a file inside &lt;code&gt;app/&lt;/code&gt;, and it automatically becomes a route. Delete it, and the route disappears. There's no separate config file to keep in sync — the thing you see in your file explorer is the thing your users navigate through.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight jsx"&gt;&lt;code&gt;&lt;span class="c1"&gt;// React Navigation: edit config → register screen → add types → test&lt;/span&gt;
&lt;span class="c1"&gt;// Expo Router: create file → save → route works&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That's a genuinely different workflow loop, not just a stylistic preference.&lt;/p&gt;




&lt;h2&gt;
  
  
  7. Nested Layouts and Shared Layouts
&lt;/h2&gt;

&lt;p&gt;Expo Router uses special &lt;code&gt;_layout.tsx&lt;/code&gt; files to define shared UI and navigator type for a segment of routes — the equivalent of a persistent header, tab bar, or drawer that wraps everything underneath it.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;app/
├── _layout.tsx              ← root layout (e.g. Stack)
├── (tabs)/
│   ├── _layout.tsx          ← tab navigator
│   ├── home/
│   │   ├── index.tsx
│   │   └── details.tsx
│   ├── profile/
│   │   ├── index.tsx
│   │   └── edit.tsx
│   └── settings/
│       ├── account.tsx
│       └── privacy.tsx
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Parentheses, like &lt;code&gt;(tabs)&lt;/code&gt;, create a &lt;strong&gt;route group&lt;/strong&gt; — a folder that organizes routes without adding a segment to the URL. This is how Expo Router keeps deeply nested navigation (tabs inside stacks inside drawers) readable: the nesting in your file tree mirrors the nesting in your navigation tree, instead of living in a separate mental model entirely.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;flowchart TD
    A["Root Layout (_layout.tsx)"] --&amp;gt; B["(tabs) layout"]
    B --&amp;gt; C["Home stack"]
    B --&amp;gt; D["Profile stack"]
    B --&amp;gt; E["Settings stack"]
    C --&amp;gt; C1["index.tsx"]
    C --&amp;gt; C2["details.tsx"]
    D --&amp;gt; D1["index.tsx"]
    D --&amp;gt; D2["edit.tsx"]
    E --&amp;gt; E1["account.tsx"]
    E --&amp;gt; E2["privacy.tsx"]
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  8. Protected Routes and Authentication Flows
&lt;/h2&gt;

&lt;p&gt;Authentication is one of the most painful parts of any large React Native app, and it's where the two approaches diverge most visibly in &lt;em&gt;workflow&lt;/em&gt;, even though the underlying mechanics are similar.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;With React Navigation&lt;/strong&gt;, you typically conditionally render an entire navigator tree — an &lt;code&gt;AuthStack&lt;/code&gt; or an &lt;code&gt;AppStack&lt;/code&gt; — based on auth state. It works, but it leans imperative and creates room for state-synchronization bugs between the auth check and the navigator that gets mounted.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;With Expo Router&lt;/strong&gt;, route groups express the same idea declaratively:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;app/
├── (public)/
├── (auth)/
├── (dashboard)/
│   ├── analytics/
│   ├── billing/
│   ├── settings/
│   └── users/
├── _layout.tsx
└── +not-found.tsx
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Auth state lives high in the tree (often in the root &lt;code&gt;_layout.tsx&lt;/code&gt;), and redirects are handled declaratively using a &lt;code&gt;redirect&lt;/code&gt; with &lt;code&gt;useRootNavigationState&lt;/code&gt; or a custom hook — instead of conditionally swapping navigators.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;flowchart TD
    A["App Launch"] --&amp;gt; B["Check Session"]
    B --&amp;gt;|"Logged In"| C["Protected Routes (dashboard)/"]
    B --&amp;gt;|"Guest"| D["Auth Flow (auth)/"]
    D --&amp;gt;|"Login Success"| C
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This mental model — routes expressing app structure instead of navigators being conditionally swapped — scales noticeably better once auth flows get complicated (onboarding, multi-tenant access, role-based dashboards).&lt;/p&gt;




&lt;h2&gt;
  
  
  9. Performance Comparison
&lt;/h2&gt;

&lt;p&gt;It's tempting to assume Expo Router is "faster" than React Navigation. That's the wrong frame. &lt;strong&gt;Since Expo Router is built on the same native navigation engine, raw transition performance is nearly identical&lt;/strong&gt; — both rely on &lt;code&gt;react-native-screens&lt;/code&gt; for native-driven, 60fps transitions.&lt;/p&gt;

&lt;p&gt;Where the real differences show up:&lt;/p&gt;

&lt;h3&gt;
  
  
  Bundle Behavior
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;React Navigation&lt;/strong&gt;: all screens are bundled together unless you manually code-split.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Expo Router&lt;/strong&gt;: automatic route-based code splitting and deferred bundling — only the screens a user actually visits get pulled in, with lazy-loaded chunks for the rest. This is especially noticeable on web builds, where smaller initial bundles directly improve load time.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Navigation Transitions
&lt;/h3&gt;

&lt;p&gt;Both are powered by the same native drivers, so transitions feel equivalent — smooth, native, gesture-ready. Expo Router doesn't add runtime overhead; it generates the same navigator config under the hood.&lt;/p&gt;

&lt;h3&gt;
  
  
  Developer Workflow
&lt;/h3&gt;

&lt;p&gt;This is where the gap is real. React Navigation's loop is &lt;em&gt;edit config → register screen → add types → test&lt;/em&gt;. Expo Router's loop is &lt;em&gt;create file → save → route works&lt;/em&gt;, with typed routes inferred automatically from the filesystem. Teams that have used both consistently describe Expo Router as "feeling faster" — not because the engine is quicker, but because the iteration loop has fewer manual steps.&lt;/p&gt;




&lt;h2&gt;
  
  
  10. Developer Experience (DX) Comparison
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;&lt;/th&gt;
&lt;th&gt;React Navigation&lt;/th&gt;
&lt;th&gt;Expo Router&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Adding a screen&lt;/td&gt;
&lt;td&gt;Edit navigator config, import component, add types&lt;/td&gt;
&lt;td&gt;Add a file to &lt;code&gt;app/&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Deep linking&lt;/td&gt;
&lt;td&gt;Manual URL scheme + platform config&lt;/td&gt;
&lt;td&gt;Automatic — every route is deep-linkable by default&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Type safety&lt;/td&gt;
&lt;td&gt;Manual type definitions per navigator&lt;/td&gt;
&lt;td&gt;Types inferred from the filesystem&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Web support&lt;/td&gt;
&lt;td&gt;Requires extra setup for unified routing&lt;/td&gt;
&lt;td&gt;Built-in universal routing across iOS, Android, web&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Learning curve&lt;/td&gt;
&lt;td&gt;Steeper — navigator composition is explicit&lt;/td&gt;
&lt;td&gt;Gentler — mirrors familiar file-system routing (Next.js-like)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Flexibility&lt;/td&gt;
&lt;td&gt;Maximum — full low-level control&lt;/td&gt;
&lt;td&gt;High, but convention-guided&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;




&lt;h2&gt;
  
  
  11. Scalability for Large Applications
&lt;/h2&gt;

&lt;p&gt;For small apps, either approach works fine — the differences don't really bite until the app grows past a few dozen screens and more than one or two engineers are touching navigation code.&lt;/p&gt;

&lt;p&gt;At that scale, Expo Router's biggest advantage is having &lt;strong&gt;one source of truth&lt;/strong&gt;. With React Navigation, the &lt;code&gt;navigation/&lt;/code&gt; (or &lt;code&gt;navigators/&lt;/code&gt;) directory has to be kept manually in sync with the &lt;code&gt;screens/&lt;/code&gt; directory — two parallel structures that can drift apart. With Expo Router, the &lt;code&gt;app/&lt;/code&gt; directory &lt;em&gt;is&lt;/em&gt; the navigation structure; there's nothing else to keep in sync.&lt;/p&gt;

&lt;p&gt;That said, "scalable" doesn't automatically mean "better for every large team." Enterprises with deeply customized navigation behavior, bespoke transitions, or legacy infrastructure often find React Navigation's lower-level control more valuable than Expo Router's conventions — more on that below.&lt;/p&gt;




&lt;h2&gt;
  
  
  12. Real-World Folder Structure Example
&lt;/h2&gt;

&lt;p&gt;A production-grade Expo Router app commonly looks something like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;app/
├── (public)/
│   ├── index.tsx
│   └── pricing.tsx
├── (auth)/
│   ├── login.tsx
│   └── signup.tsx
├── (dashboard)/
│   ├── _layout.tsx
│   ├── analytics/
│   │   └── index.tsx
│   ├── billing/
│   │   └── index.tsx
│   ├── settings/
│   │   └── account.tsx
│   └── users/
│       ├── index.tsx
│       └── [id].tsx
├── _layout.tsx
└── +not-found.tsx
components/
hooks/
services/
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The equivalent React Navigation structure typically looks like:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;src/
├── screens/
├── navigators/
├── stacks/
├── tabs/
├── drawers/
├── linking/
└── auth/
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Both can be well-organized. But notice the difference: one structure &lt;em&gt;is&lt;/em&gt; the navigation map, and the other &lt;em&gt;describes&lt;/em&gt; it from a parallel directory that has to be manually kept in sync.&lt;/p&gt;




&lt;h2&gt;
  
  
  13. Which Approach Do Companies and Teams Actually Prefer?
&lt;/h2&gt;

&lt;p&gt;Looking at new project creation, documentation defaults, and community discussion through 2026, the trend is clear without being absolute:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Startups and solo developers&lt;/strong&gt; have moved quickly to Expo Router — reduced boilerplate and faster iteration are a real productivity win when engineering time is scarce.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;New projects in general&lt;/strong&gt; default to Expo Router — &lt;code&gt;npx create-expo-app&lt;/code&gt; ships it out of the box, and the official Expo documentation is centered around it.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Larger enterprises with existing React Navigation investments&lt;/strong&gt; are mixed. Some are migrating incrementally; many are staying put, especially where navigation behavior is deeply customized or migration risk outweighs the DX gains.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;There's no "winner takes all" situation here — it's a genuine architecture-philosophy split, not a popularity contest.&lt;/p&gt;




&lt;h2&gt;
  
  
  14. When NOT to Use Expo Router
&lt;/h2&gt;

&lt;p&gt;Expo Router is excellent, but it isn't the right default in every situation. Reach for React Navigation directly instead when:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;You're working on a &lt;strong&gt;bare React Native app&lt;/strong&gt; without the Expo ecosystem (note: React Native Navigation by Wix is also not compatible with Expo Go or expo-dev-client).&lt;/li&gt;
&lt;li&gt;You need &lt;strong&gt;highly custom transitions or gestures&lt;/strong&gt; that don't fit standard navigator patterns.&lt;/li&gt;
&lt;li&gt;You're doing a &lt;strong&gt;brownfield integration&lt;/strong&gt; — adding navigation to an existing native app where Expo's conventions don't map cleanly onto existing architecture.&lt;/li&gt;
&lt;li&gt;You're &lt;strong&gt;migrating a large, mature codebase&lt;/strong&gt; where the migration risk outweighs the DX improvement.&lt;/li&gt;
&lt;li&gt;Your team is &lt;strong&gt;unfamiliar with filesystem-based routing conventions&lt;/strong&gt; and route groups, and the learning curve would slow down a near-term deadline more than it helps long-term maintainability.&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  15. Situations Where React Navigation Still Makes More Sense
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Maximum low-level control&lt;/strong&gt; — when you need to reach directly into navigator internals for behavior Expo Router's conventions don't expose cleanly.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Legacy enterprise codebases&lt;/strong&gt; — where the navigation architecture is already deeply established and stable, and there's no concrete pain point driving a migration.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Non-Expo projects&lt;/strong&gt; — if you're not using the Expo toolchain at all, React Navigation is the more natural fit without taking on Expo Router's conventions for no benefit.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Teams that value explicit configuration over convention&lt;/strong&gt; — some teams genuinely prefer seeing every navigator wired up explicitly in code, especially when onboarding engineers from a background that doesn't include file-based routing (e.g., teams without web/Next.js experience).&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Architecture Comparison at a Glance
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;flowchart LR
    subgraph RN["React Navigation"]
        direction TB
        RN1["screens/"] --&amp;gt; RN2["navigators/ (manual config)"]
        RN2 --&amp;gt; RN3["NavigationContainer"]
    end
    subgraph ER["Expo Router"]
        direction TB
        ER1["app/ folder structure"] --&amp;gt; ER2["Generated React Navigation config"]
        ER2 --&amp;gt; ER3["NavigationContainer"]
    end
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The key takeaway from this diagram: both approaches ultimately produce the same underlying navigation tree. The difference is entirely in &lt;em&gt;how you author it&lt;/em&gt; — by hand, or by folder structure.&lt;/p&gt;




&lt;h2&gt;
  
  
  The Verdict for 2026
&lt;/h2&gt;

&lt;p&gt;Expo Router doesn't replace React Navigation — it elevates it with conventions that make teams faster and code easier to maintain. For the majority of new, production-grade React Native apps built on Expo, that convention layer is worth adopting by default.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Choose Expo Router if:&lt;/strong&gt; you're starting a new Expo project, your team values developer experience and onboarding speed over granular control, you need solid web parity, and your navigation patterns are fairly standard (stacks, tabs, drawers, auth-gated route groups).&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Choose React Navigation directly if:&lt;/strong&gt; you're on bare React Native, you need deeply custom transitions or gestures, you're integrating navigation into an existing native app, or you're maintaining a large legacy codebase where migration isn't worth the risk.&lt;/p&gt;

&lt;p&gt;Make the call based on your constraints, not the hype cycle. But if you're starting fresh in 2026, Expo Router is the sensible default — and you're not giving up React Navigation's power to get there, you're just not configuring it by hand anymore.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;If you're building a new React Native app this year, what's tipping your decision — developer experience, team size, or existing infrastructure? Drop your reasoning in the comments.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>reactnative</category>
      <category>expo</category>
      <category>mobiledev</category>
      <category>javascript</category>
    </item>
    <item>
      <title>How WhatsApp Works Without Internet: Offline Messaging and Sync Explained</title>
      <dc:creator>Janmejai Singh</dc:creator>
      <pubDate>Sun, 31 May 2026 15:43:09 +0000</pubDate>
      <link>https://dev.to/janmejaisingh/how-whatsapp-works-without-internet-offline-messaging-and-sync-explained-51n2</link>
      <guid>https://dev.to/janmejaisingh/how-whatsapp-works-without-internet-offline-messaging-and-sync-explained-51n2</guid>
      <description>&lt;p&gt;You're on a train, signal drops to zero. You type a message and hit send. A single grey tick appears.&lt;/p&gt;

&lt;p&gt;Then — the moment you exit the tunnel — two ticks appear.&lt;/p&gt;

&lt;p&gt;How did that work? You were &lt;strong&gt;offline&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;This isn't magic. It's &lt;strong&gt;offline-first architecture&lt;/strong&gt;, and it's one of the most practical ideas in mobile engineering. Let's break it down.&lt;/p&gt;




&lt;h2&gt;
  
  
  The Scenario: Sending a Message in Airplane Mode
&lt;/h2&gt;

&lt;p&gt;Imagine you toggle airplane mode and type:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;"Just took off. Landing in 3 hours!"&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;WhatsApp doesn't show an error. It doesn't freeze. You see a message bubble with &lt;strong&gt;one grey tick&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Here's what actually happened:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;The app &lt;strong&gt;saved your message to a local database&lt;/strong&gt; on your phone&lt;/li&gt;
&lt;li&gt;It &lt;strong&gt;added the message to an outgoing queue&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;It &lt;strong&gt;rendered the message immediately&lt;/strong&gt; in the UI — no waiting&lt;/li&gt;
&lt;li&gt;It set the internal status to &lt;strong&gt;PENDING&lt;/strong&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Your message never touched a server. The app made a &lt;em&gt;promise&lt;/em&gt; to deliver it when connectivity returns. That promise — that optimistic local-first action — is the heart of offline-first design.&lt;/p&gt;




&lt;h2&gt;
  
  
  Local Storage and Message Persistence
&lt;/h2&gt;

&lt;p&gt;Every message is first written to a &lt;strong&gt;local SQLite database&lt;/strong&gt; on the device, before anything goes to the cloud.&lt;/p&gt;

&lt;p&gt;WhatsApp uses SQLite. Android apps use Room. iOS apps use Core Data. In React Native, you'd reach for &lt;code&gt;expo-sqlite&lt;/code&gt; or &lt;code&gt;@op-engineering/op-sqlite&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;A simplified message record looks 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;messages&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="n"&gt;id&lt;/span&gt;              &lt;span class="nb"&gt;TEXT&lt;/span&gt; &lt;span class="k"&gt;PRIMARY&lt;/span&gt; &lt;span class="k"&gt;KEY&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;  &lt;span class="c1"&gt;-- Generated on device, not server&lt;/span&gt;
  &lt;span class="n"&gt;conversation_id&lt;/span&gt; &lt;span class="nb"&gt;TEXT&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;span class="n"&gt;content&lt;/span&gt;         &lt;span class="nb"&gt;TEXT&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;span class="n"&gt;created_at&lt;/span&gt;      &lt;span class="nb"&gt;INTEGER&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;span class="c1"&gt;-- Device timestamp&lt;/span&gt;
  &lt;span class="n"&gt;status&lt;/span&gt;          &lt;span class="nb"&gt;TEXT&lt;/span&gt; &lt;span class="k"&gt;DEFAULT&lt;/span&gt; &lt;span class="s1"&gt;'PENDING'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="n"&gt;media_uri&lt;/span&gt;       &lt;span class="nb"&gt;TEXT&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;              &lt;span class="c1"&gt;-- Local file path for media&lt;/span&gt;
  &lt;span class="n"&gt;server_ack_id&lt;/span&gt;   &lt;span class="nb"&gt;TEXT&lt;/span&gt;               &lt;span class="c1"&gt;-- Set after server confirms receipt&lt;/span&gt;
&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Two things worth noting:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The &lt;strong&gt;ID is generated on the device&lt;/strong&gt;, so WhatsApp can display the message before the server even knows it exists.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Status starts as PENDING&lt;/strong&gt; — honest, explicit, and essential for the queue system.&lt;/li&gt;
&lt;/ul&gt;

&lt;blockquote&gt;
&lt;p&gt;The UI reads from local storage. It never waits for the network.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  The Message Queue
&lt;/h2&gt;

&lt;p&gt;When a message is saved locally, it also enters an &lt;strong&gt;outgoing queue&lt;/strong&gt; — a persistent list of things the app needs to send when it can.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;┌──────────────────────────────────────┐
│         OUTGOING MESSAGE QUEUE       │
│  [msg_001] "Just took off..." PENDING│
│  [msg_002] "See you soon!" PENDING   │
│  [msg_003] photo.jpg       PENDING   │
└──────────────────────────────────────┘
         ↕  No internet — held on device
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This queue is &lt;strong&gt;written to disk&lt;/strong&gt;, not held in RAM. If the app crashes, the queue survives. When internet returns, the app processes messages in order, retrying failures with exponential backoff (1s → 2s → 4s → ...) to handle flaky connections gracefully.&lt;/p&gt;




&lt;h2&gt;
  
  
  Syncing When Connectivity Returns
&lt;/h2&gt;

&lt;p&gt;The moment internet returns, the app fires off four steps almost simultaneously:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Internet returns
      │
      ▼
1. Flush outgoing queue  →  Send all pending messages to server
      │
      ▼
2. Pull missed messages  →  Fetch messages sent to YOU while offline
      │
      ▼
3. Reconcile local DB    →  Merge server state with local state
      │
      ▼
4. Update the UI         →  Ticks change, new messages appear
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Step 1&lt;/strong&gt; — Each queued message is sent to WhatsApp's servers. The server acknowledges receipt and the status upgrades from &lt;code&gt;PENDING&lt;/code&gt; to &lt;code&gt;SENT&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 2&lt;/strong&gt; — People may have messaged you while you were offline. The app requests everything from the server since the last sync timestamp.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 3&lt;/strong&gt; — Local DB is updated with what came from the server.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 4&lt;/strong&gt; — Because the UI &lt;em&gt;observes&lt;/em&gt; the local DB reactively, everything updates automatically. No manual refresh needed.&lt;/p&gt;




&lt;h2&gt;
  
  
  Delivery States: Sent, Delivered, Read
&lt;/h2&gt;

&lt;p&gt;The tick system maps directly to a message state machine:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;[PENDING] → [SENT] → [DELIVERED] → [READ]
  clock      1 grey    2 grey        2 blue
              tick      ticks         ticks
     └──────→ [FAILED] (retries exhausted)
&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;State&lt;/th&gt;
&lt;th&gt;What it means&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Pending&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Saved locally, not yet on server&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Sent&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Server received it&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Delivered&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Recipient's &lt;em&gt;device&lt;/em&gt; has it&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Read&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Recipient opened the conversation&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Failed&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Couldn't deliver after all retries&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;State transitions are triggered by &lt;strong&gt;acknowledgements (acks)&lt;/strong&gt; from the server or recipient's device via a persistent connection.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Why separate "Sent" from "Delivered"? Because the server having your message ≠ the recipient's phone having it. They could be offline too.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  Handling Media While Offline
&lt;/h2&gt;

&lt;p&gt;Text messages are tiny. Photos and videos are not. WhatsApp handles this with a &lt;strong&gt;decoupled upload strategy&lt;/strong&gt;:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Capture &amp;amp; save locally&lt;/strong&gt; — photo is stored on device&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Queue the message&lt;/strong&gt; with a local file reference (&lt;code&gt;local://img_001.jpg&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Upload media in background&lt;/strong&gt; when internet returns&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Server returns a CDN URL&lt;/strong&gt; — message record updates from &lt;code&gt;local://&lt;/code&gt; to &lt;code&gt;https://mmg.whatsapp.net/...&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Recipient downloads on demand&lt;/strong&gt; — they get the URL and fetch media lazily&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;This is why you sometimes see a blurred thumbnail or "Waiting for media" — the text message arrived, but media hasn't been downloaded yet.&lt;/p&gt;




&lt;h2&gt;
  
  
  Conflict Resolution and Message Ordering
&lt;/h2&gt;

&lt;p&gt;What happens when messages arrive out of order after going offline?&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The problem:&lt;/strong&gt; Device clocks are unreliable. Your phone might be 30 seconds off, or in the wrong timezone. If WhatsApp trusted device timestamps alone, messages could appear out of order.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The solution:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Server-assigned timestamps&lt;/strong&gt; — when a message reaches the server, it gets a canonical timestamp used for ordering&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Sequence numbers&lt;/strong&gt; — monotonically increasing numbers break ties between messages with identical timestamps&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Last-write-wins for status&lt;/strong&gt; — message status can only move &lt;em&gt;forward&lt;/em&gt; (SENT → DELIVERED, never backwards)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This is a simplified form of &lt;strong&gt;eventual consistency&lt;/strong&gt;:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;The system doesn't guarantee every device sees the same state at the same instant. But given time and connectivity, all devices converge on the same state.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;For messaging, this is an acceptable tradeoff. A message appearing 200ms out of order is fine. A message that never appears is not.&lt;/p&gt;




&lt;h2&gt;
  
  
  Why Offline-First Matters
&lt;/h2&gt;

&lt;p&gt;Traditional approach:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;User action → Network request → Update UI (only if network works)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Offline-first approach:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;User action → Update local DB → Update UI → Background sync to server
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The difference in feel is enormous.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Traditional (online-first)&lt;/th&gt;
&lt;th&gt;Offline-first&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Broken without internet&lt;/td&gt;
&lt;td&gt;Works fully offline&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;User waits on every action&lt;/td&gt;
&lt;td&gt;Instant UI feedback&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Lost actions on failure&lt;/td&gt;
&lt;td&gt;Queue retries automatically&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Spinner on every tap&lt;/td&gt;
&lt;td&gt;Smooth always&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Terrible on slow networks&lt;/td&gt;
&lt;td&gt;Great on any network&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;The tradeoffs are real though:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Conflict resolution becomes your responsibility&lt;/li&gt;
&lt;li&gt;Local data needs encryption (WhatsApp uses SQLCipher)&lt;/li&gt;
&lt;li&gt;Testing network edge cases is harder&lt;/li&gt;
&lt;li&gt;Storage management (when do you purge old queued messages?)&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  The Full Lifecycle in One Diagram
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;User types and hits send
        ↓
App generates local ID
        ↓
Saved to SQLite (PENDING)
        ↓
Added to outgoing queue
        ↓
UI renders immediately (optimistic update)
        ↓
─── [Internet returns] ───────────────────
        ↓
Queue flushed → sent to server
        ↓
Server acks → status: SENT (1 grey tick ✓)
        ↓
Recipient's device comes online → delivered
        ↓
Delivery ack → status: DELIVERED (2 grey ✓✓)
        ↓
Recipient opens chat → read receipt sent
        ↓
Read ack → status: READ (2 blue ✓✓) ✅
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  Building This in React Native
&lt;/h2&gt;

&lt;p&gt;Here's the toolkit you'd reach for:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Need&lt;/th&gt;
&lt;th&gt;Library&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Local DB&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;expo-sqlite&lt;/code&gt; or &lt;code&gt;@op-engineering/op-sqlite&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Fast key-value (queue)&lt;/td&gt;
&lt;td&gt;&lt;code&gt;react-native-mmkv&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Network state&lt;/td&gt;
&lt;td&gt;&lt;code&gt;@react-native-community/netinfo&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Background sync&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;expo-task-manager&lt;/code&gt; + background fetch&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Real-time acks&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;socket.io-client&lt;/code&gt; or Firebase RTDB&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;The pattern is always: &lt;strong&gt;local first → background sync → UI driven by local state&lt;/strong&gt;.&lt;/p&gt;




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

&lt;ul&gt;
&lt;li&gt;WhatsApp never waits for the network — it writes locally and syncs later&lt;/li&gt;
&lt;li&gt;The outgoing queue persists to disk and survives crashes&lt;/li&gt;
&lt;li&gt;Delivery states (PENDING → SENT → DELIVERED → READ) are driven by server and device acks&lt;/li&gt;
&lt;li&gt;Media uploads are decoupled from text — text queues fast, media uploads in background&lt;/li&gt;
&lt;li&gt;Server-side timestamps and sequence numbers resolve ordering conflicts&lt;/li&gt;
&lt;li&gt;Offline-first trades implementation complexity for much better UX&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  What to Explore Next
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;WatermelonDB&lt;/strong&gt; — a React Native DB built specifically for offline-first at scale&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;CRDTs&lt;/strong&gt; — the math behind conflict-free sync&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Firebase Firestore offline persistence&lt;/strong&gt; — a managed offline-first solution&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;XMPP / MQTT&lt;/strong&gt; — the protocols behind real-time messaging&lt;/li&gt;
&lt;/ul&gt;




&lt;p&gt;Next time you see those grey ticks turn blue on a plane with spotty Wi-Fi, you'll know exactly what just happened. 🛫&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Found this useful? Drop a ❤️ and share it with your dev cohort!&lt;/em&gt;&lt;/p&gt;

</description>
      <category>systemdesign</category>
      <category>mobiledev</category>
      <category>reactnative</category>
      <category>webdev</category>
    </item>
    <item>
      <title>How Instagram, WhatsApp, Uber &amp; Netflix Would Be Built Today Using Expo Router</title>
      <dc:creator>Janmejai Singh</dc:creator>
      <pubDate>Sun, 31 May 2026 14:21:55 +0000</pubDate>
      <link>https://dev.to/janmejaisingh/how-instagram-whatsapp-uber-netflix-would-be-built-today-using-expo-router-egm</link>
      <guid>https://dev.to/janmejaisingh/how-instagram-whatsapp-uber-netflix-would-be-built-today-using-expo-router-egm</guid>
      <description>&lt;p&gt;You tap Record. You nail the take. You save as Draft. You close Instagram, reopen it — and your Reel is right there. What just happened under the hood?&lt;/p&gt;

&lt;p&gt;This post traces that full journey: from your device's camera buffer to your follower's screen halfway across the world. We'll cover local storage, drafts, cloud uploads, media processing, caching, and CDNs — with diagrams at each stage.&lt;/p&gt;




&lt;h2&gt;
  
  
  Why Social Media Apps Need Efficient Media Storage
&lt;/h2&gt;

&lt;p&gt;Photos and videos are the heaviest assets any mobile app handles. A single 60-second Reel captured at 1080p can be 200–400MB before any compression. Multiply that by a billion daily uploads and you begin to understand why media storage is not a solved problem — it's an ongoing engineering discipline.&lt;/p&gt;

&lt;p&gt;Efficiency matters at three levels:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Device level&lt;/strong&gt; — local storage is limited; apps can't balloon in size&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Network level&lt;/strong&gt; — large uploads over mobile connections are fragile and slow&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Cloud level&lt;/strong&gt; — storing, processing, and serving billions of files requires distributed systems&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Instagram's architecture solves each of these with different tools, layered together.&lt;/p&gt;




&lt;h2&gt;
  
  
  How Photos and Videos Are Stored Before Upload
&lt;/h2&gt;

&lt;p&gt;When you record a Reel, the app doesn't immediately try to upload it. Your device is the first storage layer.&lt;/p&gt;

&lt;p&gt;The recording pipeline looks like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Camera Sensor
     ↓
In-Memory Frame Buffer  ← (active recording, fast but volatile)
     ↓
Local Temp Cache        ← (flushed from buffer, survives brief interruptions)
     ↓
User Decision Point
  ├── Discard → Delete temp files
  ├── Save Draft → Move to persistent storage
  └── Post → Enqueue for upload
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Why local-first?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Three reasons:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Networks are unreliable — especially on cellular. Streaming raw video directly to a cloud server mid-recording is a recipe for data loss.&lt;/li&gt;
&lt;li&gt;User intent is unknown — the app doesn't know you'll keep the clip until you decide.&lt;/li&gt;
&lt;li&gt;Upload costs — uploading every test take wastes bandwidth, battery, and server resources.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Local storage on mobile comes in different tiers:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Tier&lt;/th&gt;
&lt;th&gt;Speed&lt;/th&gt;
&lt;th&gt;Persistence&lt;/th&gt;
&lt;th&gt;Cleared by&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;RAM buffer&lt;/td&gt;
&lt;td&gt;Fastest&lt;/td&gt;
&lt;td&gt;Lost on kill&lt;/td&gt;
&lt;td&gt;OS memory pressure&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Cache directory&lt;/td&gt;
&lt;td&gt;Fast&lt;/td&gt;
&lt;td&gt;Semi-persistent&lt;/td&gt;
&lt;td&gt;OS under storage pressure&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Documents directory&lt;/td&gt;
&lt;td&gt;Fast&lt;/td&gt;
&lt;td&gt;Persistent&lt;/td&gt;
&lt;td&gt;Only explicit delete&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Active recordings live in the cache. Once you decide to keep them — as a draft or a post — they move to the documents directory.&lt;/p&gt;




&lt;h2&gt;
  
  
  What Happens When a User Saves a Draft
&lt;/h2&gt;

&lt;p&gt;A draft isn't just a video file. It's a &lt;strong&gt;bundle of state&lt;/strong&gt; that must survive app restarts:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The raw video clip(s)&lt;/li&gt;
&lt;li&gt;Applied audio track (and timestamp offset)&lt;/li&gt;
&lt;li&gt;Stickers, text overlays, and their screen positions&lt;/li&gt;
&lt;li&gt;Trim start/end points&lt;/li&gt;
&lt;li&gt;Filter and effect choices&lt;/li&gt;
&lt;li&gt;Selected thumbnail frame&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;When you tap "Save Draft," Instagram performs a &lt;strong&gt;transactional write&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;1. Write video file to documents directory
2. Write metadata record to local SQLite database
   (file path, audio, effects, trim points, etc.)
3. Confirm both writes succeeded
4. Show "Draft Saved" confirmation
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Either both writes succeed or neither is surfaced to the user. This is critical — a draft pointing to a missing file, or a video file with no matching metadata record, is worse than no draft at all.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;How drafts survive app restarts:&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;On launch, Instagram queries the SQLite database for all draft records. For each record, it reads the stored file path and reconstructs the preview. No network call needed. The entire drafts tray is built from local data.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;App Launch
    ↓
Query SQLite: SELECT * FROM drafts ORDER BY updated_at DESC
    ↓
For each row: read video file at stored path → generate thumbnail
    ↓
Render Drafts tray
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  Local Storage vs. Cloud Storage
&lt;/h2&gt;

&lt;p&gt;These two worlds have opposite strengths:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;┌──────────────────────────┬──────────────────────────┐
│     LOCAL STORAGE        │     CLOUD STORAGE        │
├──────────────────────────┼──────────────────────────┤
│ ✅ Near-zero latency     │ ✅ Unlimited scale        │
│ ✅ Works offline         │ ✅ Durable (replicated)   │
│ ✅ No network dependency │ ✅ Accessible everywhere  │
│ ❌ Limited capacity      │ ❌ Network dependent      │
│ ❌ Lost if device lost   │ ❌ Slower (network I/O)   │
│ ❌ Single device only    │ ❌ Costs money at scale   │
└──────────────────────────┴──────────────────────────┘
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Instagram's architecture uses both — local for speed and offline capability, cloud for durability and sharing. The transition point is the &lt;strong&gt;upload pipeline&lt;/strong&gt;.&lt;/p&gt;




&lt;h2&gt;
  
  
  Uploading Large Media Files Efficiently
&lt;/h2&gt;

&lt;p&gt;Uploading a 200MB video over cellular as one HTTP POST is a terrible idea. One dropped packet and you start over. Instagram avoids this with two techniques:&lt;/p&gt;

&lt;h3&gt;
  
  
  1. Chunked / Resumable Uploads
&lt;/h3&gt;

&lt;p&gt;The file is split into small chunks (e.g., 5MB each). Each chunk is uploaded independently. If a chunk fails, only that chunk retries.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Full Video: 200MB
 ├── Chunk 01 (5MB) → ✅
 ├── Chunk 02 (5MB) → ✅
 ├── Chunk 03 (5MB) → ❌ (network drop)
 │       └── Retry Chunk 03 → ✅
 ├── Chunk 04 (5MB) → ✅
 └── ... 36 more chunks
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Server-side, the chunks are tracked by upload session ID. Once all are received, the server reassembles them in order.&lt;/p&gt;

&lt;h3&gt;
  
  
  2. Background Uploads
&lt;/h3&gt;

&lt;p&gt;After you tap Post, you don't have to wait in the app. The upload continues in the background using OS-level transfer APIs:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;iOS&lt;/strong&gt;: &lt;code&gt;URLSession&lt;/code&gt; with &lt;code&gt;.background&lt;/code&gt; configuration&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Android&lt;/strong&gt;: &lt;code&gt;WorkManager&lt;/code&gt; or &lt;code&gt;JobScheduler&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;These transfers are managed by the operating system, not the app. They survive the app being backgrounded or even force-quit, and resume automatically when connectivity returns.&lt;/p&gt;

&lt;h3&gt;
  
  
  The Upload Queue
&lt;/h3&gt;

&lt;p&gt;A local queue tracks upload status for each piece of media:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;PENDING → IN_PROGRESS → COMPLETED
              ↓
           PAUSED (network lost)
              ↓
           RETRYING
              ↓
           FAILED (max retries hit)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This queue state is persisted to SQLite, so if the app is killed mid-upload, it knows exactly where to resume.&lt;/p&gt;




&lt;h2&gt;
  
  
  Media Processing and Compression
&lt;/h2&gt;

&lt;p&gt;Once your raw video lands in Instagram's cloud, it enters a &lt;strong&gt;processing pipeline&lt;/strong&gt; before being stored for delivery.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Raw Upload Received
        ↓
Validation
(corrupt file check, content policy scan, format verification)
        ↓
Transcoding
(re-encode to H.264/H.265, normalize codec/container)
        ↓
Multi-Resolution Encoding
(1080p, 720p, 480p, 360p — for adaptive bitrate streaming)
        ↓
Audio Processing
(normalize levels, validate sync)
        ↓
Thumbnail Extraction
(keyframe analysis + ML quality scoring)
        ↓
Distribute to Storage + CDN
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Compression Concepts
&lt;/h3&gt;

&lt;p&gt;Raw uploads are transcoded to significantly smaller files without noticeable quality loss:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Codec efficiency&lt;/strong&gt;: H.265 (HEVC) compresses ~40% better than H.264 at equal visual quality&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Variable Bitrate (VBR)&lt;/strong&gt;: Static scenes get fewer bits; fast-moving scenes get more — no bits wasted&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Perceptual encoding&lt;/strong&gt;: Human vision is less sensitive to chroma (color) detail than luma (brightness), so color channels are compressed more aggressively&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;A 200MB raw Reel might become a 12–20MB streamable video after processing.&lt;/p&gt;




&lt;h2&gt;
  
  
  Thumbnail Generation and Previews
&lt;/h2&gt;

&lt;p&gt;Thumbnails are more than decoration — they're a performance optimization.&lt;/p&gt;

&lt;p&gt;A thumbnail is a single JPEG, typically under 50KB, that can be fetched and displayed in milliseconds. Without thumbnails, every feed scroll would require pre-buffering video before anything appeared on screen.&lt;/p&gt;

&lt;h3&gt;
  
  
  How Thumbnails Are Created
&lt;/h3&gt;

&lt;p&gt;During the processing pipeline:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Multiple &lt;strong&gt;keyframes&lt;/strong&gt; are extracted from the video at regular intervals&lt;/li&gt;
&lt;li&gt;Each keyframe is scored by an ML model for visual quality (avoiding blurry, over-exposed, or mid-blink frames)&lt;/li&gt;
&lt;li&gt;The highest-scoring frame is selected (or the creator's manually chosen frame is used if available)&lt;/li&gt;
&lt;li&gt;The selected frame is saved as a separate JPEG in object storage&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  Low-Quality Image Placeholders (LQIP)
&lt;/h3&gt;

&lt;p&gt;Instagram also generates a &lt;strong&gt;tiny blur placeholder&lt;/strong&gt; — sometimes just 20x20 pixels encoded as a base64 string in the API response. This renders instantly (before any network request for the actual thumbnail) and gives the feed a fluid scrolling feel even on slow connections.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Feed item appears
     ↓
Base64 blur hash renders (immediate — no network)
     ↓
Full thumbnail fetched from CDN (50–200ms)
     ↓
Swap: blur → thumbnail
     ↓
User taps play → ABR video stream begins
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  Caching Frequently Viewed Content
&lt;/h2&gt;

&lt;p&gt;Every content request has a cost: network latency, server load, battery usage. Caching short-circuits that cost by keeping a local copy of recently used content.&lt;/p&gt;

&lt;h3&gt;
  
  
  Multi-Level Cache Architecture
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Request for content
        ↓
Memory cache (L1) → HIT → Render (~0ms)
        ↓ MISS
Disk cache (L2)   → HIT → Render + promote to L1 (~5–20ms)
        ↓ MISS
CDN Edge          → HIT → Render + store in L2 (~30–100ms)
        ↓ MISS
Origin Storage    → Fetch → Render + propagate to CDN + store in L2
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  What Gets Cached
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Content Type&lt;/th&gt;
&lt;th&gt;Cache Location&lt;/th&gt;
&lt;th&gt;TTL&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Profile pictures&lt;/td&gt;
&lt;td&gt;Disk (long TTL)&lt;/td&gt;
&lt;td&gt;Days&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Feed thumbnails&lt;/td&gt;
&lt;td&gt;Disk (medium TTL)&lt;/td&gt;
&lt;td&gt;Hours&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Active video buffers&lt;/td&gt;
&lt;td&gt;Memory&lt;/td&gt;
&lt;td&gt;Session only&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Stories (viewed)&lt;/td&gt;
&lt;td&gt;Disk (short TTL)&lt;/td&gt;
&lt;td&gt;Until expiry&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Prefetched posts&lt;/td&gt;
&lt;td&gt;Disk&lt;/td&gt;
&lt;td&gt;Minutes&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h3&gt;
  
  
  Prefetching: Getting Ahead of the User
&lt;/h3&gt;

&lt;p&gt;While you're watching post #3, Instagram is already fetching thumbnails and initial video buffers for posts #6–10 in your feed. This makes scrolling feel instantaneous — by the time your thumb reaches those posts, the content is already on your device.&lt;/p&gt;

&lt;p&gt;Prefetching is throttled by connection quality: aggressive on Wi-Fi, conservative on 4G, minimal on 3G.&lt;/p&gt;

&lt;h3&gt;
  
  
  Cache Invalidation
&lt;/h3&gt;

&lt;p&gt;Knowing &lt;em&gt;when&lt;/em&gt; to throw away cached content is hard. Instagram's approach:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;TTL (Time-to-Live)&lt;/strong&gt;: Cached items expire after a configured duration&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Cache-Control headers&lt;/strong&gt;: Server communicates max-age per content type&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Versioned URLs&lt;/strong&gt;: When content changes (e.g., profile photo update), the URL hash changes, forcing a fresh fetch&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Content Delivery Using CDNs
&lt;/h2&gt;

&lt;p&gt;Your followers in Tokyo, Lagos, and Berlin all load your Reel at similar speeds because Instagram doesn't serve media directly from a central data center — it uses a &lt;strong&gt;Content Delivery Network&lt;/strong&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  How CDN Delivery Works
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;User in Tokyo requests Reel video
           ↓
DNS resolves to nearest CDN edge node (Tokyo)
           ↓
Edge node checks local cache
  ├── HIT → Serve immediately (low latency)
  └── MISS → Fetch from origin → Cache locally → Serve
           ↓
Response travels &amp;lt;50ms vs ~200ms from US origin
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Meta operates its own CDN infrastructure alongside partnerships with commercial CDN providers. Edge nodes are placed in internet exchange points worldwide to minimize the physical distance content must travel.&lt;/p&gt;

&lt;h3&gt;
  
  
  Adaptive Bitrate Streaming (ABR)
&lt;/h3&gt;

&lt;p&gt;Instagram doesn't just serve one video file — it serves a &lt;strong&gt;manifest file&lt;/strong&gt; (HLS or DASH format) that lists multiple quality variants. Your device's player selects the appropriate variant based on real-time network conditions:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Excellent connection → 1080p @ 8Mbps
Good connection      → 720p  @ 4Mbps
Fair connection      → 480p  @ 2Mbps
Poor connection      → 360p  @ 800Kbps
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If your connection degrades mid-playback, the player seamlessly switches to a lower quality tier without rebuffering — you see slightly less detail, but playback continues uninterrupted.&lt;/p&gt;




&lt;h2&gt;
  
  
  Managing Storage, Performance, and User Experience
&lt;/h2&gt;

&lt;p&gt;All of these systems involve tradeoffs. More aggressive caching = faster loads but higher device storage use. More compression = smaller files but potential quality loss. More CDN edge nodes = lower latency but higher infrastructure cost.&lt;/p&gt;

&lt;h3&gt;
  
  
  Device Storage Management
&lt;/h3&gt;

&lt;p&gt;Instagram's client periodically runs &lt;strong&gt;cache eviction&lt;/strong&gt; to prevent disk bloat:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;LRU eviction: Least-recently-used content is removed first&lt;/li&gt;
&lt;li&gt;Size caps: Cache directories are bounded to a maximum size&lt;/li&gt;
&lt;li&gt;OS pressure response: When device storage is low, the OS may clear cache directories without asking&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Drafts are immune to OS-level eviction because they live in the documents directory — only explicit deletion removes them.&lt;/p&gt;

&lt;h3&gt;
  
  
  Adaptive Behavior Based on Context
&lt;/h3&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;System Response&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;On Wi-Fi&lt;/td&gt;
&lt;td&gt;Aggressive prefetch, high quality, run pending uploads&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;On cellular&lt;/td&gt;
&lt;td&gt;Conservative prefetch, adaptive quality, throttle background work&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Low battery&lt;/td&gt;
&lt;td&gt;Pause non-critical background tasks&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Storage low&lt;/td&gt;
&lt;td&gt;Evict cache aggressively, warn user if drafts at risk&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;App in background&lt;/td&gt;
&lt;td&gt;Continue uploads, pause prefetching&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;




&lt;h2&gt;
  
  
  Full Architecture: End-to-End
&lt;/h2&gt;

&lt;p&gt;Let's map the complete journey from recording to playback:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;┌─────────────────────────────────────────────────────────────────┐
│  DEVICE                                                          │
│  Record → Frame Buffer → Temp Cache                             │
│  Edit/Draft → Documents Dir + SQLite                            │
│  Post → Upload Queue → Chunked Upload (background)             │
└────────────────────────────┬────────────────────────────────────┘
                              │ HTTPS chunks
                              ▼
┌─────────────────────────────────────────────────────────────────┐
│  INSTAGRAM CLOUD                                                 │
│  Ingest API → Reassemble → Validation → Processing Pipeline     │
│  Transcode + Compress + Multi-res → Thumbnails → Object Storage │
└────────────────────────────┬────────────────────────────────────┘
                              │ Push to edge
                              ▼
┌─────────────────────────────────────────────────────────────────┐
│  CDN EDGE NETWORK                                               │
│  Edge Node (Tokyo) │ Edge Node (London) │ Edge Node (São Paulo) │
└───────────┬─────────────────┬───────────────────┬──────────────┘
            │                 │                   │
            ▼                 ▼                   ▼
       Viewer (JP)       Viewer (UK)        Viewer (BR)
   ABR stream → cache   ABR stream → cache  ABR stream → cache
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  Key Takeaways for Mobile Developers
&lt;/h2&gt;

&lt;p&gt;Building a React Native app with media features? Steal these patterns:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Local-first&lt;/strong&gt;: Always write to local storage before assuming network success&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Atomic draft saves&lt;/strong&gt;: Video file + metadata must succeed together or both fail&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Chunked uploads&lt;/strong&gt;: Never send large files as single requests; build retry logic per chunk&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Background transfers&lt;/strong&gt;: Use OS-level APIs, not in-app timers&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Queue your uploads&lt;/strong&gt;: Persist queue state to SQLite; recover from kills gracefully&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Multi-level caching&lt;/strong&gt;: Memory + disk + prefetch = fast perceived performance&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;CDN for media, API for metadata&lt;/strong&gt;: Your app servers shouldn't serve video files&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Adaptive quality&lt;/strong&gt;: Detect connection quality and degrade gracefully&lt;/li&gt;
&lt;/ol&gt;




&lt;h2&gt;
  
  
  Wrapping Up
&lt;/h2&gt;

&lt;p&gt;What looks like a simple "save" or "post" action is actually a cascade of decisions made across every layer of the stack: memory buffers, SQLite databases, upload queues, processing pipelines, object storage, CDN edge networks, and device-side caches.&lt;/p&gt;

&lt;p&gt;Understanding this architecture helps you build mobile apps that feel fast, handle failures gracefully, and respect your users' devices and data connections. Architecture and product thinking are inseparable — every engineering decision is ultimately a UX decision.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;What would you like to explore next — building a resumable upload queue in React Native, implementing multi-level caching, or designing a local draft system? Let me know in the comments!&lt;/em&gt;&lt;/p&gt;

</description>
      <category>reactnative</category>
      <category>mobiledev</category>
      <category>systemdesign</category>
      <category>architecture</category>
    </item>
  </channel>
</rss>
