<?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: Nikhil Sharma</title>
    <description>The latest articles on DEV Community by Nikhil Sharma (@nikhilsharma6).</description>
    <link>https://dev.to/nikhilsharma6</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%2F3222504%2F0c35495e-0c52-43f3-a92d-07854e3cd0e0.jpg</url>
      <title>DEV Community: Nikhil Sharma</title>
      <link>https://dev.to/nikhilsharma6</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/nikhilsharma6"/>
    <language>en</language>
    <item>
      <title>How Messages Actually Flow in a Custom Real-Time Chat App</title>
      <dc:creator>Nikhil Sharma</dc:creator>
      <pubDate>Thu, 09 Jul 2026 18:54:19 +0000</pubDate>
      <link>https://dev.to/nikhilsharma6/how-messages-actually-flow-in-a-custom-real-time-chat-app-4klb</link>
      <guid>https://dev.to/nikhilsharma6/how-messages-actually-flow-in-a-custom-real-time-chat-app-4klb</guid>
      <description>&lt;p&gt;When I set out to build Relay, I had one clear goal: I wanted to truly understand what happens under the hood when you hit "send" on an app like WhatsApp or Telegram. No Firebase, no Socket.IO, no magic. Just raw WebSockets, Node.js, and a PostgreSQL database. &lt;/p&gt;

&lt;p&gt;In my previous project updates, I outlined the high-level architecture. Today, I want to go deeper. This is a deep dive into the 5-step message send flow—expanding on the sequence of events, the ACK pattern, and how the system gracefully handles offline users. &lt;/p&gt;

&lt;p&gt;If you're curious about the mechanics of real-time distributed systems, grab a coffee. Let's dissect how a message actually gets from User A to User B.&lt;/p&gt;

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

&lt;p&gt;Before we dive into the code, let's visualize the flow. Here is the exact sequence of events when a user sends a message:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F492fo7hrsv3cjvbjnh2m.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F492fo7hrsv3cjvbjnh2m.png" alt="Architecture Diagram" width="800" height="428"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;(Note: In a local single-instance setup, Server A and Server B are the same server, but relying on Redis Pub/Sub means this scales horizontally out of the box).&lt;/em&gt;&lt;br&gt;
P.S. Sorry about the blurry architecture diagram—I couldn't get it to export at a higher resolution. I'll replace it with a better version soon.&lt;/p&gt;


&lt;h2&gt;
  
  
  Step 1: The Frontend Send (JSON Frames)
&lt;/h2&gt;

&lt;p&gt;With raw WebSockets, you don't get the luxury of Socket.IO's fancy event emitters. Everything sent over the wire is just a string. In Relay, I use JSON frames with a discriminated union structure to strictly type all messages.&lt;/p&gt;

&lt;p&gt;When you hit "Send" in the React frontend, it triggers the &lt;code&gt;sendMessage&lt;/code&gt; function inside my custom &lt;code&gt;useWebSocket&lt;/code&gt; hook:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;sendMessage&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;useCallback&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;to&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="kc"&gt;undefined&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;content&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;groupId&lt;/span&gt;&lt;span class="p"&gt;?:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="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;socketRef&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;current&lt;/span&gt;&lt;span class="p"&gt;?.&lt;/span&gt;&lt;span class="nx"&gt;readyState&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="nx"&gt;WebSocket&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;OPEN&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;groupId&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="c1"&gt;// Group message logic...&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;to&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nx"&gt;socketRef&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;current&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="nx"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;stringify&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
          &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;send_message&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
          &lt;span class="na"&gt;payload&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;to&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="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="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;It’s completely asynchronous from the frontend's perspective. We just fire the JSON frame into the void and trust the WebSocket connection to carry it. &lt;/p&gt;




&lt;h2&gt;
  
  
  Step 2: The Backend Handler &amp;amp; Database Persistence
&lt;/h2&gt;

&lt;p&gt;When the backend receives this frame, the &lt;code&gt;send_message&lt;/code&gt; event is routed to my message handler. The handler does two critical things simultaneously: it persists the message to PostgreSQL and publishes it to Redis.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;sendMessage&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="nx"&gt;senderId&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;msg&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;Extract&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;WsMessage&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;send_message&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="o"&gt;&amp;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="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;to&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;=&lt;/span&gt; &lt;span class="nx"&gt;msg&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;payload&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="c1"&gt;// 1. Check if user is online to determine delivery status&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;deliveredAt&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;presence&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;isOnline&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;to&lt;/span&gt;&lt;span class="p"&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="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="c1"&gt;// 2. Persist to Postgres via Prisma&lt;/span&gt;
  &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;saveMessage&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;to&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;senderId&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="nx"&gt;msg&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;payload&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;deliveredAt&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;sender&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;prisma&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="nf"&gt;findUnique&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;where&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;senderId&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;

  &lt;span class="c1"&gt;// 3. Publish to Redis Pub/Sub for cross-instance delivery&lt;/span&gt;
  &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;pubsub&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;publishMessage&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
    &lt;span class="na"&gt;userId&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;to&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;payload&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;receive_message&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;payload&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="na"&gt;from&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;senderId&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;sender&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;content&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;msg&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;payload&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;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="p"&gt;});&lt;/span&gt;

  &lt;span class="c1"&gt;// (Delivery acknowledgment logic for the sender omitted for brevity)&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Notice the &lt;code&gt;presence.isOnline(to)&lt;/code&gt; check. I am definitively recording whether this message is undelivered or delivered right at the moment of creation. If the user is offline, &lt;code&gt;deliveredAt&lt;/code&gt; is set to &lt;code&gt;null&lt;/code&gt;.&lt;/p&gt;




&lt;h2&gt;
  
  
  Step 3: The Redis Pub/Sub Delivery
&lt;/h2&gt;

&lt;p&gt;Why use Redis here instead of just looking up the recipient in an in-memory &lt;code&gt;Map&lt;/code&gt; of active WebSockets?&lt;/p&gt;

&lt;p&gt;If you rely solely on a local &lt;code&gt;Map&amp;lt;userId, WebSocket&amp;gt;&lt;/code&gt;, your chat app can never scale beyond a single Node.js process. If User A connects to Server 1 and User B connects to Server 2, Server 1 has no way to natively push a message to User B's socket.&lt;/p&gt;

&lt;p&gt;By publishing the &lt;code&gt;receive_message&lt;/code&gt; payload to a Redis channel, &lt;em&gt;every&lt;/em&gt; backend instance hears it. The instance that actually holds User B's live WebSocket connection intercepts the Redis event and pushes it down the wire:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"receive_message"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"payload"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"from"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"user-a-uuid"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"content"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Hey, what's up?"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"msg-1234"&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  Step 4: The ACK Pattern
&lt;/h2&gt;

&lt;p&gt;Here’s a subtle but crucial detail of building raw WebSocket apps: &lt;strong&gt;How does the sender know the server actually received and processed the message?&lt;/strong&gt; &lt;/p&gt;

&lt;p&gt;HTTP has status codes. You make a POST request, you get a &lt;code&gt;201 Created&lt;/code&gt; back. WebSockets, however, are full-duplex streams. When the client calls &lt;code&gt;socket.send()&lt;/code&gt;, there is no automatic confirmation. &lt;/p&gt;

&lt;p&gt;To solve this, I implemented an ACK (acknowledgment) pattern in the main socket listener.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="nx"&gt;ws&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;on&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;message&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;async &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;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;msg&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;WsMessageSchema&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;parse&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;parse&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;toString&lt;/span&gt;&lt;span class="p"&gt;()));&lt;/span&gt;

    &lt;span class="k"&gt;switch &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;msg&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="kd"&gt;type&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;send_message&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;await&lt;/span&gt; &lt;span class="nf"&gt;sendMessage&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="nx"&gt;msg&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// DB save and Redis publish happens here&lt;/span&gt;

        &lt;span class="c1"&gt;// Return the ACK to the sender&lt;/span&gt;
        &lt;span class="nx"&gt;ws&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="nx"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;stringify&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;ack&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;payload&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;status&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;sent&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="k"&gt;break&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
      &lt;span class="p"&gt;}&lt;/span&gt;
      &lt;span class="c1"&gt;// ... other cases&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;e&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;ws&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="nx"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;stringify&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;error&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;payload&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;message&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Failed to process message&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;When the frontend receives this &lt;code&gt;{ type: "ack" }&lt;/code&gt;, it knows the message safely made it to the database. Before this ACK, the message should technically show a "sending..." state (like a clock icon) in the UI. Once the ACK hits, we transition to a single checkmark (Sent).&lt;/p&gt;




&lt;h2&gt;
  
  
  Step 5: What Happens When the Recipient is Offline?
&lt;/h2&gt;

&lt;p&gt;If the recipient is completely offline (their phone is off, tab is closed), the Redis publish happens, but no server instance picks it up to deliver via WebSocket. &lt;/p&gt;

&lt;p&gt;Because &lt;code&gt;presence.isOnline(to)&lt;/code&gt; returned &lt;code&gt;false&lt;/code&gt; back in Step 2, the database row has &lt;code&gt;deliveredAt: null&lt;/code&gt;. &lt;/p&gt;

&lt;p&gt;When the recipient finally opens the app, two things happen:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;REST History Fetch&lt;/strong&gt;: The frontend fetches the chat history via a standard HTTP REST endpoint (&lt;code&gt;/messages&lt;/code&gt;). I use HTTP for history because it's stateless, easily cacheable, and better suited for loading large JSON arrays than WebSocket frames.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Delivery Confirmation&lt;/strong&gt;: During the WebSocket connection handshake for the newly online user, the backend runs a cleanup task:
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Inside the WebSocket connection handler&lt;/span&gt;
&lt;span class="nx"&gt;sockets&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;addUser&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="nx"&gt;ws&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="c1"&gt;// Find all messages sent to this user where deliveredAt is null&lt;/span&gt;
&lt;span class="c1"&gt;// Update them with the current timestamp and notify senders&lt;/span&gt;
&lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;markMessagesAsDelivered&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;markMessagesAsDelivered&lt;/code&gt; queries the database for all pending messages, updates them, and then fires off &lt;code&gt;delivered_messages&lt;/code&gt; events through Redis back to the original senders. The sender's UI instantly flips from a single checkmark to a double checkmark, knowing their message has finally landed on the recipient's device.&lt;/p&gt;

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

&lt;p&gt;As I said in the previous post in the series, abstracting WebSockets with tools like Socket.IO is great for getting an MVP out the door quickly. But building the message framing, persistence logic, ACK patterns, and Redis pub/sub layer yourself gives you an unparalleled understanding of system design. &lt;/p&gt;

&lt;p&gt;You stop seeing a chat app as "magic" and start seeing it as a beautifully orchestrated dance of database rows, pub/sub channels, and TCP streams.&lt;/p&gt;

&lt;p&gt;If you're building something similar or want to dive into the codebase, Relay is completely &lt;a href="https://github.com/nikshrma/relay" rel="noopener noreferrer"&gt;open-source&lt;/a&gt;. Feel free to reach out in the comments!&lt;/p&gt;

&lt;p&gt;I'll be back with more next week. Until then, stay consistent!&lt;/p&gt;

</description>
      <category>javascript</category>
      <category>react</category>
      <category>beginners</category>
      <category>webdev</category>
    </item>
    <item>
      <title>Why I Ditched Socket.IO for Raw WebSockets (And What I Learned)</title>
      <dc:creator>Nikhil Sharma</dc:creator>
      <pubDate>Sat, 04 Jul 2026 06:34:18 +0000</pubDate>
      <link>https://dev.to/nikhilsharma6/why-i-ditched-socketio-for-raw-websockets-and-what-i-learned-55i6</link>
      <guid>https://dev.to/nikhilsharma6/why-i-ditched-socketio-for-raw-websockets-and-what-i-learned-55i6</guid>
      <description>&lt;p&gt;When you google "how to build a chat app in Node.js," the very first result will almost certainly point you to Socket.IO. It is the de facto standard for a reason. When I started my project, I used it without a second thought. It worked like magic.  &lt;/p&gt;

&lt;p&gt;But as I got deeper into the project, that magic started to feel more like a black box. I eventually ripped out Socket.IO and replaced it with raw, native WebSockets. It was a daunting decision, but having built and managed it myself, I have some strong opinions on what Socket.IO abstracts away, what I had to build from scratch, and whether the headache was actually worth it.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Magic of Socket.IO (And Why We Use It)
&lt;/h2&gt;

&lt;p&gt;To understand why walking away from Socket.IO is hard, you have to understand exactly how much heavy lifting it does for you behind the scenes. It isn't just a WebSocket library; it is a real-time framework.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;The Polling Fallback:&lt;/strong&gt; Historically, if a user's corporate firewall blocked WebSockets, Socket.IO would seamlessly downgrade to HTTP long-polling. &lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Automatic Reconnections:&lt;/strong&gt; If a user drives through a tunnel and loses the connection, Socket.IO automatically handles the exponential backoff to reconnect them when they emerge.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Rooms and Namespaces:&lt;/strong&gt; It gives you a beautiful &lt;code&gt;socket.to("room-1").emit()&lt;/code&gt; API for broadcasting messages to specific groups of users.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Heartbeats:&lt;/strong&gt; It manages ping/pong messages under the hood to ensure the connection hasn't silently died.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;When you drop Socket.IO, you lose all of this for free. &lt;/p&gt;

&lt;h2&gt;
  
  
  So, Why Did I Walk Away?
&lt;/h2&gt;

&lt;p&gt;First, the fallback mechanism is largely a relic of the past. Today, native WebSocket support across modern browsers and network infrastructure is essentially ubiquitous. I didn't need to ship a massive client bundle just to support HTTP polling for the 0.1% of edge cases.&lt;/p&gt;

&lt;p&gt;Second, the lock-in is real. If you use Socket.IO on the client, you &lt;em&gt;must&lt;/em&gt; use a Socket.IO server implementation. You can't just connect to a standard WebSocket server. I wanted the freedom to swap out my backend without being tied to the Socket.IO ecosystem. &lt;/p&gt;

&lt;p&gt;Most importantly, I hated not understanding my own system. When a connection failed in Socket.IO, debugging the complex state machine of fallbacks and upgrades was a nightmare. I wanted full control over the wire.&lt;/p&gt;

&lt;h2&gt;
  
  
  Building It Myself: What I Had to Think About
&lt;/h2&gt;

&lt;p&gt;Moving to raw WebSockets meant I was suddenly responsible for the entire connection lifecycle. Here is exactly what I had to build to replicate the "magic."&lt;/p&gt;

&lt;h3&gt;
  
  
  1. The Reconnection Engine
&lt;/h3&gt;

&lt;p&gt;Native WebSockets do not reconnect when they drop. If the connection closes, it stays closed. I had to write a custom wrapper that implemented exponential backoff.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tsx"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Initializing refs to track reconnection state&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;reconnectAttemptsRef&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;useRef&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="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;reconnectTimeoutRef&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;useRef&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kr"&gt;number&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;shouldReconnectRef&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;useRef&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;connect&lt;/span&gt; &lt;span class="o"&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="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;socket&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;WebSocket&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;WS_URL&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="nx"&gt;socket&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;onopen&lt;/span&gt; &lt;span class="o"&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="c1"&gt;// Reset backoff attempts on successful connection&lt;/span&gt;
    &lt;span class="nx"&gt;reconnectAttemptsRef&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;current&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;};&lt;/span&gt;

  &lt;span class="c1"&gt;// ... (message handling logic)&lt;/span&gt;

  &lt;span class="nx"&gt;socket&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;onclose&lt;/span&gt; &lt;span class="o"&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="c1"&gt;// Don't reconnect if the component is unmounting&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;shouldReconnectRef&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;current&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="c1"&gt;// Exponential backoff: 1s, 2s, 4s, 8s, up to a maximum of 30s&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;delay&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;Math&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;min&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1000&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt; &lt;span class="o"&gt;**&lt;/span&gt; &lt;span class="nx"&gt;reconnectAttemptsRef&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;current&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;30000&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nx"&gt;reconnectAttemptsRef&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;current&lt;/span&gt;&lt;span class="o"&gt;++&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="nx"&gt;reconnectTimeoutRef&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;current&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;window&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="nf"&gt;connect&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="nx"&gt;delay&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;`Reconnecting in &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;delay&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;ms...`&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;};&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;

&lt;span class="c1"&gt;// Cleanup on unmount&lt;/span&gt;
&lt;span class="nf"&gt;useEffect&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="c1"&gt;// ...&lt;/span&gt;
  &lt;span class="k"&gt;return &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;shouldReconnectRef&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;current&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;false&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;reconnectTimeoutRef&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;current&lt;/span&gt; &lt;span class="o"&gt;!==&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nf"&gt;clearTimeout&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;reconnectTimeoutRef&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;current&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="nx"&gt;socketRef&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;current&lt;/span&gt;&lt;span class="p"&gt;?.&lt;/span&gt;&lt;span class="nf"&gt;close&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="nx"&gt;user&lt;/span&gt;&lt;span class="p"&gt;]);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  2. Custom Payload Routing
&lt;/h3&gt;

&lt;p&gt;Socket.IO lets you do &lt;code&gt;socket.on('new_message', handler)&lt;/code&gt;. Raw WebSockets only give you a single &lt;code&gt;onmessage&lt;/code&gt; event with a text payload. I had to build my own event router using standard JSON parsing and switch statements.&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;ws&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;onmessage&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;event&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="nx"&gt;payload&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;event&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="k"&gt;switch &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;payload&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;type&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;NEW_MESSAGE&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
      &lt;span class="nf"&gt;handleNewMessage&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;payload&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="k"&gt;break&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;USER_TYPING&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
      &lt;span class="nf"&gt;handleUserTyping&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;payload&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="k"&gt;break&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;h2&gt;
  
  
  Wrapping up
&lt;/h2&gt;

&lt;p&gt;You may ask was it worth it?&lt;br&gt;
I would say it absolutely was. &lt;/p&gt;

&lt;p&gt;Yes, it took a few days to write robust wrapper classes for reconnects and message routing. But once it was built, the benefits were immediate.&lt;/p&gt;

&lt;p&gt;My client bundle size dropped significantly. My backend was no longer coupled to a specific framework, allowing me to route raw WebSockets through standard load balancers with ease. But the biggest benefit was mental clarity. When a connection drops now, there is no magic "black box" trying to fix it behind the scenes. I know exactly how my code reacts, I know exactly what bytes are going over the wire, and debugging is as simple as reading my own logs.&lt;/p&gt;

&lt;p&gt;If you are at a hackathon and need real-time features done yesterday, use Socket.IO. It is brilliant for rapid prototyping. But if you are building a production system and want deep, fundamental control over your architecture, raw WebSockets are nowhere near as scary as people make them out to be. Doing it the hard way is how you actually learn the web.&lt;/p&gt;

&lt;p&gt;Again, you can check out the codebase on my &lt;a href="https://github.com/nikshrma" rel="noopener noreferrer"&gt;github&lt;/a&gt; and the live deployment &lt;a href="https://relay.nikshrma.dev" rel="noopener noreferrer"&gt;here&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;I'll be back with more next week. Till then, stay consistent!&lt;/p&gt;

</description>
      <category>beginners</category>
      <category>react</category>
      <category>javascript</category>
      <category>webdev</category>
    </item>
    <item>
      <title>Why I Chose a Dual Protocol Architecture: REST + WebSockets</title>
      <dc:creator>Nikhil Sharma</dc:creator>
      <pubDate>Thu, 25 Jun 2026 06:50:51 +0000</pubDate>
      <link>https://dev.to/nikhilsharma6/why-i-chose-a-dual-protocol-architecture-rest-websockets-4co0</link>
      <guid>https://dev.to/nikhilsharma6/why-i-chose-a-dual-protocol-architecture-rest-websockets-4co0</guid>
      <description>&lt;p&gt;When I first started building the real-time chat features for my project, the initial architectural decisions seemed quite simple to me. It was going to be a "real-time" app so 'obviously' I needed WebSockets. My first instinct was to use a socket event for everything whether it be authentication or even loading chat history.&lt;br&gt;
Eventually, I read some articles pertaining to the same and then shifted to the dual architecture which is in place now. Which is REST+WebSockets and using each protocol for what its good at. &lt;/p&gt;
&lt;h2&gt;
  
  
  The common first thought
&lt;/h2&gt;

&lt;p&gt;It is incredibly tempting to establish a WebSocket connection and route the entire application state through it. It feels clean to have a single pipe to the server. &lt;/p&gt;

&lt;p&gt;But I quickly ran into the architectural realities of stateful WebSocket connections. While they are good for maintaining persistent duplex connections, they provide a much lower-level transport abstraction than HTTP.&lt;/p&gt;

&lt;p&gt;On fetching the older chat history, I immediately missed standard HTTP status codes. If a request failed, I had to myself make my own error-handling payload structure as there was no automated 404 or 401 code being sent. As in WebSockets, after a connection is established, there are no HTTP responses, its all just messages. So I lost out on all standard HTTP semantics, which meant a lot more work for me.&lt;/p&gt;

&lt;p&gt;WebSockets are inherently stateful which makes load balancing and scaling much more complex, each connection has to stay attached to a server, so horizontally scaling requires connection-aware load balancing and some shared mechanism (such as Redis Pub-Sub) to broadcast events across instances as compared to spinning up stateless REST APIs behind a standard reverse proxy.&lt;/p&gt;
&lt;h2&gt;
  
  
  Why Not Just HTTP Polling?
&lt;/h2&gt;

&lt;p&gt;On the other end of the spectrum I very briefly considered avoiding WebSockets entirely. Why not just use REST for everything and implement polling?&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// The classic polling trap&lt;/span&gt;
&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;startPolling&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;roomId&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nf"&gt;setInterval&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;async &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="nx"&gt;newMessages&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;fetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`/api/rooms/&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;roomId&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;/messages?since=&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;lastTimestamp&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="c1"&gt;// Handle new messages...&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="mi"&gt;3000&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The issue with polling became apparent the moment I thought about the user experience and server load. With short polling I would be sending HTTP requests every few seconds. And 90% of the time the server's response would be an empty array because no new messages had arrived. This results in massive HTTP overhead, unnecessary server strain and an inherent latency equal to the polling interval, which I just did not like seeing when I tested a dummy app.&lt;/p&gt;

&lt;p&gt;Even long-polling (where the server holds the request open until a message arrives) felt like a hacky workaround to simulate the persistent connection that WebSockets natively provide. &lt;/p&gt;

&lt;p&gt;And so I instantly knew that polling, long or short, is not an option for my case.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Ideal Architecture
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;            React Client
                 │
      ┌──────────┴──────────┐
      │                     │
      ▼                     ▼
 REST (Request/Response)   WebSocket (Events)
      │                     │
      │                     │
 Load state            Live updates
(Login, Users,         (Messages,
 History, etc.)        Typing, Presence)
      └──────────┬──────────┘
                 ▼
         Backend + Database
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I eventually landed on a dual-protocol approach. The mechanism was easy to understand, anything that is coming out of the database(essentially older stuff) comes from the HTTP side of things, anything realtime comes from the WS side of things.&lt;/p&gt;

&lt;h3&gt;
  
  
  1. REST for initial state
&lt;/h3&gt;

&lt;p&gt;I use standard REST endpoints for operations that follow a Request-Response model. &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Fetching a user's profile.&lt;/li&gt;
&lt;li&gt;Fetching the list of users.&lt;/li&gt;
&lt;li&gt;Loading the initial chat history for an open chat.&lt;/li&gt;
&lt;li&gt;Authenticating the user.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;By doing this I could keep my backend mostly stateless for the heavy queries.&lt;/p&gt;

&lt;h3&gt;
  
  
  2. WebSockets for the Real-Time Pipeline
&lt;/h3&gt;

&lt;p&gt;Once the static state is loaded via REST, I open a WebSocket connection strictly as an event pipeline. The socket's only job is to push real-time events from the server to the client.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;new message&lt;/li&gt;
&lt;li&gt;typing event&lt;/li&gt;
&lt;li&gt;isOnline event&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Here is a simplified snippet(not from the codebase because there is a lot of other stuff going on there) of how I orchestrate this dual approach when a user enters a chat room:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;enterChatRoom&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;roomId&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="c1"&gt;// 1. Fetch the static history via REST&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;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;fetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`/api/rooms/&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;roomId&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;/history`&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;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;ok&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Failed to load history&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;pastMessages&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;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="nf"&gt;setMessages&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;pastMessages&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;error&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nf"&gt;handleRestError&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="c1"&gt;// 2. Open the WebSocket strictly for real-time events&lt;/span&gt;
  &lt;span class="c1"&gt;// We get low-latency, bi-directional communication without HTTP overhead&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;ws&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;WebSocket&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`wss://api.myproject.com/rooms/&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;roomId&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;/live`&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="nx"&gt;ws&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;onmessage&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;event&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="nx"&gt;data&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;parse&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;event&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="k"&gt;if &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="kd"&gt;type&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;NEW_MESSAGE&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;setMessages&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;prev&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;prev&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="nx"&gt;message&lt;/span&gt;&lt;span class="p"&gt;]);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="kd"&gt;type&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;USER_TYPING&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;showTypingIndicator&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="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="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="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;ws&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;close&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;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;Building this project taught me that just because a technology &lt;em&gt;can&lt;/em&gt; do something doesn't mean it &lt;em&gt;should&lt;/em&gt;. WebSockets are an incredible tool for real-time, low-latency communication, but they are a poor substitute for the robust, heavily-standardized world of RESTful HTTP. &lt;/p&gt;

&lt;p&gt;By utilizing a dual-protocol architecture, I avoided the heavy overhead of HTTP polling while simultaneously saving myself from the nightmare of reinventing routing, caching, and error handling over a raw socket connection. &lt;/p&gt;

&lt;p&gt;I'll be back with more next week. Till then, stay consistent!&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>javascript</category>
      <category>beginners</category>
      <category>react</category>
    </item>
    <item>
      <title>The Stale Closure Bug That Haunted My Chat App</title>
      <dc:creator>Nikhil Sharma</dc:creator>
      <pubDate>Sun, 14 Jun 2026 05:53:19 +0000</pubDate>
      <link>https://dev.to/nikhilsharma6/the-stale-closure-bug-that-haunted-my-chat-app-khp</link>
      <guid>https://dev.to/nikhilsharma6/the-stale-closure-bug-that-haunted-my-chat-app-khp</guid>
      <description>&lt;p&gt;Building a real-time chat app sounds straightforward until React's rendering model meets WebSockets . In Relay, I built what I thought was a solid real-time integration. Everything seemed to work perfectly—until I started switching between conversations.&lt;/p&gt;

&lt;h2&gt;
  
  
  What I Built
&lt;/h2&gt;

&lt;p&gt;In Relay, I wanted a clean separation of concerns, so I extracted my WebSocket logic into a custom hook called useWebSocket. It takes an onMessage callback so my Chat UI can decide how to handle incoming messages:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tsx"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Chat.tsx&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;sendMessage&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;useWebSocket&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;msg&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="c1"&gt;// If the message is from the person I'm actively chatting with, add it to the window&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;selectedUser&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nx"&gt;msg&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;senderId&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="nx"&gt;selectedUser&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="nf"&gt;addMessage&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;msg&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="c1"&gt;// Otherwise, show a toast notification&lt;/span&gt;
  &lt;span class="nf"&gt;toast&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;msg&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;sender&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="nx"&gt;msg&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="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;Inside useWebSocket.ts, I wired up the actual WebSocket connection inside a useEffect:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tsx"&gt;&lt;code&gt;&lt;span class="c1"&gt;// useWebSocket.ts (The broken version)&lt;/span&gt;
&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;useWebSocket&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;onMessage&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;msg&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Message&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="k"&gt;void&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;user&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;useAuth&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="nf"&gt;useEffect&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="nx"&gt;socket&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;WebSocket&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;WS_URL&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="nx"&gt;socket&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;onmessage&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;e&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="nx"&gt;msg&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;e&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="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;msg&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="kd"&gt;type&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;receive_message&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;onMessage&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;msg&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;payload&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="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="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;socket&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;close&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="nx"&gt;user&lt;/span&gt;&lt;span class="p"&gt;]);&lt;/span&gt; &lt;span class="c1"&gt;// Only reconnect if the authenticated user changes&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  What Broke
&lt;/h2&gt;

&lt;p&gt;At first glance, this code looks entirely reasonable. I'd open Relay, click on Alice's profile, and we'd start chatting. It worked flawlessly.&lt;/p&gt;

&lt;p&gt;Then, I'd switch my active chat to Bob. Bob would send me a message, but instead of the message appearing in Bob's chat window, a toast notification would pop up. Even worse, if Alice sent me a message while I was looking at Bob's chat, her message silently injected itself directly into Bob's chat window!&lt;/p&gt;

&lt;p&gt;I was literally watching the messages of one user show up in another user's chat after I switched.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why It Broke (The Mental Model)
&lt;/h2&gt;

&lt;p&gt;The culprit was a classic React gotcha: the stale closure.&lt;/p&gt;

&lt;p&gt;When the useWebSocket hook ran for the first time, the useEffect established the WebSocket connection. The socket.onmessage event listener was created, and it permanently "captured" the onMessage function from that specific initial render.&lt;/p&gt;

&lt;p&gt;In that first render, selectedUser was Alice.&lt;/p&gt;

&lt;p&gt;When I clicked on Bob in the sidebar, React re-rendered my Chat component. A brand new onMessage function was created where selectedUser was Bob. However, because my useEffect dependency array only included &lt;a href="https://dev.tothe%20logged-in%20auth%20user,%20not%20the%20selected%20chat%20partner"&gt;user&lt;/a&gt;, the effect didn't re-run. The WebSocket stayed connected—which is what we want.&lt;/p&gt;

&lt;p&gt;But this meant the socket.onmessage listener was still holding onto the very first onMessage function it captured—the one where selectedUser is permanently frozen as Alice.&lt;/p&gt;

&lt;p&gt;If I simply added onMessage to the useEffect dependency array to fix the stale state, the WebSocket would brutally disconnect and reconnect every single time I clicked a new user, typed a character, or triggered a re-render. That's terrible UX and a massive performance killer.&lt;/p&gt;

&lt;h2&gt;
  
  
  The useRef Fix
&lt;/h2&gt;

&lt;p&gt;I needed a way for the WebSocket listener to always access the freshest version of the onMessage callback, without actually triggering the useEffect to tear down the socket.&lt;/p&gt;

&lt;p&gt;Enter useRef.&lt;/p&gt;

&lt;p&gt;Unlike state, updating a ref doesn't trigger a re-render. More importantly, a ref is a mutable object that persists across renders. By continuously storing the latest callback in a ref, the WebSocket listener can just peek into ref.current and always find the most up-to-date function.&lt;/p&gt;

&lt;p&gt;Here is the exact code that fixed the bug in Relay:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tsx"&gt;&lt;code&gt;&lt;span class="c1"&gt;// useWebSocket.ts (The fixed version)&lt;/span&gt;
&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;useWebSocket&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;onMessage&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;msg&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Message&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="k"&gt;void&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;user&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;useAuth&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

  &lt;span class="c1"&gt;// 1. Create a ref to hold the callback&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;onMessageRef&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;useRef&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;onMessage&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="c1"&gt;// 2. Silently update the ref every time the callback changes (on every render)&lt;/span&gt;
  &lt;span class="nf"&gt;useEffect&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;onMessageRef&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;current&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;onMessage&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="nx"&gt;onMessage&lt;/span&gt;&lt;span class="p"&gt;]);&lt;/span&gt;
  &lt;span class="nf"&gt;useEffect&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="nx"&gt;socket&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;WebSocket&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;WS_URL&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="nx"&gt;socket&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;onmessage&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;e&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="nx"&gt;msg&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;e&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="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;msg&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="kd"&gt;type&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;receive_message&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="c1"&gt;// 3. Always call the freshest callback stored in the ref!&lt;/span&gt;
        &lt;span class="nx"&gt;onMessageRef&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;current&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;msg&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;payload&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="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="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;socket&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;close&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="nx"&gt;user&lt;/span&gt;&lt;span class="p"&gt;]);&lt;/span&gt; 
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  The Takeaway
&lt;/h2&gt;

&lt;p&gt;Whenever you integrate long-lived event listeners (like WebSockets, window events, or setInterval) with React components, you have to be paranoid about what variables are being captured in their closures. If you need access to constantly changing React state inside a permanent event listener, the useRef escape hatch is your best friend.&lt;/p&gt;

&lt;p&gt;I'll be back with more next week. Until then, stay consistent!&lt;/p&gt;

&lt;h2&gt;
  
  
  Sidenote
&lt;/h2&gt;

&lt;p&gt;Relay is now live: &lt;a href="https://relay.nikshrma.dev" rel="noopener noreferrer"&gt;https://relay.nikshrma.dev&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;It's running on a DigitalOcean droplet and has been a great playground for learning WebSockets, authentication, testing, and deployment.&lt;/p&gt;

&lt;p&gt;You can also find my portfolio at &lt;a href="https://nikshrma.dev" rel="noopener noreferrer"&gt;https://nikshrma.dev&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>beginners</category>
      <category>react</category>
      <category>javascript</category>
      <category>webdev</category>
    </item>
    <item>
      <title>The WebSocket Auth Problem: Cookies vs. Bearer Tokens</title>
      <dc:creator>Nikhil Sharma</dc:creator>
      <pubDate>Wed, 03 Jun 2026 20:23:14 +0000</pubDate>
      <link>https://dev.to/nikhilsharma6/the-websocket-auth-problem-cookies-vs-bearer-tokens-4eel</link>
      <guid>https://dev.to/nikhilsharma6/the-websocket-auth-problem-cookies-vs-bearer-tokens-4eel</guid>
      <description>&lt;p&gt;If you've ever built a real-time web app using WebSockets, you've probably hit this exact wall - how do I do the auth.&lt;/p&gt;

&lt;p&gt;On the HTTP side, the answer is pretty simple. You either use a cookie, which has so many npm packages, or you attach an &lt;code&gt;Authorization: Bearer &amp;lt;token&amp;gt;&lt;/code&gt; header to your requests, which is a simple JWT signed string. However, things get a bit weird on the WebSocket side of things.&lt;/p&gt;

&lt;p&gt;The fundamental issue stems from how WebSocket connections are established. Before a WebSocket connection becomes a continuous bidirectional channel of data, it starts off as a simple HTTP &lt;code&gt;GET&lt;/code&gt; request with an &lt;code&gt;Upgrade&lt;/code&gt; header. This is known as the WebSocket Handshake.&lt;/p&gt;

&lt;p&gt;Let's dive into why this handshake makes the seemingly simple task of authentication a surprisingly nuanced architectural decision.&lt;/p&gt;




&lt;h2&gt;
  
  
  Why Bearer Tokens are Clunky for WebSockets
&lt;/h2&gt;

&lt;p&gt;If your application relies on Bearer tokens (usually stored in memory or local storage on the client side), you are used to attaching them via the &lt;code&gt;Authorization&lt;/code&gt; header using an interceptor or a fetch wrapper.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// A typical HTTP request with a Bearer token&lt;/span&gt;
&lt;span class="nf"&gt;fetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/api/data&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;headers&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Authorization&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;`Bearer &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;myToken&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;However, the native browser &lt;code&gt;WebSocket&lt;/code&gt; API &lt;strong&gt;does not support custom headers&lt;/strong&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// This is all we get.&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;ws&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;WebSocket&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;wss://api.myapp.com/socket&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;Since you can't pass the token in an &lt;code&gt;Authorization&lt;/code&gt; header during the initial HTTP Upgrade request, developers are forced into clunky workarounds:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Query Parameters&lt;/strong&gt;: Passing the token in the URL (&lt;code&gt;wss://api.myapp.com/socket?token=ey...&lt;/code&gt;). This is generally considered a bad practice because URLs (and the sensitive tokens within them) end up in server logs, browser histories, and proxy logs.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;First Message Auth&lt;/strong&gt;: Opening the WebSocket connection unauthenticated, and then requiring the client to immediately send an authentication message containing the token before the server accepts any further messages. While secure, this adds latency and complexity to the connection lifecycle logic. You have to write boilerplate to handle connections that open but never authenticate.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Subprotocols&lt;/strong&gt;: A hacky but working method where the token is passed as a WebSocket subprotocol (&lt;code&gt;new WebSocket('wss://...', ['access_token', token])&lt;/code&gt;).&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;None of these feel particularly clean or idiomatic.&lt;/p&gt;




&lt;h2&gt;
  
  
  Why Cookies Work Naturally
&lt;/h2&gt;

&lt;p&gt;This brings us to cookies, the method that I chose for &lt;a href="https://github.com/nikshrma/relay" rel="noopener noreferrer"&gt;Relay&lt;/a&gt;! Cookies are managed by the browser. Whenever the browser makes an HTTP request to a domain, it automatically attaches the cookies associated with that domain.&lt;/p&gt;

&lt;p&gt;Because the WebSocket handshake is just a standard HTTP &lt;code&gt;GET&lt;/code&gt; request, &lt;strong&gt;the browser automatically includes your cookies&lt;/strong&gt; in the first request itself!&lt;/p&gt;

&lt;p&gt;If your authentication system sets an HTTP-only cookie containing your JWT or session ID when the user logs in, you literally don't have to change any client-side code to authenticate your WebSockets. The browser handles it natively and securely.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// The browser automatically attaches the JWT auth cookie!&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;ws&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;WebSocket&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;wss://api.myapp.com/socket&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;h2&gt;
  
  
  The Backend Implementation: Parsing the Cookie
&lt;/h2&gt;

&lt;p&gt;While the client side becomes trivial, the backend need manual cookie parsing. When your WebSocket server receives the &lt;code&gt;upgrade&lt;/code&gt; request, it hands you a raw Node.js &lt;code&gt;IncomingMessage&lt;/code&gt; object. &lt;/p&gt;

&lt;p&gt;Unlike Express requests which come with the &lt;code&gt;cookies&lt;/code&gt; objects provided by middleware, the &lt;code&gt;IncomingMessage&lt;/code&gt; just gives you the raw &lt;code&gt;cookie&lt;/code&gt; header string. You have to parse it yourself before accepting the WebSocket connection.&lt;/p&gt;

&lt;p&gt;Below is how I did the cookie parsing in Relay!&lt;/p&gt;

&lt;h3&gt;
  
  
  1. Extracting and Verifying the Token
&lt;/h3&gt;

&lt;p&gt;First, we need a utility function to take the raw &lt;code&gt;IncomingMessage&lt;/code&gt;, parse the cookie string, and verify our JWT.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;cookie&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;cookie&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;jwt&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;jsonwebtoken&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="kd"&gt;type&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;IncomingMessage&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;http&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;jwtPayloadSchema&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;../../http/schemas/auth.schema.js&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="c1"&gt;// Extract user ID from the raw HTTP upgrade request&lt;/span&gt;
&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;extractUserId&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;IncomingMessage&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="c1"&gt;// Parse the raw cookie header string&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;cookies&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;cookie&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;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;cookie&lt;/span&gt; &lt;span class="o"&gt;??&lt;/span&gt; &lt;span class="dl"&gt;""&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;token&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;cookies&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;token&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="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;token&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="kc"&gt;null&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="c1"&gt;// Verify the JWT signature&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;decoded&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;jwt&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;verify&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;token&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;JWT_SECRET&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="c1"&gt;// Validate the payload shape&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;parsed&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;jwtPayloadSchema&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;safeParse&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;decoded&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="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;parsed&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;success&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="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="c1"&gt;// Return the authenticated User ID&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;parsed&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="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="k"&gt;catch&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;// Token is invalid or expired&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;
  
  
  2. The main Socket handling code
&lt;/h3&gt;

&lt;p&gt;Now here, I integrate this utility into my WebSocket server initialisation. I intercept the connection, attempt to extract the user ID, and immediately close the connection with a &lt;code&gt;4001 Unauthorized&lt;/code&gt; status if the authentication fails.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;WebSocketServer&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kd"&gt;type&lt;/span&gt; &lt;span class="nx"&gt;WebSocket&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;ws&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="kd"&gt;type&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;Server&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="nx"&gt;HttpServer&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;http&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;extractUserId&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;./handlers/message.handler.js&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;sockets&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;./store.js&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;initWebSocketServer&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;server&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;HttpServer&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;wss&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;WebSocketServer&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;server&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;

  &lt;span class="nx"&gt;wss&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;on&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;connection&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;ws&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;WebSocket&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;request&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;IncomingMessage&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;ws&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;on&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;error&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="c1"&gt;// 1. Authenticate immediately using the parsed cookie&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;extractUserId&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;request&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="c1"&gt;// 2. Reject unauthenticated connections at the handshake level&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;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;ws&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;close&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;4001&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Unauthorized&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="c1"&gt;// 3. Connection is fully authenticated!&lt;/span&gt;
    &lt;span class="c1"&gt;// We can safely associate this socket with the user&lt;/span&gt;
    &lt;span class="nx"&gt;sockets&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;addUser&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="nx"&gt;ws&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="c1"&gt;// Proceed with authorized application logic...&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;`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; connected successfully.`&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="nx"&gt;ws&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;on&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;message&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;async &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="c1"&gt;// ... handle incoming messages knowing the sender's true identity&lt;/span&gt;
    &lt;span class="p"&gt;});&lt;/span&gt;

    &lt;span class="nx"&gt;ws&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;on&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;close&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;sockets&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;removeUserSocket&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="nx"&gt;ws&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;h2&gt;
  
  
  Wrapping up
&lt;/h2&gt;

&lt;p&gt;When building real-time applications, relying on HTTP-only cookies for authentication eliminates the friction of securing WebSockets. By leaning on the browser's native cookie management, we avoid leaking tokens in URLs and bypass the complexity of first-message authentication handshakes. It requires a tiny bit of manual header parsing on the backend, but the resulting client-side simplicity and security posture are well worth the trade-off.&lt;/p&gt;

&lt;p&gt;For those of you who would like to go through the codebase, you can do so here.&lt;br&gt;
I'll be back with more next week. Until then, stay consistent!&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>javascript</category>
      <category>beginners</category>
      <category>react</category>
    </item>
    <item>
      <title>I Built a Real-Time Chat App from scratch - Here's What I Learned</title>
      <dc:creator>Nikhil Sharma</dc:creator>
      <pubDate>Tue, 26 May 2026 18:03:37 +0000</pubDate>
      <link>https://dev.to/nikhilsharma6/i-built-a-real-time-chat-app-from-scratch-heres-what-i-learned-2nhn</link>
      <guid>https://dev.to/nikhilsharma6/i-built-a-real-time-chat-app-from-scratch-heres-what-i-learned-2nhn</guid>
      <description>&lt;p&gt;When dealing with WebSockets, most tutorials will straightaway hand you Socket.io and call it a day. You get a working chat app in short period of time but you have no idea what happened.&lt;/p&gt;

&lt;p&gt;I actually wanted to understand what was going on, how WebSockets work, how auth happens, how to deal with cookies when working with WebSockets and so many more tiny things.&lt;br&gt;
So I built Relay. A full stack real-time chat app with raw WebSockets, Express for the backend, React for the frontend and PostgreSQL. &lt;br&gt;
The reason for doing everything manually was that I wanted to &lt;strong&gt;learn&lt;/strong&gt;. I did not want to launch and make a chat app and sell it. I just wanted to learn the implementation of these technologies.&lt;/p&gt;

&lt;p&gt;Thus, here we are. This is the first post in a series where I will be exploring the project and breaking down the most interesting technical problems I ran into. But first, let's see what Relay is, how it works, and why I built it the way I did.&lt;/p&gt;

&lt;h2&gt;
  
  
  What Relay Does
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Real-time messaging&lt;/strong&gt; — Messages are sent and received instantly over WebSockets. No polling, no long-polling, no hacks. &lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Persistent history&lt;/strong&gt; — Every message is stored in PostgreSQL via Prisma ORM. Close the tab, logout, the messages will still be there when you come back.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Cookie-based auth&lt;/strong&gt; — JWT tokens stored in httpOnly cookies. The WebSocket handshake authenticates by manually parsing cookies from the upgrade request headers.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Dual-protocol architecture&lt;/strong&gt; — REST API(the express part) handles the CRUD stuff (auth, fetching users, loading message history). WebSockets handle the real-time stuff (sending messages, live delivery). Each protocol does what it's good at and that is exactly what I wanted to learn to do myself. And also the fact that how would I implement both these protocols in the same project for maximum efficiency.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Architecture and Flow
&lt;/h2&gt;

&lt;p&gt;The app follows a fairly standard real-time chat architecture with a clear separation between frontend and backend responsibilities.&lt;/p&gt;

&lt;p&gt;On the frontend, the application is built using React, TypeScript, and Vite. Authentication state is managed through an AuthContext, chat history is handled using a useMessages hook, and real-time communication is managed by a useWebSocket hook. The frontend communicates with the backend in two ways:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;REST API&lt;/strong&gt; calls using &lt;strong&gt;Axios&lt;/strong&gt; over HTTP for things like authentication, fetching users, loading messages, and logout.&lt;br&gt;
&lt;strong&gt;WebSocket connections&lt;/strong&gt; (ws://) for real-time messaging and live updates.&lt;/p&gt;

&lt;p&gt;On the backend, the application uses Node.js with Express for REST APIs and the ws library for WebSocket support. The backend exposes REST routes such as /signup, /signin, /users, /messages, /me, and /logout. Alongside this, a WebSocket server handles persistent connections, live message delivery, and connection management.&lt;/p&gt;

&lt;p&gt;Both the REST API layer and WebSocket server interact with the database through Prisma ORM, which connects to a PostgreSQL database for storing users, messages, authentication data, and related application state.&lt;/p&gt;

&lt;p&gt;Now for &lt;strong&gt;the flow&lt;/strong&gt;. This is the part I find most interesting. Here's what happens when you hit send:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Frontend&lt;/strong&gt; calls &lt;code&gt;sendMessage(to, content)&lt;/code&gt; which writes a JSON frame to the open WebSocket: &lt;code&gt;{ type: "send_message", payload: { to, content } }&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Backend WS handler&lt;/strong&gt; receives it, validates the payload, and does two things simultaneously:

&lt;ul&gt;
&lt;li&gt;Persists the message to PostgreSQL via Prisma&lt;/li&gt;
&lt;li&gt;Looks up the recipient's socket in the in-memory connection store (which I will update to be a singleton SocketManager class instead of just a map)&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;If the recipient is online, the backend &lt;strong&gt;pushes&lt;/strong&gt; the message to their socket as a &lt;code&gt;receive_message&lt;/code&gt; event, they see it instantly&lt;/li&gt;
&lt;li&gt;If they're offline, the message is still in the database. Next time they open the chat, the frontend fetches history via the REST endpoint &lt;code&gt;/messages?userId=...&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;The sender gets an &lt;code&gt;ack&lt;/code&gt; back over the WebSocket confirming the message was processed&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;No message broker, no queue, no Redis (yet, as that is the plan for v2). Just an in-memory &lt;code&gt;Map&amp;lt;userId, WebSocket&amp;gt;&lt;/code&gt; and a database. It's simple, it works for a single server, and it's the kind of thing that makes you appreciate what a system like Kafka(v2.5 maybe?) is actually solving when you eventually need it.&lt;/p&gt;

&lt;h2&gt;
  
  
  Here are the interesting bits
&lt;/h2&gt;

&lt;p&gt;All of these will be individual posts in the coming days.&lt;/p&gt;

&lt;h3&gt;
  
  
  1. Cookie auth over WebSockets
&lt;/h3&gt;

&lt;p&gt;Auth over REST is easy, but how does it happen when dealing with persistent connections. WebSockets don't have a middleware chain and also have no authorization header during the upgrade handshake.&lt;br&gt;
The solution - &lt;strong&gt;cookies!&lt;/strong&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  2. The React Stale Closure bug
&lt;/h3&gt;

&lt;p&gt;Imagine opening a chat in two tabs, you receive a message from User A, but see it silently render in the chat window for User B. No backend errors, just a complete UI mismatch.&lt;/p&gt;

&lt;h3&gt;
  
  
  3. The dual protocol magic
&lt;/h3&gt;

&lt;p&gt;How the HTTP and WS protocols interleave.&lt;/p&gt;

&lt;h3&gt;
  
  
  4. Raw WS
&lt;/h3&gt;

&lt;p&gt;How you create every feature you need using raw WebSockets instead of  Socket.io and have so much more understanding of the concepts.&lt;/p&gt;

&lt;h2&gt;
  
  
  Wrapping up
&lt;/h2&gt;

&lt;p&gt;For those of you who would like to go through the codebase, you can do so &lt;a href="https://github.com/nikshrma/relay" rel="noopener noreferrer"&gt;here&lt;/a&gt;. &lt;br&gt;
I'll be back with more next week. Until then, stay consistent!&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>javascript</category>
      <category>beginners</category>
      <category>react</category>
    </item>
    <item>
      <title>I Built a Real-Time Chat App from scratch - Here's What I Learned</title>
      <dc:creator>Nikhil Sharma</dc:creator>
      <pubDate>Tue, 26 May 2026 18:03:37 +0000</pubDate>
      <link>https://dev.to/nikhilsharma6/i-built-a-real-time-chat-app-from-scratch-heres-what-i-learned-30dd</link>
      <guid>https://dev.to/nikhilsharma6/i-built-a-real-time-chat-app-from-scratch-heres-what-i-learned-30dd</guid>
      <description>&lt;p&gt;When dealing with WebSockets, most tutorials will straightaway hand you Socket.io and call it a day. You get a working chat app in short period of time but you have no idea what happened.&lt;/p&gt;

&lt;p&gt;I actually wanted to understand what was going on, how WebSockets work, how auth happens, how to deal with cookies when working with WebSockets and so many more tiny things.&lt;br&gt;
So I built Relay. A full stack real-time chat app with raw WebSockets, Express for the backend, React for the frontend and PostgreSQL. &lt;br&gt;
The reason for doing everything manually was that I wanted to &lt;strong&gt;learn&lt;/strong&gt;. I did not want to launch and make a chat app and sell it. I just wanted to learn the implementation of these technologies.&lt;/p&gt;

&lt;p&gt;Thus, here we are. This is the first post in a series where I will be exploring the project and breaking down the most interesting technical problems I ran into. But first, let's see what Relay is, how it works, and why I built it the way I did.&lt;/p&gt;

&lt;h2&gt;
  
  
  What Relay Does
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Real-time messaging&lt;/strong&gt; — Messages are sent and received instantly over WebSockets. No polling, no long-polling, no hacks. &lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Persistent history&lt;/strong&gt; — Every message is stored in PostgreSQL via Prisma ORM. Close the tab, logout, the messages will still be there when you come back.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Cookie-based auth&lt;/strong&gt; — JWT tokens stored in httpOnly cookies. The WebSocket handshake authenticates by manually parsing cookies from the upgrade request headers.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Dual-protocol architecture&lt;/strong&gt; — REST API(the express part) handles the CRUD stuff (auth, fetching users, loading message history). WebSockets handle the real-time stuff (sending messages, live delivery). Each protocol does what it's good at and that is exactly what I wanted to learn to do myself. And also the fact that how would I implement both these protocols in the same project for maximum efficiency.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Architecture and Flow
&lt;/h2&gt;

&lt;p&gt;The app follows a fairly standard real-time chat architecture with a clear separation between frontend and backend responsibilities.&lt;/p&gt;

&lt;p&gt;On the frontend, the application is built using React, TypeScript, and Vite. Authentication state is managed through an AuthContext, chat history is handled using a useMessages hook, and real-time communication is managed by a useWebSocket hook. The frontend communicates with the backend in two ways:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;REST API&lt;/strong&gt; calls using &lt;strong&gt;Axios&lt;/strong&gt; over HTTP for things like authentication, fetching users, loading messages, and logout.&lt;br&gt;
&lt;strong&gt;WebSocket connections&lt;/strong&gt; (ws://) for real-time messaging and live updates.&lt;/p&gt;

&lt;p&gt;On the backend, the application uses Node.js with Express for REST APIs and the ws library for WebSocket support. The backend exposes REST routes such as /signup, /signin, /users, /messages, /me, and /logout. Alongside this, a WebSocket server handles persistent connections, live message delivery, and connection management.&lt;/p&gt;

&lt;p&gt;Both the REST API layer and WebSocket server interact with the database through Prisma ORM, which connects to a PostgreSQL database for storing users, messages, authentication data, and related application state.&lt;/p&gt;

&lt;p&gt;Now for &lt;strong&gt;the flow&lt;/strong&gt;. This is the part I find most interesting. Here's what happens when you hit send:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Frontend&lt;/strong&gt; calls &lt;code&gt;sendMessage(to, content)&lt;/code&gt; which writes a JSON frame to the open WebSocket: &lt;code&gt;{ type: "send_message", payload: { to, content } }&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Backend WS handler&lt;/strong&gt; receives it, validates the payload, and does two things simultaneously:

&lt;ul&gt;
&lt;li&gt;Persists the message to PostgreSQL via Prisma&lt;/li&gt;
&lt;li&gt;Looks up the recipient's socket in the in-memory connection store (which I will update to be a singleton SocketManager class instead of just a map)&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;If the recipient is online, the backend &lt;strong&gt;pushes&lt;/strong&gt; the message to their socket as a &lt;code&gt;receive_message&lt;/code&gt; event, they see it instantly&lt;/li&gt;
&lt;li&gt;If they're offline, the message is still in the database. Next time they open the chat, the frontend fetches history via the REST endpoint &lt;code&gt;/messages?userId=...&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;The sender gets an &lt;code&gt;ack&lt;/code&gt; back over the WebSocket confirming the message was processed&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;No message broker, no queue, no Redis (yet, as that is the plan for v2). Just an in-memory &lt;code&gt;Map&amp;lt;userId, WebSocket&amp;gt;&lt;/code&gt; and a database. It's simple, it works for a single server, and it's the kind of thing that makes you appreciate what a system like Kafka(v2.5 maybe?) is actually solving when you eventually need it.&lt;/p&gt;

&lt;h2&gt;
  
  
  Here are the interesting bits
&lt;/h2&gt;

&lt;p&gt;All of these will be individual posts in the coming days.&lt;/p&gt;

&lt;h3&gt;
  
  
  1. Cookie auth over WebSockets
&lt;/h3&gt;

&lt;p&gt;Auth over REST is easy, but how does it happen when dealing with persistent connections. WebSockets don't have a middleware chain and also have no authorization header during the upgrade handshake.&lt;br&gt;
The solution - &lt;strong&gt;cookies!&lt;/strong&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  2. The React Stale Closure bug
&lt;/h3&gt;

&lt;p&gt;Imagine opening a chat in two tabs, you receive a message from User A, but see it silently render in the chat window for User B. No backend errors, just a complete UI mismatch.&lt;/p&gt;

&lt;h3&gt;
  
  
  3. The dual protocol magic
&lt;/h3&gt;

&lt;p&gt;How the HTTP and WS protocols interleave.&lt;/p&gt;

&lt;h3&gt;
  
  
  4. Raw WS
&lt;/h3&gt;

&lt;p&gt;How you create every feature you need using raw WebSockets instead of  Socket.io and have so much more understanding of the concepts.&lt;/p&gt;

&lt;h2&gt;
  
  
  Wrapping up
&lt;/h2&gt;

&lt;p&gt;For those of you who would like to go through the codebase, you can do so &lt;a href="https://github.com/nikshrma/relay" rel="noopener noreferrer"&gt;here&lt;/a&gt;. &lt;br&gt;
I'll be back with more next week. Until then, stay consistent!&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>javascript</category>
      <category>beginners</category>
      <category>react</category>
    </item>
    <item>
      <title>Week 12: Typescript Magic!</title>
      <dc:creator>Nikhil Sharma</dc:creator>
      <pubDate>Sun, 01 Feb 2026 17:11:40 +0000</pubDate>
      <link>https://dev.to/nikhilsharma6/week-11-typescript-magic-nmk</link>
      <guid>https://dev.to/nikhilsharma6/week-11-typescript-magic-nmk</guid>
      <description>&lt;p&gt;Last week, we talked about how Prisma helps us in simplifying our DB queries, but as a project grows, we face another massive challenge: &lt;strong&gt;consistency&lt;/strong&gt;. When you have dozens of files and data moving between them, it's very easy to lose track of what "shape" an object has. &lt;/p&gt;

&lt;p&gt;In plain JavaScript, you might call a property user_id in your database logic, userId in your controller, and just id in your frontend. In a small project, you can keep that in your head. In a production-level app? &lt;/p&gt;

&lt;p&gt;It’s a straight-up disaster waiting to happen. You won't know something is broken until the code actually runs and crashes with the dreaded Cannot read property 'id' of undefined. &lt;/p&gt;

&lt;p&gt;The solution is &lt;strong&gt;TypeScript&lt;/strong&gt;. It’s not just "JavaScript with types"; it’s a high-tech safety net. It creates a development environment where the editor itself understands your data structures. It ensures that if you change a property name in one place, the rest of your app doesn't quietly crumble—it screams at you until you fix it.&lt;/p&gt;

&lt;h3&gt;
  
  
  1a. Setting Up the Environment📎
&lt;/h3&gt;

&lt;p&gt;Unlike JavaScript, which Node.js or a browser can read directly, TypeScript needs to be "transpiled." Think of it as a pre-processing step where your type-heavy TS code is stripped down into clean, performant JS that engines can actually execute. To get started, the setup involves a few key commands that I now have on muscle memory:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Initialize a node project to manage dependencies&lt;/span&gt;
npm init &lt;span class="nt"&gt;-y&lt;/span&gt;

&lt;span class="c"&gt;# Install TypeScript as a dev dependency (we don't need it in production)&lt;/span&gt;
npm &lt;span class="nb"&gt;install &lt;/span&gt;typescript &lt;span class="nt"&gt;--save-dev&lt;/span&gt;

&lt;span class="c"&gt;# Initialize the TypeScript compiler and create the config&lt;/span&gt;
npx tsc &lt;span class="nt"&gt;--init&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This generates a tsconfig.json file. This file is the "brain" of your project. It’s where you tell the compiler exactly how strict you want to be, which version of JavaScript to target, and where to look for your files. &lt;/p&gt;

&lt;h3&gt;
  
  
  1b. Organizing with rootDir and outDir
&lt;/h3&gt;

&lt;p&gt;In a professional workflow, you never want your source code (the stuff you edit) and the distribution code (the stuff that runs) mixed together. It makes version control a nightmare and the project structure confusing. &lt;/p&gt;

&lt;p&gt;To solve this, we dive into the tsconfig.json and configure two crucial settings:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;rootDir:&lt;/strong&gt; This points the compiler to our source folder. Usually, we set this to ./src. This is where all our .ts files live.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;outDir:&lt;/strong&gt; This tells the compiler where to dump the generated JavaScript. We usually use ./dist (distribution) or ./build. &lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;By setting these up, your workflow becomes streamlined: you write code in src, run npx tsc, and TypeScript replicates your entire folder structure inside dist, but converted to JavaScript. This keeps your project clean and ready for deployment. &lt;/p&gt;

&lt;h2&gt;
  
  
  2. Beyond Basics: Interfaces, Enums, and Generics
&lt;/h2&gt;

&lt;h3&gt;
  
  
  A. The Power of Interfaces
&lt;/h3&gt;

&lt;p&gt;Interfaces enforce a "contract." If a blueprint says a house needs four windows, TypeScript won't let you build it with three.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kr"&gt;interface&lt;/span&gt; &lt;span class="nx"&gt;User&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nl"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nl"&gt;username&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nl"&gt;email&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nl"&gt;role&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;UserRole&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;// Using an Enum&lt;/span&gt;
    &lt;span class="nl"&gt;tags&lt;/span&gt;&lt;span class="p"&gt;?:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;[];&lt;/span&gt; &lt;span class="c1"&gt;// Optional property&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

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

&lt;/div&gt;



&lt;h3&gt;
  
  
  B. Enums: Eliminating "Magic Strings"
&lt;/h3&gt;

&lt;p&gt;Don't use strings like &lt;code&gt;"admin"&lt;/code&gt; or &lt;code&gt;"guest"&lt;/code&gt; throughout your code. Use &lt;strong&gt;Enums&lt;/strong&gt; to create a single source of truth for constant values.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kr"&gt;enum&lt;/span&gt; &lt;span class="nx"&gt;UserRole&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;Admin&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;ADMIN&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nx"&gt;Moderator&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;MODERATOR&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nx"&gt;User&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;USER&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;myUser&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;User&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;1&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;username&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Nikhil_Dev&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;email&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;nikhil@example.com&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;role&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;UserRole&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Admin&lt;/span&gt; &lt;span class="c1"&gt;// Type-safe and discoverable&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;

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

&lt;/div&gt;



&lt;h3&gt;
  
  
  C. Generics: Writing Reusable Logic
&lt;/h3&gt;

&lt;p&gt;Generics allow you to create components that work over a variety of types rather than a single one. This is crucial for API responses.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kr"&gt;interface&lt;/span&gt; &lt;span class="nx"&gt;ApiResponse&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;T&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;status&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;number&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nl"&gt;data&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;T&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nl"&gt;error&lt;/span&gt;&lt;span class="p"&gt;?:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;// Now we can reuse this for Users, Products, or Orders&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;userResponse&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;ApiResponse&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;User&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;status&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;200&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;data&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;myUser&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;

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

&lt;/div&gt;






&lt;h2&gt;
  
  
  3. End-to-End Type Safety (Frontend + Backend)
&lt;/h2&gt;

&lt;p&gt;The "Holy Grail" of development is sharing types across your stack. By exporting interfaces from a shared folder, your Frontend knows exactly what the Backend is sending.&lt;/p&gt;

&lt;h3&gt;
  
  
  Shared Function Signatures
&lt;/h3&gt;

&lt;p&gt;Imagine a function that updates a user profile. In TypeScript, we can ensure the payload matches the expected structure before the API call is even made.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;type&lt;/span&gt; &lt;span class="nx"&gt;UpdateUserPayload&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;Pick&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;User&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;username&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;tags&lt;/span&gt;&lt;span class="dl"&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;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;updateUser&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="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;payload&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;UpdateUserPayload&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="nb"&gt;Promise&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="k"&gt;void&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// TypeScript ensures payload only contains username or tags&lt;/span&gt;
    &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;fetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`/api/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;`&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="na"&gt;method&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;POST&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;body&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;stringify&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;payload&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;h2&gt;
  
  
  New things I learnt this week🔄
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;The Power of strict: true:&lt;/strong&gt; Turning this on in tsconfig is like playing a game on "Hard Mode." It forces you to handle null and undefined cases explicitly. It’s annoying at first, but it eliminates about 90% of common runtime bugs. &lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Targeting Environments:&lt;/strong&gt; I learned that I can write modern ES6+ TypeScript but tell the compiler to output older ES5 code if I need to support legacy environments. &lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Type Inference:&lt;/strong&gt; I realized I don't have to define everything. If I write let name = "Nikhil", TypeScript is smart enough to know it's a string. You only need to be explicit when things get complex.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Wrapping up
&lt;/h2&gt;

&lt;p&gt;While the topics that I covered this week were small, they are crucial and of immense importance when building production-ready apps. If you have any questions or feedback, make sure to comment and let me know! &lt;/p&gt;

&lt;p&gt;I'll be back next week with more. Until then, stay consistent!&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>programming</category>
      <category>typescript</category>
      <category>beginners</category>
    </item>
    <item>
      <title>Week 11: Prisma!</title>
      <dc:creator>Nikhil Sharma</dc:creator>
      <pubDate>Wed, 17 Dec 2025 04:57:00 +0000</pubDate>
      <link>https://dev.to/nikhilsharma6/week-11-prisma-3lpo</link>
      <guid>https://dev.to/nikhilsharma6/week-11-prisma-3lpo</guid>
      <description>&lt;p&gt;This week I explored the &lt;strong&gt;Prisma ORM&lt;/strong&gt;, which simplifies our SQL journey by a LOT. Let’s get right into it!&lt;/p&gt;

&lt;h2&gt;
  
  
  Topics Covered✅
&lt;/h2&gt;

&lt;p&gt;Last week, I worked with basic schemas and simple database interactions. But once our data starts to grow and multiple entities are involved, raw SQL queries or loosely typed database access quickly become messy and error-prone.&lt;/p&gt;

&lt;p&gt;That’s where &lt;strong&gt;Prisma&lt;/strong&gt; comes in.&lt;/p&gt;

&lt;p&gt;This week revolved around:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Understanding what an ORM actually solves&lt;/li&gt;
&lt;li&gt;Defining a database schema using Prisma&lt;/li&gt;
&lt;li&gt;Generating type-safe database clients&lt;/li&gt;
&lt;li&gt;Performing CRUD operations cleanly and safely&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  1. What is Prisma and why do we need it? 🧠
&lt;/h2&gt;

&lt;p&gt;Prisma is an &lt;strong&gt;ORM (Object Relational Mapper)&lt;/strong&gt;. In simple terms, it acts as a bridge between our database and our application code.&lt;/p&gt;

&lt;p&gt;Without Prisma, we might write raw SQL 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;SELECT&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;users&lt;/span&gt; &lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="n"&gt;email&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'test@gmail.com'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;With Prisma, the same thing looks like:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;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="nx"&gt;prisma&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="nf"&gt;findUnique&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;where&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;email&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;test@gmail.com&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Why this matters
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;We don’t manually write SQL for every operation&lt;/li&gt;
&lt;li&gt;Queries become &lt;strong&gt;type-safe&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;Refactoring database fields doesn’t silently break things&lt;/li&gt;
&lt;li&gt;Autocomplete + compile-time checks save us from runtime bugs&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  2. Prisma Schema — the single source of truth 📄
&lt;/h2&gt;

&lt;p&gt;First we initialise Prisma in our project (for which you can refer to the &lt;a href="https://www.prisma.io/docs" rel="noopener noreferrer"&gt;docs here&lt;/a&gt;).&lt;br&gt;
At the heart of all generated files lies the &lt;code&gt;schema.prisma&lt;/code&gt; file. This file defines:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;our database connection&lt;/li&gt;
&lt;li&gt;our models (tables)&lt;/li&gt;
&lt;li&gt;relationships between models&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;A basic example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;model User {
  id        Int      @id @default(autoincrement())
  email     String   @unique
  name      String?
  createdAt DateTime @default(now())
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



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

&lt;ul&gt;
&lt;li&gt;Creates a &lt;code&gt;User&lt;/code&gt; table&lt;/li&gt;
&lt;li&gt;Automatically enforces uniqueness on email&lt;/li&gt;
&lt;li&gt;Generates types that our editor understands&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Once this is written, running:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npx prisma generate
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;creates a fully typed Prisma Client for us — which we can just import in our DB setup files and use as simply as this -&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;PrismaPg&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@prisma/adapter-pg&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;PrismaClient&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;@/app/generated/prisma/client&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;adapter&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;PrismaPg&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;connectionString&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;DATABASE_URL&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;prisma&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;PrismaClient&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="nx"&gt;adapter&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;
  
  
  3. Migrations — evolving the database safely 🔁
&lt;/h2&gt;

&lt;p&gt;Databases are never static. Fields get added, removed, or changed. Prisma handles this through &lt;strong&gt;migrations&lt;/strong&gt;.&lt;/p&gt;

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

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npx prisma migrate dev &lt;span class="nt"&gt;--name&lt;/span&gt; add-user-model
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;What this does:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Creates a migration file&lt;/li&gt;
&lt;li&gt;Applies changes to the database&lt;/li&gt;
&lt;li&gt;Keeps history of schema evolution&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This ensures our local DB, staging DB, and production DB all stay in sync. And the generated migration files are simply the SQL versions of the queries that we can write easily using Prisma.&lt;/p&gt;

&lt;h2&gt;
  
  
  4. CRUD operations with Prisma ✍️
&lt;/h2&gt;

&lt;p&gt;Prisma makes CRUD operations intuitive and readable.&lt;/p&gt;

&lt;h3&gt;
  
  
  Create
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;prisma&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="nf"&gt;create&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;data&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;email&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;nikhil@gmail.com&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Nikhil&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;h3&gt;
  
  
  Read
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;users&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;prisma&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="nf"&gt;findMany&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Update
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;prisma&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="nf"&gt;update&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;where&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="na"&gt;data&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Updated Name&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;h3&gt;
  
  
  Delete
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;prisma&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="k"&gt;delete&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;where&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;1&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;Each of these operations is:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Typed&lt;/li&gt;
&lt;li&gt;Validated at compile time&lt;/li&gt;
&lt;li&gt;Predictable in behaviour&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;And to see what parameters these functions expect we can just refer to the docs. &lt;/p&gt;

&lt;h2&gt;
  
  
  5. Relations and foreign keys made simple 🔗
&lt;/h2&gt;

&lt;p&gt;Defining relations in Prisma is surprisingly clean.&lt;/p&gt;

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

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;model User {
  id    Int    @id @default(autoincrement())
  posts Post[]
}

model Post {
  id     Int  @id @default(autoincrement())
  title  String
  user   User @relation(fields: [userId], references: [id])
  userId Int
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now Prisma understands:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;One user → many posts&lt;/li&gt;
&lt;li&gt;How to join data internally&lt;/li&gt;
&lt;li&gt;How to fetch nested data safely&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Fetching related data:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;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="nx"&gt;prisma&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="nf"&gt;findUnique&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;where&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="na"&gt;include&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;posts&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  New things I learnt this week 🔄
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Prisma acts as an intermediary between our code and database - increasing our ease while also giving type safety, which is an absolute game changer.&lt;/li&gt;
&lt;li&gt;Schema-first design prevents an entire class of bugs&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Wrapping up
&lt;/h2&gt;

&lt;p&gt;While Prisma might feel like “just another tool”, it fundamentally changes how confidently we interact with our database. Clean schemas and safe migrations make production systems far more reliable. &lt;/p&gt;

&lt;p&gt;If you have any questions or feedback, make sure to comment and let me know! &lt;strong&gt;Also, if you have any project ideas for me, please drop em in the comments!&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;I’ll be back next week with more. Until then, stay consistent!&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>programming</category>
      <category>beginners</category>
      <category>javascript</category>
    </item>
    <item>
      <title>Week 10: Databases - The Real Backbone of Our Apps!</title>
      <dc:creator>Nikhil Sharma</dc:creator>
      <pubDate>Mon, 08 Dec 2025 18:14:30 +0000</pubDate>
      <link>https://dev.to/nikhilsharma6/week-10-databases-the-real-backbone-of-our-apps-2k45</link>
      <guid>https://dev.to/nikhilsharma6/week-10-databases-the-real-backbone-of-our-apps-2k45</guid>
      <description>&lt;p&gt;This week, I went deeper into something that quietly run the entire show behind our apps - &lt;strong&gt;databases&lt;/strong&gt;.&lt;br&gt;&lt;br&gt;
Honestly, this was one of those weeks where everything just &lt;em&gt;clicked&lt;/em&gt;. The more I understood, the more things felt structured and “ohhh, so that's how it's done.” Let’s get right into it!&lt;/p&gt;


&lt;h2&gt;
  
  
  🗂️ Types of Databases
&lt;/h2&gt;

&lt;p&gt;At the highest level, we can split databases into two big families:&lt;/p&gt;
&lt;h3&gt;
  
  
  &lt;strong&gt;1. SQL Databases (Relational)&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;These are structured, rigid, strict but all in a good way.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Data lives in &lt;strong&gt;tables&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;Tables have &lt;strong&gt;columns&lt;/strong&gt; with fixed types
&lt;/li&gt;
&lt;li&gt;There are &lt;strong&gt;relationships&lt;/strong&gt; between tables
&lt;/li&gt;
&lt;li&gt;Everything is based on a schema we define upfront
&lt;/li&gt;
&lt;li&gt;We interact using &lt;strong&gt;SQL (Structured Query Language)&lt;/strong&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Examples: &lt;strong&gt;PostgreSQL, MySQL, MariaDB, SQL Server&lt;/strong&gt;&lt;/p&gt;


&lt;h3&gt;
  
  
  &lt;strong&gt;2. NoSQL Databases (Non-Relational)&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;These are way more flexible and relaxed with structure.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;No fixed schema
&lt;/li&gt;
&lt;li&gt;Great for rapidly changing data
&lt;/li&gt;
&lt;li&gt;Designed for quick reads, massive scale
&lt;/li&gt;
&lt;li&gt;Data can be documents, key-value pairs, graphs, columns…
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Examples: &lt;strong&gt;MongoDB, Redis, Cassandra, DynamoDB&lt;/strong&gt;&lt;/p&gt;


&lt;h2&gt;
  
  
  Where MongoDB Fits
&lt;/h2&gt;

&lt;p&gt;MongoDB is a &lt;strong&gt;NoSQL Document Database&lt;/strong&gt;.&lt;br&gt;&lt;br&gt;
Instead of rows and columns, MongoDB stores data as &lt;strong&gt;documents&lt;/strong&gt; that look like JSON.&lt;/p&gt;

&lt;p&gt;A user record might look like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Ayu"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"age"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;20&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"hobbies"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"painting"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"reading"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is super flexible! We can add or remove fields anytime and get no schema alerts.&lt;/p&gt;




&lt;h2&gt;
  
  
  MongoDB Advantages
&lt;/h2&gt;

&lt;p&gt;MongoDB does a bunch of things &lt;em&gt;really&lt;/em&gt; well:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Schema flexibility&lt;/strong&gt; - perfect for fast prototyping
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Horizontal scaling&lt;/strong&gt; - adding more servers is easier
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Great for document-shaped data&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Works naturally with JavaScript&lt;/strong&gt; since it uses JSON-like syntax
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;High read performance&lt;/strong&gt; when queries align with document structure
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;When we want speed and flexibility(say in a hackathon), MongoDB feels amazing.&lt;/p&gt;




&lt;h2&gt;
  
  
  But MongoDB Has Its Downsides Too
&lt;/h2&gt;

&lt;p&gt;As we scale, we start to notice some cracks:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Relationships are painful&lt;/strong&gt;
Joining data across collections feels messy.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Data integrity isn’t guaranteed by default&lt;/strong&gt;
If we expect strict correctness → things get tricky.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Query patterns can degrade performance&lt;/strong&gt;
Indexing becomes super important and manually tuned.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Harder to enforce structure&lt;/strong&gt;
Which means bugs can slip into the data layer.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;MongoDB is perfect until we need the kind of discipline that big systems demand.&lt;/p&gt;




&lt;h2&gt;
  
  
  🧠 Why Most Companies Still Prefer SQL
&lt;/h2&gt;

&lt;p&gt;Across the industry, SQL remains the default, and once we dig into it, the reasons feel obvious.&lt;/p&gt;

&lt;p&gt;SQL databases like &lt;strong&gt;Postgres&lt;/strong&gt; give us:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Structure
&lt;/li&gt;
&lt;li&gt;Predictability
&lt;/li&gt;
&lt;li&gt;Reliability
&lt;/li&gt;
&lt;li&gt;Better Relationships
&lt;/li&gt;
&lt;li&gt;ACID guarantees
&lt;/li&gt;
&lt;li&gt;Rich querying capabilities
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;When correctness matters, SQL usually wins.&lt;br&gt;&lt;br&gt;
Say in payments, inventory, authentication, banking, analytics, logistics and so many more. Basically anything where one mistake can cause a lot of trouble, SQL wins.&lt;/p&gt;

&lt;h3&gt;
  
  
  Why SQL (especially Postgres) feels better for many use cases:
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Every table has a schema → fewer “surprise bugs”
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Strong JOIN support&lt;/strong&gt; → complex relationships are easy
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Transactions&lt;/strong&gt; → either everything succeeds or nothing does
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Indices, constraints, triggers&lt;/strong&gt; → very mature tooling
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Postgres-specific power-ups:&lt;/strong&gt;

&lt;ul&gt;
&lt;li&gt;JSON support (best of both worlds)
&lt;/li&gt;
&lt;li&gt;Window functions
&lt;/li&gt;
&lt;li&gt;Full-text search
&lt;/li&gt;
&lt;li&gt;Extensions like &lt;code&gt;pgvector&lt;/code&gt;, &lt;code&gt;PostGIS&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;

&lt;p&gt;Postgres feels like that reliable friend who just never drops the ball.&lt;/p&gt;




&lt;h2&gt;
  
  
  ⚠️ But SQL Has Some Cons Too
&lt;/h2&gt;

&lt;p&gt;Nothing is perfect.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Schema changes can be heavy
&lt;/li&gt;
&lt;li&gt;Horizontal scaling is harder
&lt;/li&gt;
&lt;li&gt;Rigid structure might feel slow during rapid iteration
&lt;/li&gt;
&lt;li&gt;Requires more planning upfront
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Still, for most of the systems we want to build, SQL’s strengths massively outweigh its weaknesses.&lt;/p&gt;




&lt;h2&gt;
  
  
  🐘 Our Choice This Week: &lt;strong&gt;PostgreSQL&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;So this week was all about &lt;strong&gt;Postgres&lt;/strong&gt;, one of the most powerful SQL databases out there.&lt;/p&gt;

&lt;p&gt;We focused on understanding:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;How tables are created
&lt;/li&gt;
&lt;li&gt;How constraints keep our data safe
&lt;/li&gt;
&lt;li&gt;How relationships work using foreign keys
&lt;/li&gt;
&lt;li&gt;How SQL queries are structured
&lt;/li&gt;
&lt;li&gt;Why good database design matters
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;And since we’re building in the JavaScript ecosystem, we’ll be pairing Postgres with &lt;strong&gt;Prisma&lt;/strong&gt; as our ORM but that’s a whole topic by itself, so that’s coming next week.&lt;/p&gt;

&lt;p&gt;This week is pure SQL.&lt;/p&gt;




&lt;h2&gt;
  
  
  SQL Examples — With Intuition
&lt;/h2&gt;

&lt;h3&gt;
  
  
  ▶ Creating a Table
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;CREATE&lt;/span&gt; &lt;span class="k"&gt;TABLE&lt;/span&gt; &lt;span class="n"&gt;users&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="n"&gt;id&lt;/span&gt; &lt;span class="nb"&gt;SERIAL&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="n"&gt;name&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;email&lt;/span&gt; &lt;span class="nb"&gt;TEXT&lt;/span&gt; &lt;span class="k"&gt;UNIQUE&lt;/span&gt; &lt;span class="k"&gt;NOT&lt;/span&gt; &lt;span class="k"&gt;NULL&lt;/span&gt;
&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;What’s happening?&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
We’re designing a structure where:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;id&lt;/code&gt; auto-increments
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;name&lt;/code&gt; must always exist
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;email&lt;/code&gt; must be unique
&lt;/li&gt;
&lt;li&gt;Postgres will now enforce these rules for us
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This is the kind of discipline that NoSQL doesn’t enforce.&lt;/p&gt;




&lt;h3&gt;
  
  
  ▶ Inserting Data
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;INSERT&lt;/span&gt; &lt;span class="k"&gt;INTO&lt;/span&gt; &lt;span class="n"&gt;users&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;email&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;VALUES&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'Ayu'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'ayu@example.com'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This just means:&lt;br&gt;&lt;br&gt;
“Put a new row into the &lt;code&gt;users&lt;/code&gt; table with these values.”&lt;/p&gt;

&lt;p&gt;SQL has a learning curve which could be slow in the beginning but, as is the case with everything, gets easier with practice.&lt;/p&gt;




&lt;h3&gt;
  
  
  ▶ Querying Data
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;users&lt;/span&gt; &lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="n"&gt;email&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'ayu@example.com'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We’re pulling only the data that matters.&lt;br&gt;&lt;br&gt;
This precision is where SQL shines.&lt;/p&gt;




&lt;h3&gt;
  
  
  ▶ Updating Data
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;UPDATE&lt;/span&gt; &lt;span class="n"&gt;users&lt;/span&gt;
&lt;span class="k"&gt;SET&lt;/span&gt; &lt;span class="n"&gt;name&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'Ayu'&lt;/span&gt;
&lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="n"&gt;id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We’re telling Postgres:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;“Find the row where &lt;code&gt;id = 1&lt;/code&gt;”&lt;/li&gt;
&lt;li&gt;“Change the name to Ayu”
&lt;/li&gt;
&lt;/ul&gt;




&lt;h3&gt;
  
  
  ▶ Deleting Data
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;DELETE&lt;/span&gt; &lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;users&lt;/span&gt; &lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="n"&gt;id&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Again - clean and readable(because our use-case is simple – SQL queries can get very complex, but an ORM helps simplify it further).&lt;/p&gt;




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

&lt;p&gt;This week gave us a solid foundation in understanding why databases behave the way they do, and why SQL (especially Postgres) is still the king for most real-world applications. MongoDB is incredible for flexibility and fast-moving projects but SQL is unbeatable for consistency and correctness.&lt;/p&gt;

&lt;p&gt;Next week, we’ll take all this knowledge and plug it into &lt;strong&gt;Prisma&lt;/strong&gt;, making database interactions feel a LOT easier.&lt;/p&gt;

&lt;p&gt;If you have any questions or feedback, make sure to comment and let me know!&lt;/p&gt;

&lt;p&gt;I'll be back next week with more. Until then, stay consistent!  &lt;/p&gt;

</description>
      <category>webdev</category>
      <category>postgres</category>
      <category>beginners</category>
      <category>backend</category>
    </item>
    <item>
      <title>Week 9: Using Recoil in React!</title>
      <dc:creator>Nikhil Sharma</dc:creator>
      <pubDate>Mon, 01 Dec 2025 18:28:41 +0000</pubDate>
      <link>https://dev.to/nikhilsharma6/week-9-using-recoil-in-react-2m0j</link>
      <guid>https://dev.to/nikhilsharma6/week-9-using-recoil-in-react-2m0j</guid>
      <description>&lt;p&gt;Back after 2 weeks of vacation!(it was actually a continuous battle with so many evaluations and exams at college). During this time I've been covering State Management with context API and Recoil! Let's get right into it!&lt;/p&gt;

&lt;h2&gt;
  
  
  Topics Covered✅
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Context API&lt;/li&gt;
&lt;li&gt;Problems with Context API&lt;/li&gt;
&lt;li&gt;Recoil and why it's better&lt;/li&gt;
&lt;li&gt;Atoms &amp;amp; Selectors&lt;/li&gt;
&lt;li&gt;Recoil hooks (&lt;code&gt;useRecoilState&lt;/code&gt;, &lt;code&gt;useRecoilValue&lt;/code&gt;, &lt;code&gt;useSetRecoilState&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;Using &lt;code&gt;RecoilRoot&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;When to still use &lt;code&gt;useState&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;




&lt;h1&gt;
  
  
  1. Starting with the Context API 🧩
&lt;/h1&gt;

&lt;p&gt;Last week, we spoke about prop drilling and how passing props through multiple layers can make your code look messy. The &lt;strong&gt;Context API&lt;/strong&gt; is React’s built-in solution to this.&lt;/p&gt;

&lt;p&gt;It lets you create &lt;strong&gt;global-ish&lt;/strong&gt; state without prop drilling.&lt;/p&gt;

&lt;p&gt;Here’s a tiny example:&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;// UserContext.jsx&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;createContext&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;react&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;UserContext&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;createContext&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="c1"&gt;// App.jsx&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;React&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;useState&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;react&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;UserContext&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;./UserContext&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;App&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;user&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;setUser&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;useState&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Nikhil&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="k"&gt;return &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;UserContext&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Provider&lt;/span&gt; &lt;span class="na"&gt;value&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&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="nx"&gt;setUser&lt;/span&gt; &lt;span class="p"&gt;}&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;Parent&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;UserContext&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Provider&lt;/span&gt;&lt;span class="p"&gt;&amp;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;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="nx"&gt;App&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="c1"&gt;// Child.jsx&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;useContext&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;react&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;UserContext&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;./UserContext&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;Child&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;user&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;useContext&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;UserContext&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;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;h2&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;Hello, &lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;user&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;!&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;h2&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="nx"&gt;Child&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This appears to work well. But it does so only for small and simple apps.&lt;/p&gt;

&lt;h1&gt;
  
  
  2. The problem? 😵‍💫
&lt;/h1&gt;

&lt;p&gt;As our app grows, Context API introduces a few issues:&lt;/p&gt;

&lt;h2&gt;
  
  
  1. Re-renders
&lt;/h2&gt;

&lt;p&gt;Any value change in the context re-renders every component that consumes it, even if only one component actually needed the update.&lt;/p&gt;

&lt;h2&gt;
  
  
  2. Too many contexts
&lt;/h2&gt;

&lt;p&gt;You often end up creating multiple contexts:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;UserContext&lt;/li&gt;
&lt;li&gt;ThemeContext&lt;/li&gt;
&lt;li&gt;NotificationContext&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;and so on.&lt;/p&gt;

&lt;p&gt;This becomes hard to organise and maintain.&lt;/p&gt;

&lt;h2&gt;
  
  
  3. Not a full state management solution
&lt;/h2&gt;

&lt;p&gt;Context wasn't designed for big, complex global state. It's more of a “prop drilling escape hatch”.&lt;/p&gt;

&lt;h1&gt;
  
  
  3. Recoil ⚛️ - The solution
&lt;/h1&gt;

&lt;p&gt;Recoil is a super-lightweight state management library made for React. It fixes the problems above without introducing the complexity of Redux.&lt;/p&gt;

&lt;p&gt;Why Recoil feels so good:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;It updates only the components that need the state.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;You don’t need multiple providers — everything sits under one RecoilRoot.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;It’s extremely intuitive because it feels like working with React’s useState.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Recoil introduces atoms, which are simply pieces of global state.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h1&gt;
  
  
  4. Getting started with Recoil
&lt;/h1&gt;

&lt;h2&gt;
  
  
  Step 1: Wrap your app in a RecoilRoot
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight jsx"&gt;&lt;code&gt;&lt;span class="c1"&gt;// index.jsx&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;React&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;react&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;ReactDOM&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;react-dom/client&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;App&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;./App&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;RecoilRoot&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;recoil&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="nx"&gt;ReactDOM&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;createRoot&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getElementById&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;root&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)).&lt;/span&gt;&lt;span class="nf"&gt;render&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;RecoilRoot&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;App&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;RecoilRoot&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Step 2:Creating an atom
&lt;/h2&gt;

&lt;p&gt;Atoms store global state. We usually create them in a separate folder and export them from there and then we can use them globally.&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;// atoms/userAtom.jsx&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;atom&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;recoil&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;userAtom&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;atom&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
    &lt;span class="na"&gt;key&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;userAtom&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;default&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Nikhil&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Step 3:Using the globally declared states in Components
&lt;/h2&gt;

&lt;p&gt;For this we use majorly three functions&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;useRecoilState → like useState but global
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight jsx"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;useRecoilState&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;recoil&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;userAtom&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;./atoms/userAtom&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;UserUpdater&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;user&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;setUser&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;useRecoilState&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;userAtom&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;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;button&lt;/span&gt; &lt;span class="na"&gt;onClick&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&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="nf"&gt;setUser&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Ayu&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
      Change User
    &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;button&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ol&gt;
&lt;li&gt;useRecoilValue → read-only access- so it basically just gives us the variable and not the function to change its value.
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight jsx"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;useRecoilValue&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;recoil&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;userAtom&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;./atoms/userAtom&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;UserDisplay&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="nf"&gt;useRecoilValue&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;userAtom&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;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;h2&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;Hello, &lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;user&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;!&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;h2&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ol&gt;
&lt;li&gt;useSetRecoilState → write-only access
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight jsx"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;useSetRecoilState&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;recoil&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;userAtom&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;./atoms/userAtom&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;LogoutButton&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;setUser&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;useSetRecoilState&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;userAtom&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;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;button&lt;/span&gt; &lt;span class="na"&gt;onClick&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&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="nf"&gt;setUser&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;Logout&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;button&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Selectors -
&lt;/h3&gt;

&lt;p&gt;Selectors let you derive data from atoms, say computed values. This is how we'd create one.&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="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;selector&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;recoil&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;userAtom&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;./userAtom&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;capitalisedUserSelector&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;selector&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;key&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;capitalisedUserSelector&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;get&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="kd"&gt;get&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="nx"&gt;user&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;userAtom&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;user&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;toUpperCase&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;And to use it we do this-&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="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;useRecoilValue&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;recoil&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;capitalisedUserSelector&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;./atoms/selectors&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;Display&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;name&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;useRecoilValue&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;capitalisedUserSelector&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;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;h3&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;h3&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h1&gt;
  
  
  5. Should we ditch useState now?
&lt;/h1&gt;

&lt;p&gt;&lt;strong&gt;Not at all.&lt;/strong&gt;&lt;br&gt;
If a state variable is only needed by 1–2 components, using Recoil is unnecessary.&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="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;Counter&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;count&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;setCount&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;useState&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="k"&gt;return &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;&amp;gt;&lt;/span&gt;
      &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;p&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;count&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;p&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="nt"&gt;button&lt;/span&gt; &lt;span class="na"&gt;onClick&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&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="nf"&gt;setCount&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;count&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&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="nt"&gt;button&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;/&amp;gt;&lt;/span&gt;
  &lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;No reason to make count global here. React’s local state is still perfect for component-specific logic.&lt;br&gt;
We use Recoil only when:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Many components need the same state&lt;/li&gt;
&lt;li&gt;Passing props becomes painful&lt;/li&gt;
&lt;li&gt;Context starts slowing down your UI
## Things I found interesting this week&lt;/li&gt;
&lt;li&gt;Seeing how Recoil prevents unnecessary re-renders felt magical. The component isolation makes the app feel snappier.&lt;/li&gt;
&lt;li&gt;Atoms being global state and also visually being "global state" made the learning curve super smooth for me.
## Wrapping up🔄
Interesting week.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; While learning Recoil this week, I actually didn’t know that the library had been archived. Since I had already started, I went ahead and completed it anyway — the concepts are super useful to understand. I’ll be moving to Jotai soon since it’s lightweight, actively maintained, and follows a similar mental model to Recoil, so the transition should feel smooth. &lt;/p&gt;

&lt;p&gt;If you have any questions or feedback, make sure to comment and let me know!&lt;/p&gt;

&lt;p&gt;I'll be back next week with more. Until then, stay consistent!&lt;/p&gt;

</description>
      <category>javascript</category>
      <category>webdev</category>
      <category>beginners</category>
      <category>react</category>
    </item>
    <item>
      <title>Vacation week!</title>
      <dc:creator>Nikhil Sharma</dc:creator>
      <pubDate>Mon, 17 Nov 2025 16:39:25 +0000</pubDate>
      <link>https://dev.to/nikhilsharma6/vacation-week-2489</link>
      <guid>https://dev.to/nikhilsharma6/vacation-week-2489</guid>
      <description>&lt;p&gt;This week wasn’t about new concepts or heavy learning.&lt;br&gt;
Exams are around the corner, I’ve been traveling back and forth between home and college, and it was also the birthday of someone very close to me. So life was a little full in the best ways.&lt;/p&gt;

&lt;p&gt;So this became a vacation week. A breather I honestly needed.&lt;/p&gt;

&lt;p&gt;I'll be back with more next week! Till then, stay consistent.&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>beginners</category>
      <category>javascript</category>
      <category>programming</category>
    </item>
  </channel>
</rss>
