<?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: Md Mohosin Ali Shah</title>
    <description>The latest articles on DEV Community by Md Mohosin Ali Shah (@mohosin2126).</description>
    <link>https://dev.to/mohosin2126</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%2F2584956%2Fdbfb4a7d-f905-4202-ba9d-a0a270632737.jpg</url>
      <title>DEV Community: Md Mohosin Ali Shah</title>
      <link>https://dev.to/mohosin2126</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/mohosin2126"/>
    <language>en</language>
    <item>
      <title>React Query Showed the Wrong User's Data — Here's How to Reproduce and Fix It</title>
      <dc:creator>Md Mohosin Ali Shah</dc:creator>
      <pubDate>Tue, 10 Nov 2026 23:24:49 +0000</pubDate>
      <link>https://dev.to/mohosin2126/react-query-showed-the-wrong-users-data-heres-how-to-reproduce-and-fix-it-3i08</link>
      <guid>https://dev.to/mohosin2126/react-query-showed-the-wrong-users-data-heres-how-to-reproduce-and-fix-it-3i08</guid>
      <description>&lt;p&gt;A React dashboard can display the wrong user's data even when the API is returning the correct response.&lt;/p&gt;

&lt;p&gt;One of the easiest ways to introduce this bug is by using a query key that doesn't account for the current user.&lt;/p&gt;

&lt;p&gt;Consider 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="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;data&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;useQuery&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="n"&gt;queryKey&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"orders"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="n"&gt;queryFn&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="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;fetchOrders&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&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="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 looks perfectly reasonable.&lt;/p&gt;

&lt;p&gt;But there's a problem.&lt;/p&gt;

&lt;p&gt;The query function depends on &lt;code&gt;userId&lt;/code&gt;, while the cache key doesn't.&lt;/p&gt;

&lt;p&gt;If Alice's orders are already cached and the application switches to Bob, both users can end up sharing the same cache entry.&lt;/p&gt;

&lt;p&gt;Depending on the query's freshness settings, Bob's dashboard could display Alice's previously cached orders.&lt;/p&gt;

&lt;p&gt;I wanted to break down why this happens because it's the kind of issue that's easy to overlook when testing with only one account.&lt;/p&gt;

&lt;p&gt;We'll reproduce it in a small React application, fix the query key, and then look at what changes when authentication, logout, and multiple SaaS workspaces are involved.&lt;/p&gt;

&lt;p&gt;Alice logs in and opens her orders page. Everything looks fine.&lt;/p&gt;

&lt;p&gt;She logs out, and Bob logs in using the same browser.&lt;/p&gt;

&lt;p&gt;But Bob sees something unexpected.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Alice's orders are still showing on his dashboard.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;You check the backend. The API works. The database queries are correct. Your authorization checks appear to be doing their job.&lt;/p&gt;

&lt;p&gt;So why is Bob seeing Alice's data?&lt;/p&gt;

&lt;p&gt;The issue might be hiding in one line of React Query code:&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="n"&gt;queryKey&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"orders"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That looks harmless, doesn't it?&lt;/p&gt;

&lt;p&gt;But when the data depends on the authenticated user, this query key doesn't tell TanStack Query whose orders it's caching.&lt;/p&gt;

&lt;p&gt;Let's reproduce the issue, understand why it happens, and fix it properly.&lt;/p&gt;

&lt;p&gt;We'll also look at a few related problems that are easy to miss in production: switching workspaces, logging out, and displaying previous data while a new request loads.&lt;/p&gt;

&lt;h2&gt;
  
  
  1. The bug starts with a perfectly normal-looking query
&lt;/h2&gt;

&lt;p&gt;Consider a React component that fetches the current user's orders.&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="n"&gt;useQuery&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="s2"&gt;"@tanstack/react-query"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nc"&gt;Orders&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&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="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;isPending&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;useQuery&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;queryKey&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"orders"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;queryFn&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="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;fetchOrders&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&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="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="n"&gt;isPending&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="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;p&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="nc"&gt;Loading&lt;/span&gt; &lt;span class="n"&gt;orders&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="o"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="n"&gt;p&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="k"&gt;return&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;OrdersTable&lt;/span&gt; &lt;span class="n"&gt;orders&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;/&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;At first glance, there's nothing unusual here.&lt;/p&gt;

&lt;p&gt;We have a query key, a query function, and a loading state.&lt;/p&gt;

&lt;p&gt;The problem is that &lt;code&gt;fetchOrders()&lt;/code&gt; depends on &lt;code&gt;userId&lt;/code&gt;, but the query key doesn't.&lt;/p&gt;

&lt;p&gt;Let's say Alice has these orders:&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="p"&gt;{&lt;/span&gt; &lt;span class="nl"&gt;"id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;101&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nl"&gt;"product"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"MacBook Pro"&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="nl"&gt;"id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;102&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nl"&gt;"product"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"Keyboard"&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 Bob has these:&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="p"&gt;{&lt;/span&gt; &lt;span class="nl"&gt;"id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;201&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nl"&gt;"product"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"Monitor"&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="nl"&gt;"id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;202&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nl"&gt;"product"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"Mouse"&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;Our React component requests different data depending on the user.&lt;/p&gt;

&lt;p&gt;But TanStack Query sees this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Alice → ["orders"]

Bob   → ["orders"]
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It's the same cache key.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;TanStack Query doesn't automatically inspect the variables inside your query function to identify the data.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;It relies on the query key you provide.&lt;/p&gt;

&lt;p&gt;If two requests use the same key, they're treated as requests for the same cached query.&lt;/p&gt;

&lt;p&gt;And that's where things can go wrong.&lt;/p&gt;

&lt;h2&gt;
  
  
  2. Let's reproduce the problem
&lt;/h2&gt;

&lt;p&gt;Instead of stopping at the explanation, let's create a small React application where you can see the bug yourself.&lt;/p&gt;

&lt;p&gt;We'll use Vite and TanStack Query.&lt;/p&gt;

&lt;p&gt;No backend or database is required for this demonstration.&lt;/p&gt;

&lt;p&gt;The data is mocked so we can focus entirely on caching behavior. We're simulating account switching, not implementing real authentication.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 1: Create the project
&lt;/h3&gt;

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

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm create vite@latest react-query-cache-demo -- --template react

&lt;span class="nb"&gt;cd&lt;/span&gt; react-query-cache-demo

npm install

npm install @tanstack/react-query

npm run dev
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Step 2: Configure QueryClient
&lt;/h3&gt;

&lt;p&gt;Open &lt;code&gt;src/main.jsx&lt;/code&gt; and replace its contents with:&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="nc"&gt;React&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="s2"&gt;"react"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nc"&gt;ReactDOM&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="s2"&gt;"react-dom/client"&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="nc"&gt;QueryClient&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nc"&gt;QueryClientProvider&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="s2"&gt;"@tanstack/react-query"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nc"&gt;App&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="s2"&gt;"./App.jsx"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="n"&gt;queryClient&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;QueryClient&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="nc"&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="s2"&gt;"root"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;render&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;React&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;StrictMode&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;QueryClientProvider&lt;/span&gt; &lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;queryClient&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;App&lt;/span&gt; &lt;span class="o"&gt;/&amp;gt;&lt;/span&gt;
    &lt;span class="o"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nc"&gt;QueryClientProvider&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="o"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nc"&gt;React&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;StrictMode&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Step 3: Create the demo
&lt;/h3&gt;

&lt;p&gt;Now replace &lt;code&gt;src/App.jsx&lt;/code&gt; with:&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="n"&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="s2"&gt;"react"&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="n"&gt;useQuery&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="s2"&gt;"@tanstack/react-query"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="n"&gt;ordersDatabase&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="n"&gt;alice&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="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;101&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;product&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"MacBook Pro"&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="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;102&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;product&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"Keyboard"&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="n"&gt;bob&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="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;201&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;product&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"Monitor"&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="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;202&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;product&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"Mouse"&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="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;fetchOrders&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;userId&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="c1"&gt;// Simulate network latency&lt;/span&gt;
  &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nb"&gt;Promise&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;resolve&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt;
    &lt;span class="nb"&gt;setTimeout&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;resolve&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;500&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="n"&gt;ordersDatabase&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&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;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nc"&gt;App&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="n"&gt;userId&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;setUserId&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="s2"&gt;"alice"&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="n"&gt;fixed&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;setFixed&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="kc"&gt;false&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="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;orders&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;isPending&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;useQuery&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;queryKey&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;fixed&lt;/span&gt;
      &lt;span class="o"&gt;?&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"orders"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&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="s2"&gt;"orders"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;

    &lt;span class="n"&gt;queryFn&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="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;fetchOrders&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;userId&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;

    &lt;span class="c1"&gt;// Keep cached data fresh for this demo&lt;/span&gt;
    &lt;span class="n"&gt;staleTime&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;Infinity&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="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;div&lt;/span&gt; &lt;span class="n"&gt;style&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;padding&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;24&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="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;h1&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="nc"&gt;Orders&lt;/span&gt; &lt;span class="nc"&gt;Dashboard&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="n"&gt;h1&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;

      &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;p&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="nc"&gt;Logged&lt;/span&gt; &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;strong&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;userId&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="n"&gt;strong&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="o"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="n"&gt;p&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;

      &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;button&lt;/span&gt; &lt;span class="n"&gt;onClick&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;setUserId&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"alice"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="nc"&gt;Switch&lt;/span&gt; &lt;span class="n"&gt;to&lt;/span&gt; &lt;span class="nc"&gt;Alice&lt;/span&gt;
      &lt;span class="o"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="n"&gt;button&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;

      &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;button&lt;/span&gt; &lt;span class="n"&gt;onClick&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;setUserId&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"bob"&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="nc"&gt;Switch&lt;/span&gt; &lt;span class="n"&gt;to&lt;/span&gt; &lt;span class="nc"&gt;Bob&lt;/span&gt;
      &lt;span class="o"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="n"&gt;button&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;

      &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;hr&lt;/span&gt; &lt;span class="o"&gt;/&amp;gt;&lt;/span&gt;

      &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;label&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;input&lt;/span&gt;
          &lt;span class="kd"&gt;type&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"checkbox"&lt;/span&gt;
          &lt;span class="n"&gt;checked&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;fixed&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
          &lt;span class="n"&gt;onChange&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&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="nf"&gt;setFixed&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;event&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;target&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;checked&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="nc"&gt;Use&lt;/span&gt; &lt;span class="n"&gt;correct&lt;/span&gt; &lt;span class="n"&gt;query&lt;/span&gt; &lt;span class="n"&gt;key&lt;/span&gt;
      &lt;span class="o"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="n"&gt;label&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;

      &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;h2&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="nc"&gt;Orders&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="n"&gt;h2&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;

      &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;isPending&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;lt;&lt;/span&gt;&lt;span class="n"&gt;p&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="nc"&gt;Loading&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="o"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="n"&gt;p&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="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;ul&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
          &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;orders&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;map&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;order&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="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;li&lt;/span&gt; &lt;span class="n"&gt;key&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;order&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;id&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="n"&gt;order&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;order&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;product&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
            &lt;span class="o"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="n"&gt;li&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="o"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="n"&gt;ul&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="o"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="n"&gt;div&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Step 4: See what happens
&lt;/h3&gt;

&lt;p&gt;Open the application.&lt;/p&gt;

&lt;p&gt;Initially, Alice is selected.&lt;/p&gt;

&lt;p&gt;Wait for her orders to load.&lt;/p&gt;

&lt;p&gt;You should see:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Logged in as: alice

Orders:
#101 - MacBook Pro
#102 - Keyboard
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now click &lt;strong&gt;Switch to Bob&lt;/strong&gt; without enabling the checkbox.&lt;/p&gt;

&lt;p&gt;You might expect Bob's orders to appear.&lt;/p&gt;

&lt;p&gt;But the screen still shows:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Logged in as: bob

Orders:
#101 - MacBook Pro
#102 - Keyboard
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That's the bug.&lt;/p&gt;

&lt;p&gt;We're displaying Alice's cached orders while the interface says Bob is the current user.&lt;/p&gt;

&lt;h3&gt;
  
  
  Why does this happen?
&lt;/h3&gt;

&lt;p&gt;Our query key hasn't changed:&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="n"&gt;queryKey&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"orders"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We also deliberately configured:&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="n"&gt;staleTime&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;Infinity&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This tells TanStack Query to consider the cached data fresh indefinitely, unless it is manually invalidated.&lt;/p&gt;

&lt;p&gt;Because the cache key remains unchanged and the data is considered fresh, changing &lt;code&gt;userId&lt;/code&gt; doesn't trigger a new query.&lt;/p&gt;

&lt;p&gt;This setting makes the problem easy to reproduce.&lt;/p&gt;

&lt;p&gt;With the default stale-time configuration, you may see different refetching behavior depending on when the query mounts or is invalidated. But the underlying problem remains: two different datasets are sharing one cache identity.&lt;/p&gt;

&lt;h2&gt;
  
  
  3. Fixing it with the correct query key
&lt;/h2&gt;

&lt;p&gt;The fix is straightforward.&lt;/p&gt;

&lt;p&gt;Instead of:&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;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;data&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;useQuery&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="n"&gt;queryKey&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"orders"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="n"&gt;queryFn&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="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;fetchOrders&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&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="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;Use:&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;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;data&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;useQuery&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="n"&gt;queryKey&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"orders"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;userId&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="n"&gt;queryFn&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="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;fetchOrders&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&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="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;Now the cache keys are different:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Alice → ["orders", "alice"]

Bob   → ["orders", "bob"]
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;TanStack Query can distinguish the two datasets.&lt;/p&gt;

&lt;p&gt;Go back to the demo and enable &lt;strong&gt;Use correct query key&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Switch between Alice and Bob again.&lt;/p&gt;

&lt;p&gt;The orders should now match the selected account.&lt;/p&gt;

&lt;p&gt;When you switch back to Alice, her previously fetched data can be retrieved from her own cache entry.&lt;/p&gt;

&lt;p&gt;That's exactly the behavior we want.&lt;/p&gt;

&lt;p&gt;Here's the principle I follow:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;If a variable changes which data your query returns, it probably belongs in the query key.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Not every variable needs to be part of the key. But variables that determine the identity of the requested data do.&lt;/p&gt;

&lt;h2&gt;
  
  
  4. The same mistake becomes more complicated in multi-tenant SaaS applications
&lt;/h2&gt;

&lt;p&gt;Now imagine a more realistic application.&lt;/p&gt;

&lt;p&gt;You're working on a SaaS platform where a single account can belong to multiple organizations.&lt;/p&gt;

&lt;p&gt;For example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Alice
  |
  |-- Company A
  |     |-- Orders
  |     |-- Customers
  |
  |-- Company B
        |-- Orders
        |-- Customers
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Alice can switch between Company A and Company B without logging out.&lt;/p&gt;

&lt;p&gt;You might already be using this query key:&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="n"&gt;queryKey&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"orders"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;userId&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It separates Alice's data from Bob's.&lt;/p&gt;

&lt;p&gt;But it doesn't separate Company A's orders from Company B's.&lt;/p&gt;

&lt;p&gt;Both organizations still share the same key for Alice.&lt;/p&gt;

&lt;p&gt;A better key would be:&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="n"&gt;queryKey&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
  &lt;span class="s2"&gt;"orders"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="n"&gt;userId&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="n"&gt;organizationId&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 what if the orders table has filters?&lt;/p&gt;

&lt;p&gt;Suppose users can filter by order status, search terms, or pages.&lt;/p&gt;

&lt;p&gt;Those variables also affect the result.&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="n"&gt;queryKey&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
  &lt;span class="s2"&gt;"orders"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="n"&gt;userId&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="n"&gt;organizationId&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;status&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;page&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;search&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;Now each dataset has its own cache identity.&lt;/p&gt;

&lt;p&gt;For example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;["orders", "alice", "company-a", { page: 1 }]

["orders", "alice", "company-b", { page: 1 }]

["orders", "bob", "company-b", { page: 1 }]
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;One important detail: don't include access tokens, passwords, or other secrets in query keys.&lt;/p&gt;

&lt;p&gt;Use stable, non-secret identifiers.&lt;/p&gt;

&lt;p&gt;And remember that cache scoping is not authorization. Even if a user manipulates a query key, the backend must independently check permissions.&lt;/p&gt;

&lt;h2&gt;
  
  
  5. How I'd structure the query in a real application
&lt;/h2&gt;

&lt;p&gt;For a workspace-based SaaS application, I prefer keeping the query logic inside a reusable hook.&lt;/p&gt;

&lt;p&gt;Something like 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="n"&gt;useQuery&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="s2"&gt;"@tanstack/react-query"&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;fetchWorkspaceOrders&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="n"&gt;workspaceId&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="n"&gt;status&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="n"&gt;page&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="n"&gt;signal&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="kd"&gt;const&lt;/span&gt; &lt;span class="n"&gt;params&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;URLSearchParams&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;status&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;page&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;String&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;page&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="kd"&gt;const&lt;/span&gt; &lt;span class="n"&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="nb"&gt;fetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="sb"&gt;`/api/workspaces/${encodeURIComponent(workspaceId)}/orders?${params}`&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="n"&gt;credentials&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"include"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="n"&gt;signal&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="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;status&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="mi"&gt;401&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nb"&gt;Error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"Authentication required"&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;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;status&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="mi"&gt;403&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nb"&gt;Error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"Access denied"&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;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ok&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nb"&gt;Error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"Failed to fetch orders"&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="n"&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="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;export&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;useWorkspaceOrders&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="n"&gt;userId&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="n"&gt;workspaceId&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="n"&gt;status&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"all"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="n"&gt;page&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="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="nf"&gt;useQuery&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;queryKey&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
      &lt;span class="s2"&gt;"orders"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="n"&gt;userId&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="n"&gt;workspaceId&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;status&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;page&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="n"&gt;queryFn&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="n"&gt;signal&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="nf"&gt;fetchWorkspaceOrders&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;workspaceId&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;status&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;page&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;signal&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="n"&gt;enabled&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;Boolean&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;userId&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="n"&gt;workspaceId&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;There are a few details worth understanding.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The query key describes the data.&lt;/strong&gt; It includes the account, workspace, and filters that determine the response.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The &lt;code&gt;enabled&lt;/code&gt; option prevents automatic fetching until the required identifiers are available.&lt;/strong&gt; It doesn't remove cached data or enforce access control.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The query function uses the supplied &lt;code&gt;AbortSignal&lt;/code&gt;.&lt;/strong&gt; This allows obsolete requests to be cancelled when TanStack Query cancels the query.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;HTTP errors are handled explicitly.&lt;/strong&gt; The browser's &lt;code&gt;fetch()&lt;/code&gt; function doesn't automatically reject a promise for responses such as &lt;code&gt;403&lt;/code&gt; or &lt;code&gt;500&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;For this example, I'm assuming the backend uses cookie-based authentication. If your application uses bearer tokens or another mechanism, the request implementation will be different.&lt;/p&gt;

&lt;p&gt;The query key should still reflect the authenticated data context.&lt;/p&gt;

&lt;h2&gt;
  
  
  6. Don't forget what happens on logout
&lt;/h2&gt;

&lt;p&gt;Correct query keys solve an important problem, but I wouldn't stop there.&lt;/p&gt;

&lt;p&gt;Consider a shared computer.&lt;/p&gt;

&lt;p&gt;Alice logs in, opens her dashboard, and logs out.&lt;/p&gt;

&lt;p&gt;Then Bob logs in using the same browser tab.&lt;/p&gt;

&lt;p&gt;If both sessions use the same &lt;code&gt;QueryClient&lt;/code&gt;, Alice's previously fetched data can remain in its in-memory cache even after her session ends.&lt;/p&gt;

&lt;p&gt;Correctly scoped keys help prevent the new session from accidentally requesting Alice's cached data.&lt;/p&gt;

&lt;p&gt;But for private information, I generally prefer clearing the previous session's cached state when the authentication boundary changes.&lt;/p&gt;

&lt;p&gt;TanStack Query provides:&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="n"&gt;queryClient&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;clear&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This clears both the query cache and mutation cache associated with that client.&lt;/p&gt;

&lt;p&gt;You can also cancel active queries:&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;await&lt;/span&gt; &lt;span class="n"&gt;queryClient&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;cancelQueries&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;But the order of operations matters.&lt;/p&gt;

&lt;h3&gt;
  
  
  A safer logout strategy
&lt;/h3&gt;

&lt;p&gt;The application should immediately stop displaying authenticated screens when logout begins.&lt;/p&gt;

&lt;p&gt;Then it should coordinate request cancellation, cache cleanup, and session revocation.&lt;/p&gt;

&lt;p&gt;Here's a simplified 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="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;handleLogout&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;// The application must stop rendering private screens.&lt;/span&gt;
  &lt;span class="nf"&gt;setAuthStatus&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"signing-out"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="c1"&gt;// Cancel active queries.&lt;/span&gt;
  &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;queryClient&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;cancelQueries&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;// Clear in-memory query and mutation caches.&lt;/span&gt;
  &lt;span class="n"&gt;queryClient&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;clear&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;// Revoke the authenticated session on the server.&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="n"&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="nb"&gt;fetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"/api/logout"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;method&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"POST"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;credentials&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"include"&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="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="n"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ok&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// Keep private screens hidden and provide&lt;/span&gt;
    &lt;span class="c1"&gt;// a way to retry or recover from this error.&lt;/span&gt;
    &lt;span class="nf"&gt;setAuthStatus&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"logout-error"&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="p"&gt;}&lt;/span&gt;

  &lt;span class="nf"&gt;setAuthStatus&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"signed-out"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This assumes your application uses &lt;code&gt;authStatus&lt;/code&gt; to stop rendering all protected components when the value isn't an authenticated state.&lt;/p&gt;

&lt;p&gt;It's an example of the sequence, not a complete authentication implementation.&lt;/p&gt;

&lt;p&gt;A few things are important here:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Clearing React Query's cache doesn't revoke a server session.&lt;/li&gt;
&lt;li&gt;Cancelling a query doesn't undo a request the server has already processed.&lt;/li&gt;
&lt;li&gt;An unsuccessful logout request must not be presented as successful server-side logout.&lt;/li&gt;
&lt;li&gt;If you're persisting queries to browser storage, that persisted data must be handled separately.&lt;/li&gt;
&lt;li&gt;Other caches, global stores, service workers, and open browser tabs may also need attention.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;For applications with sensitive information, the full authentication lifecycle deserves its own design and testing.&lt;/p&gt;

&lt;p&gt;A cache clear is useful, but it isn't a complete security solution.&lt;/p&gt;

&lt;h2&gt;
  
  
  7. Another mistake: showing previous data during account switches
&lt;/h2&gt;

&lt;p&gt;There's another subtle situation worth understanding.&lt;/p&gt;

&lt;p&gt;TanStack Query supports showing previous query data while the next result loads.&lt;/p&gt;

&lt;p&gt;For 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="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="n"&gt;keepPreviousData&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="n"&gt;useQuery&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="s2"&gt;"@tanstack/react-query"&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="n"&gt;data&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;useQuery&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="n"&gt;queryKey&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"products"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;page&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="n"&gt;queryFn&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="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;fetchProducts&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;page&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="n"&gt;placeholderData&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;keepPreviousData&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is often useful for pagination.&lt;/p&gt;

&lt;p&gt;Instead of briefly displaying an empty table whenever the page changes, you can keep showing the previous page until the new result arrives.&lt;/p&gt;

&lt;p&gt;That's a good experience for many interfaces.&lt;/p&gt;

&lt;p&gt;But imagine using the same approach when switching between private workspaces.&lt;/p&gt;

&lt;p&gt;Even with correctly scoped query keys, previous-data placeholders can temporarily display information belonging to the previous workspace.&lt;/p&gt;

&lt;p&gt;The query cache entries may be isolated correctly, while the component still renders the previous result as placeholder data.&lt;/p&gt;

&lt;p&gt;These are different behaviors.&lt;/p&gt;

&lt;p&gt;For public product listings, this might be harmless.&lt;/p&gt;

&lt;p&gt;For customer invoices, medical records, financial reports, or private business dashboards, it could be unacceptable.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;A smoother loading experience isn't always the correct loading experience.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;When the authenticated user or tenant changes, I generally prefer a loading or empty state over showing data from the previous context.&lt;/p&gt;

&lt;h2&gt;
  
  
  8. Is this actually a security vulnerability?
&lt;/h2&gt;

&lt;p&gt;It's important to be precise here.&lt;/p&gt;

&lt;p&gt;A wrong query key can cause the frontend to display data fetched under a previous authenticated context.&lt;/p&gt;

&lt;p&gt;That can create a confidentiality problem, especially on shared devices or when sensitive information remains visible after switching accounts.&lt;/p&gt;

&lt;p&gt;But this doesn't automatically mean the backend has an authorization vulnerability.&lt;/p&gt;

&lt;p&gt;Consider two different scenarios.&lt;/p&gt;

&lt;h3&gt;
  
  
  Scenario A: Client-side cache isolation failure
&lt;/h3&gt;

&lt;p&gt;Alice is authorized to access Company A.&lt;/p&gt;

&lt;p&gt;The backend correctly returns Company A's orders.&lt;/p&gt;

&lt;p&gt;Later, the frontend switches to Company B but mistakenly displays the previously cached Company A orders.&lt;/p&gt;

&lt;p&gt;The UI has displayed inappropriate cached data.&lt;/p&gt;

&lt;p&gt;That doesn't necessarily mean the server authorized an invalid request.&lt;/p&gt;

&lt;h3&gt;
  
  
  Scenario B: Broken backend authorization
&lt;/h3&gt;

&lt;p&gt;Alice requests:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;GET /api/workspaces/company-b/orders
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Even though she doesn't have permission to access Company B.&lt;/p&gt;

&lt;p&gt;The server returns Company B's private orders anyway.&lt;/p&gt;

&lt;p&gt;That's a backend authorization failure.&lt;/p&gt;

&lt;p&gt;Correcting React Query keys won't fix it.&lt;/p&gt;

&lt;p&gt;The backend must verify that the authenticated user has access to the requested resource, regardless of what the frontend sends.&lt;/p&gt;

&lt;p&gt;In a multi-tenant application, I would treat both areas seriously:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Frontend isolation:&lt;/strong&gt; Make sure cached data is associated with the correct user and tenant.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Backend authorization:&lt;/strong&gt; Enforce resource access permissions on every protected request.&lt;/p&gt;

&lt;p&gt;Neither replaces the other.&lt;/p&gt;

&lt;h2&gt;
  
  
  9. Let's add a test for the cache behavior
&lt;/h2&gt;

&lt;p&gt;It's easy to assume a bug is fixed because switching users appears to work manually.&lt;/p&gt;

&lt;p&gt;I'd also want a small automated test that verifies how the cache behaves.&lt;/p&gt;

&lt;p&gt;Install Vitest:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm install -D vitest
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Create &lt;code&gt;src/query-cache.test.js&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;describe&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;it&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="s2"&gt;"vitest"&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="nc"&gt;QueryClient&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="s2"&gt;"@tanstack/react-query"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="n"&gt;orders&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="n"&gt;alice&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"Alice Order"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="n"&gt;bob&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"Bob Order"&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="nf"&gt;describe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"React Query cache isolation"&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="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nf"&gt;it&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"demonstrates the shared-key mistake"&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="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="n"&gt;client&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;QueryClient&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="n"&gt;aliceOrders&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;fetchQuery&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="n"&gt;queryKey&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"orders"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="n"&gt;queryFn&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="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;orders&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;alice&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="n"&gt;staleTime&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;Infinity&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="kd"&gt;const&lt;/span&gt; &lt;span class="n"&gt;bobOrders&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;fetchQuery&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="n"&gt;queryKey&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"orders"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="n"&gt;queryFn&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="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;orders&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;bob&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="n"&gt;staleTime&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;Infinity&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="nf"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;aliceOrders&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;toEqual&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"Alice Order"&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;// Bob receives the cached result for Alice.&lt;/span&gt;
    &lt;span class="nf"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;bobOrders&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;toEqual&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"Alice Order"&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="n"&gt;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;clear&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="nf"&gt;it&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"keeps each user's orders separate"&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="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="n"&gt;client&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;QueryClient&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="n"&gt;aliceOrders&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;fetchQuery&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="n"&gt;queryKey&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"orders"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"alice"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="n"&gt;queryFn&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="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;orders&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;alice&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="kd"&gt;const&lt;/span&gt; &lt;span class="n"&gt;bobOrders&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;fetchQuery&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="n"&gt;queryKey&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"orders"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"bob"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="n"&gt;queryFn&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="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;orders&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;bob&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="nf"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;aliceOrders&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;toEqual&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"Alice Order"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nf"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;bobOrders&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;toEqual&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"Bob Order"&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="n"&gt;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;clear&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;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;Run:&lt;br&gt;
&lt;/p&gt;

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

&lt;/div&gt;



&lt;p&gt;Notice that the first test deliberately expects the wrong result.&lt;/p&gt;

&lt;p&gt;It's a regression demonstration: it confirms that the same fresh cache key is being reused.&lt;/p&gt;

&lt;p&gt;The second test verifies that distinct keys keep the data separate.&lt;/p&gt;

&lt;p&gt;This doesn't replace UI-level or authentication testing. In a production application, I'd also test account switching while requests are in progress, logout behavior, tenant authorization, and persisted cache cleanup.&lt;/p&gt;

&lt;h2&gt;
  
  
  10. My checklist before shipping a multi-tenant dashboard
&lt;/h2&gt;

&lt;p&gt;When reviewing a React Query implementation, these are the cases I'd verify:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Scenario&lt;/th&gt;
&lt;th&gt;Expected behavior&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Alice opens her orders&lt;/td&gt;
&lt;td&gt;Alice's orders appear&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Alice switches to Bob in the demo&lt;/td&gt;
&lt;td&gt;Bob's orders appear with a different key&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;A user switches organizations&lt;/td&gt;
&lt;td&gt;Only the selected organization's orders appear&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Filters or pagination change&lt;/td&gt;
&lt;td&gt;The query uses the correct cache identity&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;A user logs out&lt;/td&gt;
&lt;td&gt;Protected screens stop rendering and cached session data is cleaned up&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;An old request resolves after logout&lt;/td&gt;
&lt;td&gt;Its result doesn't reappear in the new session's private UI&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;An unauthorized workspace is requested&lt;/td&gt;
&lt;td&gt;The server rejects access&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Previous-data placeholders are enabled&lt;/td&gt;
&lt;td&gt;Private data isn't shown across authentication or tenant boundaries&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;One detail I find especially important is testing with more than one account.&lt;/p&gt;

&lt;p&gt;An application can look completely correct during development when you always test with the same user and workspace.&lt;/p&gt;

&lt;p&gt;Context-switching bugs often become visible only when you deliberately switch accounts, organizations, or permissions.&lt;/p&gt;

&lt;h2&gt;
  
  
  Final thoughts
&lt;/h2&gt;

&lt;p&gt;TanStack Query makes it much easier to manage server state in React applications.&lt;/p&gt;

&lt;p&gt;Caching, refetching, request deduplication, loading states, and error handling are all valuable features.&lt;/p&gt;

&lt;p&gt;But caching introduces a responsibility that we shouldn't overlook.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The cache needs to know what makes one dataset different from another.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Sometimes that difference is a product ID.&lt;/p&gt;

&lt;p&gt;Sometimes it's a page number or filter.&lt;/p&gt;

&lt;p&gt;And in SaaS applications, it might be the current user, organization, or workspace.&lt;/p&gt;

&lt;p&gt;This small change:&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;// Too generic for user-specific data&lt;/span&gt;
&lt;span class="n"&gt;queryKey&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"orders"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;to:&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;// Separates users and workspaces&lt;/span&gt;
&lt;span class="n"&gt;queryKey&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"orders"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;userId&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;workspaceId&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;can prevent a surprisingly confusing class of bugs.&lt;/p&gt;

&lt;p&gt;But the bigger lesson isn't simply to add more identifiers to every query key.&lt;/p&gt;

&lt;p&gt;It's to think carefully about data ownership, cache lifetime, authentication boundaries, and what the user is actually allowed to see.&lt;/p&gt;

&lt;p&gt;A working API doesn't guarantee that the interface is displaying the right data.&lt;/p&gt;

&lt;p&gt;That's something worth testing before calling a feature production-ready.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;How do you handle React Query caching when users log out or switch workspaces? Do you clear the cache, use user-scoped query keys, or combine both approaches?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;I'd be interested to hear how others handle this in production.&lt;/p&gt;




&lt;h3&gt;
  
  
  Further reading
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://tanstack.com/query/latest/docs/framework/react/guides/query-keys" rel="noopener noreferrer"&gt;TanStack Query — Query Keys&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://tanstack.com/query/latest/docs/framework/react/guides/query-cancellation" rel="noopener noreferrer"&gt;TanStack Query — Query Cancellation&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://tanstack.com/query/latest/docs/framework/react/guides/placeholder-query-data" rel="noopener noreferrer"&gt;TanStack Query — Placeholder Query Data&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://tanstack.com/query/latest/docs/framework/react/reference/classes/QueryClient" rel="noopener noreferrer"&gt;TanStack Query — QueryClient API&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>react</category>
      <category>javascript</category>
      <category>webdev</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>Why Disabling the Submit Button Isn't Enough: Preventing Duplicate Requests in React &amp; Node.js</title>
      <dc:creator>Md Mohosin Ali Shah</dc:creator>
      <pubDate>Mon, 09 Nov 2026 23:05:19 +0000</pubDate>
      <link>https://dev.to/mohosin2126/why-disabling-the-submit-button-isnt-enough-preventing-duplicate-requests-in-react-nodejs-5c3p</link>
      <guid>https://dev.to/mohosin2126/why-disabling-the-submit-button-isnt-enough-preventing-duplicate-requests-in-react-nodejs-5c3p</guid>
      <description>&lt;p&gt;Imagine this scenario:&lt;/p&gt;

&lt;p&gt;A customer clicks &lt;strong&gt;Place Order&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;The request reaches your server, the database successfully creates the order, but the response never reaches the browser because of a network issue.&lt;/p&gt;

&lt;p&gt;The customer assumes something went wrong and clicks again.&lt;/p&gt;

&lt;p&gt;Now you have &lt;strong&gt;two orders instead of one&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;This is a common reliability problem in web applications.&lt;/p&gt;

&lt;p&gt;Disabling a submit button might prevent accidental double-clicks, but it doesn't guarantee that an operation will execute only once.&lt;/p&gt;

&lt;p&gt;Let's explore how to handle this properly using React, Node.js, and PostgreSQL.&lt;/p&gt;

&lt;h2&gt;
  
  
  1. Frontend protection is only the first layer
&lt;/h2&gt;

&lt;p&gt;In React, we often prevent repeated submissions using a loading state:&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;const&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;isSubmitting&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;setIsSubmitting&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="kc"&gt;false&lt;/span&gt;&lt;span class="p"&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;handleSubmit&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;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;isSubmitting&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="nf"&gt;setIsSubmitting&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="k"&gt;try&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;createOrder&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="k"&gt;finally&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nf"&gt;setIsSubmitting&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is useful for improving the user experience.&lt;/p&gt;

&lt;p&gt;But there is a problem.&lt;/p&gt;

&lt;p&gt;Duplicate requests can still happen because of:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Network retries&lt;/li&gt;
&lt;li&gt;Multiple browser tabs&lt;/li&gt;
&lt;li&gt;Concurrent API calls&lt;/li&gt;
&lt;li&gt;Repeated webhook deliveries&lt;/li&gt;
&lt;li&gt;Client or server failures&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Even rapid calls within the same render cycle can observe the same React state before an update takes effect.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The important lesson: frontend protection improves UX, but backend protection ensures data integrity.&lt;/strong&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  2. Understanding idempotency
&lt;/h2&gt;

&lt;p&gt;An operation is idempotent when performing it multiple times has the same intended effect as performing it once.&lt;/p&gt;

&lt;p&gt;For example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;POST /api/orders
Idempotency-Key: order-abc-123
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If the same request is submitted again with the same idempotency key, the server should recognize that the operation has already been processed.&lt;/p&gt;

&lt;p&gt;Instead of creating a new order, it can return the result of the original operation.&lt;/p&gt;

&lt;p&gt;This is particularly important for:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Order creation&lt;/li&gt;
&lt;li&gt;Payment processing&lt;/li&gt;
&lt;li&gt;Subscription activation&lt;/li&gt;
&lt;li&gt;Inventory adjustments&lt;/li&gt;
&lt;li&gt;Financial transactions&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;However, an important distinction is that &lt;strong&gt;an idempotency key represents one intended operation, not an entire user session&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Retrying the same order should reuse the original key. Creating a genuinely new order should use a new one.&lt;/p&gt;

&lt;h2&gt;
  
  
  3. Implementing idempotency in React
&lt;/h2&gt;

&lt;p&gt;We can generate a unique idempotency key using the browser's built-in Web Crypto API.&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="n"&gt;useRef&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="s2"&gt;"react"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nc"&gt;OrderForm&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="n"&gt;idempotencyKeyRef&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;null&lt;/span&gt;&lt;span class="p"&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;createOrder&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;order&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// Generate once for this intended operation.&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="n"&gt;idempotencyKeyRef&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;current&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="n"&gt;idempotencyKeyRef&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;current&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;crypto&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;randomUUID&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="kd"&gt;const&lt;/span&gt; &lt;span class="n"&gt;key&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;idempotencyKeyRef&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;current&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="n"&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="nb"&gt;fetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"/api/orders"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="n"&gt;method&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"POST"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="n"&gt;headers&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="s2"&gt;"Content-Type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"application/json"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="s2"&gt;"Idempotency-Key"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;key&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="n"&gt;body&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&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="n"&gt;order&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="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="n"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ok&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nb"&gt;Error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"Order request failed"&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="n"&gt;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&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="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="c1"&gt;// Reset only after confirmed completion.&lt;/span&gt;
    &lt;span class="n"&gt;idempotencyKeyRef&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&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="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="c1"&gt;// Use createOrder in your submission flow.&lt;/span&gt;
  &lt;span class="c1"&gt;// Prevent concurrent submissions from this form.&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;There are two important details here.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;First: Reuse the same key when retrying an uncertain operation.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;If the request reaches the server but the network fails before a response arrives, the client cannot know whether the order was created.&lt;/p&gt;

&lt;p&gt;In that situation, we preserve the idempotency key so the same operation can be retried safely.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Second: Reset the key after confirmed success.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Once the server confirms the order was successfully created, the key is cleared.&lt;/p&gt;

&lt;p&gt;This ensures the next legitimate order receives a new key instead of accidentally reusing the previous one.&lt;/p&gt;

&lt;p&gt;For example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Order A
  |
  Key: abc-123
  |
  Network failure
  |
  Retry with abc-123
  |
  Server returns existing Order A
  |
  Confirmed success
  |
  Clear key
  |
Order B
  |
  Generate new key: xyz-456
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A few additional considerations for production applications:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Retry with the same order data, not a modified payload.&lt;/li&gt;
&lt;li&gt;Prevent overlapping submissions of the same form operation.&lt;/li&gt;
&lt;li&gt;Preserve an unfinished operation's key and payload across page refreshes if recovery is required.&lt;/li&gt;
&lt;li&gt;Clear the key only after the operation's completion has been confirmed. A &lt;code&gt;202 Accepted&lt;/code&gt; response, for example, might only indicate that processing has started.&lt;/li&gt;
&lt;li&gt;Handle validation failures separately from uncertain network failures.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The main principle remains the same:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;One intended operation = one idempotency key.&lt;/strong&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  4. Protecting the database against duplicate inserts
&lt;/h2&gt;

&lt;p&gt;Frontend protection and idempotency middleware are useful, but concurrent requests can still introduce race conditions if the database isn't protected.&lt;/p&gt;

&lt;p&gt;Consider this backend logic:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="n"&gt;existingOrder&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;findOrder&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;key&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="n"&gt;existingOrder&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;createOrder&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Looks fine, right?&lt;/p&gt;

&lt;p&gt;Not necessarily.&lt;/p&gt;

&lt;p&gt;Two requests can execute simultaneously:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Request A                  Request B
    |                          |
Check existing order       Check existing order
    |                          |
Not found                  Not found
    |                          |
Create order               Create order
    |                          |
Success                    Success
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Both requests can pass the check before either creates the record.&lt;/p&gt;

&lt;p&gt;This is a classic check-then-insert race condition.&lt;/p&gt;

&lt;h3&gt;
  
  
  A better approach: Database uniqueness constraints
&lt;/h3&gt;

&lt;p&gt;In PostgreSQL, enforce uniqueness at the database level.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;CREATE&lt;/span&gt; &lt;span class="k"&gt;UNIQUE&lt;/span&gt; &lt;span class="k"&gt;INDEX&lt;/span&gt; &lt;span class="n"&gt;unique_order_request&lt;/span&gt;
&lt;span class="k"&gt;ON&lt;/span&gt; &lt;span class="n"&gt;orders&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;user_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;idempotency_key&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This assumes your orders table contains the corresponding columns and that idempotency keys are required for this operation.&lt;/p&gt;

&lt;p&gt;Now use PostgreSQL's &lt;code&gt;ON CONFLICT&lt;/code&gt; functionality:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;INSERT&lt;/span&gt; &lt;span class="k"&gt;INTO&lt;/span&gt; &lt;span class="n"&gt;orders&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="n"&gt;user_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="n"&gt;idempotency_key&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="n"&gt;product_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="n"&gt;quantity&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="n"&gt;request_hash&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="nv"&gt;$1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$3&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$4&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$5&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;ON&lt;/span&gt; &lt;span class="k"&gt;CONFLICT&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;user_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;idempotency_key&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;DO&lt;/span&gt; &lt;span class="k"&gt;NOTHING&lt;/span&gt;
&lt;span class="k"&gt;RETURNING&lt;/span&gt; &lt;span class="n"&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;If the insert succeeds, the query returns the new order ID.&lt;/p&gt;

&lt;p&gt;But there is an important detail.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;When a conflict occurs, &lt;code&gt;DO NOTHING RETURNING id&lt;/code&gt; returns zero rows.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;It does not automatically return the existing order's ID.&lt;/p&gt;

&lt;p&gt;So the backend needs to handle both cases.&lt;/p&gt;

&lt;h3&gt;
  
  
  Handling an existing order
&lt;/h3&gt;

&lt;p&gt;If the insert returns no rows, retrieve the existing order using the same user ID and idempotency key.&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="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;request_hash&lt;/span&gt;
&lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;orders&lt;/span&gt;
&lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="n"&gt;user_id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;$1&lt;/span&gt;
  &lt;span class="k"&gt;AND&lt;/span&gt; &lt;span class="n"&gt;idempotency_key&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;$2&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then compare the stored request hash with the incoming request's hash.&lt;/p&gt;

&lt;p&gt;There are two possible outcomes:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Case 1: The request hashes match&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The client is retrying the same intended operation.&lt;/p&gt;

&lt;p&gt;Return the previously created order instead of creating another one.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Case 2: The request hashes don't match&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The same idempotency key is being reused with different order parameters.&lt;/p&gt;

&lt;p&gt;Reject the request:&lt;br&gt;
&lt;/p&gt;

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

{
  "error": "Idempotency key already used with different request data"
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This prevents the application from silently treating two different operations as the same request.&lt;/p&gt;

&lt;p&gt;For request hashing, both attempts must use a consistent representation of the relevant order data. The server should calculate or verify the hash rather than blindly trusting a value supplied by the client.&lt;/p&gt;

&lt;h3&gt;
  
  
  Why not use ON CONFLICT DO UPDATE?
&lt;/h3&gt;

&lt;p&gt;Another approach is to perform an update during a conflict so PostgreSQL can return the existing row within the same statement.&lt;/p&gt;

&lt;p&gt;For example, a no-op update can return the existing order ID and request hash.&lt;/p&gt;

&lt;p&gt;However, this approach has trade-offs:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;It can create a new row version even when values don't meaningfully change.&lt;/li&gt;
&lt;li&gt;It acquires row locks.&lt;/li&gt;
&lt;li&gt;It may trigger update-related behavior.&lt;/li&gt;
&lt;li&gt;Some techniques for identifying whether the row was inserted rely on PostgreSQL implementation details.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;For many applications, keeping &lt;code&gt;DO NOTHING&lt;/code&gt; and performing a follow-up &lt;code&gt;SELECT&lt;/code&gt; is a straightforward choice.&lt;/p&gt;

&lt;p&gt;With PostgreSQL's default &lt;code&gt;READ COMMITTED&lt;/code&gt; isolation, the follow-up query is a separate statement and can see the winning transaction's committed row.&lt;/p&gt;

&lt;p&gt;The application should still handle unexpected missing rows or transaction failures appropriately.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Database constraints provide protection that application-level checks alone cannot guarantee.&lt;/strong&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  5. What about transactions?
&lt;/h2&gt;

&lt;p&gt;Preventing duplicate orders is only part of the problem.&lt;/p&gt;

&lt;p&gt;Creating an order might involve multiple operations:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Create the order.&lt;/li&gt;
&lt;li&gt;Reserve inventory.&lt;/li&gt;
&lt;li&gt;Update related database records.&lt;/li&gt;
&lt;li&gt;Record the transaction.&lt;/li&gt;
&lt;li&gt;Send a notification.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;If one operation fails halfway through, the system can end up in an inconsistent state.&lt;/p&gt;

&lt;p&gt;Database transactions help ensure that related database modifications succeed or fail together.&lt;/p&gt;

&lt;p&gt;For example, creating an order and reserving inventory should generally be part of the same database transaction when they must succeed together.&lt;/p&gt;

&lt;p&gt;That way, a failure doesn't leave an order committed without its required inventory changes.&lt;/p&gt;

&lt;p&gt;The idempotency check and the business operation also need to be coordinated correctly. Preventing duplicate inserts alone doesn't guarantee every related operation executes only once.&lt;/p&gt;

&lt;p&gt;However, external operations such as sending emails or publishing messages are not automatically rolled back with a database transaction.&lt;/p&gt;

&lt;p&gt;For these cases, patterns such as the &lt;strong&gt;transactional outbox&lt;/strong&gt; and idempotent event consumers can improve reliability.&lt;/p&gt;

&lt;p&gt;This is an important distinction when designing systems that interact with external services.&lt;/p&gt;

&lt;h2&gt;
  
  
  6. Test real failure scenarios
&lt;/h2&gt;

&lt;p&gt;A feature working during normal testing doesn't necessarily mean it is reliable under failure conditions.&lt;/p&gt;

&lt;p&gt;Here are some scenarios worth testing:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Scenario&lt;/th&gt;
&lt;th&gt;Expected behavior&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Two concurrent requests with the same key&lt;/td&gt;
&lt;td&gt;Only one order is created&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Retry after the original request commits&lt;/td&gt;
&lt;td&gt;Existing order is returned&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Same key with different request data&lt;/td&gt;
&lt;td&gt;Request is rejected with &lt;code&gt;409 Conflict&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Failed database transaction&lt;/td&gt;
&lt;td&gt;No partial order is committed&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Two legitimate operations with different keys&lt;/td&gt;
&lt;td&gt;Both can proceed&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;New order after successful submission&lt;/td&gt;
&lt;td&gt;A fresh idempotency key is generated&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Network failure after server commits&lt;/td&gt;
&lt;td&gt;Retry returns the original order&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;One especially useful test is simulating a lost response after a successful database commit.&lt;/p&gt;

&lt;p&gt;The client should be able to retry without accidentally creating another order.&lt;/p&gt;

&lt;p&gt;These tests provide more confidence than simply checking whether a submit button becomes disabled.&lt;/p&gt;

&lt;h2&gt;
  
  
  7. The approach I recommend
&lt;/h2&gt;

&lt;p&gt;For reliable applications, multiple layers should work together:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Frontend safeguards&lt;/strong&gt; improve the user experience and discourage accidental repeated submissions.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Backend idempotency&lt;/strong&gt; identifies repeated attempts to perform the same operation and distinguishes them from genuinely new requests.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Database constraints&lt;/strong&gt; protect against duplicate inserts, including concurrent requests.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Transactions and carefully designed side effects&lt;/strong&gt; help maintain consistency across the entire workflow.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Failure scenario testing&lt;/strong&gt; verifies that retries and concurrency behave as expected outside the normal happy path.&lt;/p&gt;

&lt;p&gt;No single layer solves every problem.&lt;/p&gt;

&lt;p&gt;The goal isn't just to make an API request succeed.&lt;/p&gt;

&lt;p&gt;It's to make sure retries, failures, and concurrent requests don't produce unintended results.&lt;/p&gt;

&lt;h2&gt;
  
  
  Final thoughts
&lt;/h2&gt;

&lt;p&gt;Building reliable software requires thinking beyond the happy path.&lt;/p&gt;

&lt;p&gt;A submit button can be disabled. A network request can fail. A server can process the same operation more than once.&lt;/p&gt;

&lt;p&gt;The application should still behave predictably.&lt;/p&gt;

&lt;p&gt;A combination of frontend safeguards, idempotency, database constraints, and transaction handling is generally more reliable than depending on any one of them.&lt;/p&gt;

&lt;p&gt;There is also an important distinction between preventing duplicate requests and guaranteeing exactly-once processing across an entire distributed system. Idempotency makes repeated attempts safe, but external side effects still require careful coordination.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What's your approach to preventing duplicate requests in production applications?&lt;/strong&gt;&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;Further reading:&lt;/strong&gt; &lt;a href="https://www.postgresql.org/docs/current/sql-insert.html" rel="noopener noreferrer"&gt;PostgreSQL INSERT / ON CONFLICT documentation&lt;/a&gt;&lt;/p&gt;

</description>
      <category>react</category>
      <category>node</category>
      <category>web3</category>
      <category>postgres</category>
    </item>
    <item>
      <title>RepoLens Version 2 Ranked #7 on Product Hunt — Building AI for Code Change Intelligence</title>
      <dc:creator>Md Mohosin Ali Shah</dc:creator>
      <pubDate>Sun, 29 Mar 2026 08:54:57 +0000</pubDate>
      <link>https://dev.to/mohosin2126/repolens-version-2-ranked-7-on-product-hunt-building-ai-for-code-change-intelligence-5ggj</link>
      <guid>https://dev.to/mohosin2126/repolens-version-2-ranked-7-on-product-hunt-building-ai-for-code-change-intelligence-5ggj</guid>
      <description>&lt;p&gt;RepoLens Version 2 ranked &lt;strong&gt;#7 on Product Hunt&lt;/strong&gt; this week.&lt;/p&gt;

&lt;p&gt;That number was exciting, but what mattered more to me was what it represented.&lt;/p&gt;

&lt;p&gt;It felt like a signal that this problem resonates with more people than just me.&lt;/p&gt;

&lt;p&gt;Because RepoLens started from a frustration I kept running into again and again:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;understanding a codebase takes too long.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;When I opened a new repository, I would usually go through the same cycle:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;open random folders&lt;/li&gt;
&lt;li&gt;search for routes&lt;/li&gt;
&lt;li&gt;trace imports manually&lt;/li&gt;
&lt;li&gt;guess how modules fit together&lt;/li&gt;
&lt;li&gt;try to understand the architecture from scattered files and tribal knowledge&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;That process was slow, repetitive, and mentally expensive.&lt;/p&gt;

&lt;p&gt;So I built &lt;strong&gt;RepoLens Version 1&lt;/strong&gt; to solve that.&lt;/p&gt;

&lt;p&gt;Version 1 was about helping developers understand a repository faster.&lt;/p&gt;

&lt;p&gt;It could analyze repositories, detect stack and structure, map modules and dependencies, extract API endpoints, generate docs, support grounded repository chat, compare branches, and turn a codebase into something much more explorable.&lt;/p&gt;

&lt;p&gt;That solved an important problem.&lt;/p&gt;

&lt;p&gt;But while building it, I realized I had only solved the first half of the real pain.&lt;/p&gt;

&lt;h2&gt;
  
  
  Understanding a Repository Once Is Not Enough
&lt;/h2&gt;

&lt;p&gt;A repository is not static.&lt;/p&gt;

&lt;p&gt;It keeps moving.&lt;/p&gt;

&lt;p&gt;Pull requests change behavior.&lt;br&gt;&lt;br&gt;
Endpoints shift.&lt;br&gt;&lt;br&gt;
Dependencies evolve.&lt;br&gt;&lt;br&gt;
Architecture drifts.&lt;br&gt;&lt;br&gt;
And teams are constantly trying to answer a harder question:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;what changed, what was affected, and what actually matters?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;That is where RepoLens Version 2 came from.&lt;/p&gt;

&lt;p&gt;Version 2 is not just about repository understanding.&lt;/p&gt;

&lt;p&gt;It is about &lt;strong&gt;change intelligence&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;That shift changed how I thought about the product.&lt;/p&gt;

&lt;p&gt;Instead of only helping someone understand a repository once, I wanted RepoLens to become useful inside the real engineering loop:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;when a pull request opens&lt;/li&gt;
&lt;li&gt;when a branch changes&lt;/li&gt;
&lt;li&gt;when APIs move&lt;/li&gt;
&lt;li&gt;when architecture starts drifting&lt;/li&gt;
&lt;li&gt;when a team needs clarity before review or release&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  What Changed in Version 2
&lt;/h2&gt;

&lt;p&gt;RepoLens Version 2 now focuses on helping teams understand change with more structure and more trust.&lt;/p&gt;

&lt;p&gt;It can now:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;analyze pull requests and generate engineering summaries&lt;/li&gt;
&lt;li&gt;detect affected modules&lt;/li&gt;
&lt;li&gt;detect changed endpoints&lt;/li&gt;
&lt;li&gt;highlight likely review hotspots&lt;/li&gt;
&lt;li&gt;support branch-aware and PR-aware repository chat&lt;/li&gt;
&lt;li&gt;show exact code snippet references and confidence signals&lt;/li&gt;
&lt;li&gt;run incremental analysis instead of full rebuilds every time&lt;/li&gt;
&lt;li&gt;sync automatically from GitHub events&lt;/li&gt;
&lt;li&gt;detect architecture drift&lt;/li&gt;
&lt;li&gt;send alerts for important changes and failed analysis runs&lt;/li&gt;
&lt;li&gt;track workspace usage and operational health&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The product story became much clearer through this work:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Version 1:&lt;/strong&gt; Know any repo fast&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Version 2:&lt;/strong&gt; Know what changed and what matters&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;That clarity was one of the biggest wins for me as a builder.&lt;/p&gt;

&lt;h2&gt;
  
  
  What I Learned Building It
&lt;/h2&gt;

&lt;p&gt;One of the biggest lessons from building RepoLens is that &lt;strong&gt;generic AI output is not enough for engineering workflows&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;It is easy to generate a summary.&lt;/p&gt;

&lt;p&gt;It is much harder to generate something that engineers can actually trust.&lt;/p&gt;

&lt;p&gt;That means the system has to do more than just “talk about code.”&lt;/p&gt;

&lt;p&gt;It has to first build a structured understanding of the repository and its changes, then use AI on top of that structure to make the output more useful, grounded, and verifiable.&lt;/p&gt;

&lt;p&gt;That has shaped almost every product decision in RepoLens so far.&lt;/p&gt;

&lt;p&gt;I do not want RepoLens to be another tool that gives impressive-sounding answers without enough proof behind them.&lt;/p&gt;

&lt;p&gt;I want it to help teams see:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;where an answer came from&lt;/li&gt;
&lt;li&gt;what changed&lt;/li&gt;
&lt;li&gt;what was affected&lt;/li&gt;
&lt;li&gt;what deserves attention&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  The Product Hunt Launch
&lt;/h2&gt;

&lt;p&gt;Launching RepoLens Version 2 on Product Hunt and seeing it reach &lt;strong&gt;#7&lt;/strong&gt; was encouraging not just because of visibility, but because of the kinds of conversations it created.&lt;/p&gt;

&lt;p&gt;The most interesting feedback was not about “AI” in the abstract.&lt;/p&gt;

&lt;p&gt;It was about very practical engineering questions:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;How do you handle overlapping PRs?&lt;/li&gt;
&lt;li&gt;How reliable is changed endpoint detection?&lt;/li&gt;
&lt;li&gt;Can this work beyond a single framework?&lt;/li&gt;
&lt;li&gt;Which signals are actually useful in day-to-day development?&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;That is exactly the kind of feedback I hoped for.&lt;/p&gt;

&lt;p&gt;It means the product is starting to move from “interesting demo” territory into “could this fit real engineering workflows?” territory.&lt;/p&gt;

&lt;p&gt;That is the transition I care about most.&lt;/p&gt;

&lt;h2&gt;
  
  
  Where RepoLens Is Going
&lt;/h2&gt;

&lt;p&gt;RepoLens started as a way to understand repositories faster.&lt;/p&gt;

&lt;p&gt;Now it is becoming a way to understand &lt;strong&gt;change&lt;/strong&gt; faster.&lt;/p&gt;

&lt;p&gt;That feels like the more important problem.&lt;/p&gt;

&lt;p&gt;Because teams do not just need help reading code.&lt;/p&gt;

&lt;p&gt;They need help reasoning about evolving systems.&lt;/p&gt;

&lt;p&gt;That is what I want RepoLens to keep getting better at.&lt;/p&gt;

&lt;h2&gt;
  
  
  Links
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Live platform&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
&lt;a href="https://repolensai.com" rel="noopener noreferrer"&gt;https://repolensai.com&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Open-source analysis engine&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
&lt;a href="https://github.com/mohosin2126/repolens-community" rel="noopener noreferrer"&gt;https://github.com/mohosin2126/repolens-community&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Product Hunt launch&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
&lt;a href="https://www.producthunt.com/products/repolens" rel="noopener noreferrer"&gt;https://www.producthunt.com/products/repolens&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;If you work in fast-moving repositories, I would love to know:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Which signal would save your team the most time?&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;PR summaries&lt;/li&gt;
&lt;li&gt;affected modules&lt;/li&gt;
&lt;li&gt;changed endpoints&lt;/li&gt;
&lt;li&gt;review hotspots&lt;/li&gt;
&lt;li&gt;branch-aware chat&lt;/li&gt;
&lt;li&gt;architecture drift alerts&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>ai</category>
      <category>opensource</category>
      <category>productivity</category>
      <category>webdev</category>
    </item>
    <item>
      <title>RepoLens Version 2 - Know what changed and what matters</title>
      <dc:creator>Md Mohosin Ali Shah</dc:creator>
      <pubDate>Wed, 25 Mar 2026 17:30:00 +0000</pubDate>
      <link>https://dev.to/mohosin2126/repolens-version-2-know-what-changed-and-what-matters-1adi</link>
      <guid>https://dev.to/mohosin2126/repolens-version-2-know-what-changed-and-what-matters-1adi</guid>
      <description>&lt;p&gt;RepoLens Version 1 was about understanding a repository quickly.&lt;/p&gt;

&lt;p&gt;It helped answer:&lt;br&gt;
What is in this repository?&lt;br&gt;
How is the codebase structured?&lt;br&gt;
Where do I start?&lt;/p&gt;

&lt;p&gt;But after building and using it more, I realized something important:&lt;/p&gt;

&lt;p&gt;understanding a codebase once is not enough.&lt;/p&gt;

&lt;p&gt;Repositories keep changing.&lt;br&gt;
Pull requests introduce risk.&lt;br&gt;
APIs evolve.&lt;br&gt;
Architecture drifts.&lt;br&gt;
And teams need better visibility into what changed, what was affected, and what actually matters.&lt;/p&gt;

&lt;p&gt;So I built RepoLens Version 2.&lt;/p&gt;

&lt;p&gt;RepoLens is no longer just a repository analysis dashboard.&lt;br&gt;
It is becoming a change intelligence platform for engineering teams.&lt;/p&gt;

&lt;p&gt;What RepoLens V2 Adds&lt;/p&gt;

&lt;p&gt;RepoLens can now:&lt;/p&gt;

&lt;p&gt;analyze pull requests and generate engineering summaries&lt;/p&gt;

&lt;p&gt;show affected modules, changed endpoints, and likely review hotspots&lt;/p&gt;

&lt;p&gt;run incremental analysis instead of reprocessing the whole repository every time&lt;/p&gt;

&lt;p&gt;sync automatically from GitHub push, PR, and merge events&lt;/p&gt;

&lt;p&gt;answer chat questions in repository, branch, PR, and branch-compare context&lt;/p&gt;

&lt;p&gt;show exact code snippets, references, and confidence signals in chat answers&lt;/p&gt;

&lt;p&gt;detect architecture drift and endpoint changes over time&lt;/p&gt;

&lt;p&gt;send alerts for meaningful changes and failed analysis runs&lt;/p&gt;

&lt;p&gt;track workspace usage, plan limits, and operational health&lt;/p&gt;

&lt;p&gt;What This Changes&lt;/p&gt;

&lt;p&gt;Version 1 helped understand a repository.&lt;/p&gt;

&lt;p&gt;Version 2 helps understand change.&lt;/p&gt;

&lt;p&gt;That means RepoLens starts becoming useful in everyday engineering workflows, not just onboarding.&lt;/p&gt;

&lt;p&gt;For example:&lt;/p&gt;

&lt;p&gt;reviewing pull requests&lt;/p&gt;

&lt;p&gt;tracking architecture changes across runs&lt;/p&gt;

&lt;p&gt;seeing what modules and endpoints were affected&lt;/p&gt;

&lt;p&gt;understanding branch differences faster&lt;/p&gt;

&lt;p&gt;getting grounded answers in the exact context you care about&lt;/p&gt;

&lt;p&gt;receiving alerts instead of checking manually&lt;/p&gt;

&lt;p&gt;Product Direction&lt;/p&gt;

&lt;p&gt;The RepoLens product story is becoming clearer:&lt;/p&gt;

&lt;p&gt;Version 1: Know any repo fast.&lt;br&gt;
Version 2: Know what changed and what matters.&lt;/p&gt;

&lt;p&gt;That shift is the part I’m most excited about.&lt;/p&gt;

&lt;p&gt;RepoLens is moving from static repository understanding&lt;br&gt;
to ongoing engineering intelligence.&lt;/p&gt;

&lt;p&gt;Live Platform&lt;br&gt;
&lt;a href="https://repolensai.com" rel="noopener noreferrer"&gt;https://repolensai.com&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Open Source Repository&lt;/p&gt;

&lt;p&gt;The open-source analysis engine is available here:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/mohosin2126/repolens-community" rel="noopener noreferrer"&gt;https://github.com/mohosin2126/repolens-community&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Version 2 LinkedIn Post&lt;br&gt;
&lt;a href="https://www.linkedin.com/posts/mohosin2126_opensource-ai-github-activity-7442612868300234752-HWkc?utm_source=share&amp;amp;utm_medium=member_desktop&amp;amp;rcm=ACoAADpg8RMBJaipaeKrpBc7xSoC8S90bR4HWpk" rel="noopener noreferrer"&gt;https://www.linkedin.com/posts/mohosin2126_opensource-ai-github-activity-7442612868300234752-HWkc?utm_source=share&amp;amp;utm_medium=member_desktop&amp;amp;rcm=ACoAADpg8RMBJaipaeKrpBc7xSoC8S90bR4HWpk&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Why I Built Version 2&lt;/p&gt;

&lt;p&gt;I wanted RepoLens to be useful not only when someone first opens a repository,&lt;br&gt;
but also when a team is actively changing it.&lt;/p&gt;

&lt;p&gt;Especially when:&lt;/p&gt;

&lt;p&gt;reviewing pull requests&lt;/p&gt;

&lt;p&gt;tracking risky changes&lt;/p&gt;

&lt;p&gt;watching API and architecture evolution&lt;/p&gt;

&lt;p&gt;keeping teams aligned as systems grow&lt;/p&gt;

&lt;p&gt;Open Core&lt;/p&gt;

&lt;p&gt;RepoLens continues to follow an open-core model.&lt;/p&gt;

&lt;p&gt;The reusable analysis engine remains open source, while the hosted platform builds on top of it with collaboration, change intelligence, notifications, workspace controls, and premium product features.&lt;/p&gt;

</description>
      <category>ai</category>
      <category>opensource</category>
      <category>productivity</category>
      <category>webdev</category>
    </item>
    <item>
      <title>I Shipped v0.3.0 of My Open Source Project Scaffolding CLI</title>
      <dc:creator>Md Mohosin Ali Shah</dc:creator>
      <pubDate>Wed, 18 Mar 2026 01:03:49 +0000</pubDate>
      <link>https://dev.to/mohosin2126/i-shipped-v030-of-my-open-source-project-scaffolding-cli-10ki</link>
      <guid>https://dev.to/mohosin2126/i-shipped-v030-of-my-open-source-project-scaffolding-cli-10ki</guid>
      <description>&lt;p&gt;A few days ago, I published my open source npm package: &lt;strong&gt;starter-structure-cli&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;It’s a Node.js CLI that scaffolds &lt;strong&gt;production-ready starter projects&lt;/strong&gt; from simple stack tokens.&lt;/p&gt;

&lt;p&gt;You can run something like:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;npx starter-structure-cli my-app react vite ts tailwind express prisma postgres&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;and get a structured project generated for you instead of spending hours wiring everything manually.&lt;/p&gt;

&lt;p&gt;Since publishing it, I’ve been improving it quickly, and today I shipped &lt;strong&gt;v0.3.0&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;What made this update especially exciting for me is that the package also started getting real usage.&lt;/p&gt;

&lt;p&gt;From &lt;strong&gt;March 3, 2026 to March 9, 2026&lt;/strong&gt;, it got &lt;strong&gt;480 downloads&lt;/strong&gt;.&lt;br&gt;
In the following week, it got &lt;strong&gt;35 more downloads&lt;/strong&gt;.&lt;br&gt;
That brings it to &lt;strong&gt;515 total downloads across the last two weeks&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;It’s still early, but seeing people actually try something I built has been genuinely motivating.&lt;/p&gt;


&lt;h2&gt;
  
  
  What &lt;code&gt;starter-structure-cli&lt;/code&gt; does
&lt;/h2&gt;

&lt;p&gt;The goal is simple:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Start coding faster without repeating the same setup work again and again.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Instead of manually creating folder structures, wiring frontend and backend pieces together, setting up ORM config, styling, auth, and other repetitive boilerplate, the CLI gives you a working baseline from the stack you choose.&lt;/p&gt;

&lt;p&gt;It currently supports technologies like:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;React&lt;/li&gt;
&lt;li&gt;Next.js&lt;/li&gt;
&lt;li&gt;Vue&lt;/li&gt;
&lt;li&gt;Vite&lt;/li&gt;
&lt;li&gt;Tailwind CSS&lt;/li&gt;
&lt;li&gt;shadcn/ui&lt;/li&gt;
&lt;li&gt;Express&lt;/li&gt;
&lt;li&gt;NestJS&lt;/li&gt;
&lt;li&gt;Prisma&lt;/li&gt;
&lt;li&gt;Mongoose&lt;/li&gt;
&lt;li&gt;Sequelize&lt;/li&gt;
&lt;li&gt;MongoDB&lt;/li&gt;
&lt;li&gt;MySQL&lt;/li&gt;
&lt;li&gt;PostgreSQL&lt;/li&gt;
&lt;li&gt;JWT&lt;/li&gt;
&lt;li&gt;NextAuth&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;And it organizes templates across these project types:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Single App&lt;/li&gt;
&lt;li&gt;Frontend-only&lt;/li&gt;
&lt;li&gt;Backend-only&lt;/li&gt;
&lt;li&gt;Fullstack&lt;/li&gt;
&lt;li&gt;Monorepo&lt;/li&gt;
&lt;li&gt;Turbo Monorepo&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;With &lt;strong&gt;v0.3.0&lt;/strong&gt;, the catalog now supports &lt;strong&gt;36 production-ready starter templates&lt;/strong&gt;.&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.amazonaws.com%2Fuploads%2Farticles%2F41cb552rzyse7iqgg7v2.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.amazonaws.com%2Fuploads%2Farticles%2F41cb552rzyse7iqgg7v2.png" alt=" " width="800" height="1200"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h2&gt;
  
  
  What’s new in v0.3.0
&lt;/h2&gt;

&lt;p&gt;This release focused on three things:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;expanding the template catalog&lt;/li&gt;
&lt;li&gt;cleaning up the CLI internally&lt;/li&gt;
&lt;li&gt;strengthening release quality&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;
  
  
  New starters added
&lt;/h3&gt;

&lt;p&gt;I added several new templates, including:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Fastify + Prisma + PostgreSQL backend starter&lt;/li&gt;
&lt;li&gt;Next.js + TypeScript + shadcn/ui + Tailwind starter&lt;/li&gt;
&lt;li&gt;React/Vite + Prisma + PostgreSQL fullstack starter&lt;/li&gt;
&lt;li&gt;Vue/Vite + Prisma + PostgreSQL fullstack starter&lt;/li&gt;
&lt;li&gt;additional backend, single-app, and fullstack combinations&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;That pushed the catalog to &lt;strong&gt;36 starter templates&lt;/strong&gt;.&lt;/p&gt;


&lt;h2&gt;
  
  
  A cleaner CLI internally
&lt;/h2&gt;

&lt;p&gt;One of the biggest improvements in this release was refactoring the CLI into smaller, more maintainable modules.&lt;/p&gt;

&lt;p&gt;I split the logic into separate parts for things like:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;argument parsing&lt;/li&gt;
&lt;li&gt;catalog discovery&lt;/li&gt;
&lt;li&gt;template matching&lt;/li&gt;
&lt;li&gt;prompts&lt;/li&gt;
&lt;li&gt;scaffolding&lt;/li&gt;
&lt;li&gt;constants and aliases&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This made the codebase easier to understand and easier to extend as the number of templates keeps growing.&lt;/p&gt;

&lt;p&gt;That cleanup mattered a lot, because once a scaffolding tool starts supporting many combinations, the matching and discovery flow can become messy very quickly.&lt;/p&gt;

&lt;p&gt;I wanted to improve that before continuing to add more starters.&lt;/p&gt;


&lt;h2&gt;
  
  
  Stronger validation before release
&lt;/h2&gt;

&lt;p&gt;I also improved the validation and release checks.&lt;/p&gt;

&lt;p&gt;The project now verifies things like:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;referenced bases and layers actually exist&lt;/li&gt;
&lt;li&gt;built template outputs are valid&lt;/li&gt;
&lt;li&gt;expected files are present&lt;/li&gt;
&lt;li&gt;template integrity is preserved before publishing&lt;/li&gt;
&lt;li&gt;release quality is more consistent&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;That gave me much more confidence when shipping updates to npm.&lt;/p&gt;

&lt;p&gt;When a project becomes template-driven and composable, validation stops being optional very quickly.&lt;/p&gt;


&lt;h2&gt;
  
  
  Why I’m building this
&lt;/h2&gt;

&lt;p&gt;I built this because I kept running into the same problem:&lt;/p&gt;

&lt;p&gt;Every time I started a new project, I spent too much time on setup before I could start building the real thing.&lt;/p&gt;

&lt;p&gt;Not just creating folders, but also:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;organizing frontend and backend structure&lt;/li&gt;
&lt;li&gt;connecting stack pieces together&lt;/li&gt;
&lt;li&gt;configuring database tooling&lt;/li&gt;
&lt;li&gt;choosing a maintainable baseline&lt;/li&gt;
&lt;li&gt;reusing and cleaning up old boilerplate&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I wanted a tool that could take simple stack words and give me a serious starting point.&lt;/p&gt;

&lt;p&gt;Not a toy template.&lt;br&gt;
Not an empty folder structure.&lt;br&gt;
A real baseline that someone could actually continue building on.&lt;/p&gt;


&lt;h2&gt;
  
  
  Example usage
&lt;/h2&gt;

&lt;p&gt;Interactive mode:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;npx starter-structure-cli my-app
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Natural stack-token mode:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;npx starter-structure-cli my-app react vite ts tailwind express prisma postgres
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;List available templates:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;npx starter-structure-cli --list
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  What I learned from shipping this
&lt;/h2&gt;

&lt;p&gt;A few things became very clear while working on this release.&lt;/p&gt;

&lt;h3&gt;
  
  
  1. Template quality matters more than template count
&lt;/h3&gt;

&lt;p&gt;Adding more starters is useful, but only if the generated output actually feels worth building on.&lt;/p&gt;

&lt;h3&gt;
  
  
  2. Validation becomes essential very quickly
&lt;/h3&gt;

&lt;p&gt;As soon as templates become composable, release-time checks become necessary.&lt;/p&gt;

&lt;h3&gt;
  
  
  3. Internal cleanup is part of shipping
&lt;/h3&gt;

&lt;p&gt;Adding features is exciting, but maintainability work is what keeps the project from collapsing under growth.&lt;/p&gt;

&lt;h3&gt;
  
  
  4. Real users change how you think
&lt;/h3&gt;

&lt;p&gt;Once people start downloading a package, small details start to matter more:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;naming&lt;/li&gt;
&lt;li&gt;docs&lt;/li&gt;
&lt;li&gt;consistency&lt;/li&gt;
&lt;li&gt;output quality&lt;/li&gt;
&lt;li&gt;error handling&lt;/li&gt;
&lt;li&gt;discoverability&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  What’s next
&lt;/h2&gt;

&lt;p&gt;I want to keep improving:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;template coverage&lt;/li&gt;
&lt;li&gt;matching quality&lt;/li&gt;
&lt;li&gt;documentation&lt;/li&gt;
&lt;li&gt;generated project quality&lt;/li&gt;
&lt;li&gt;the contributor experience for adding new starters&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I also want to make template authoring easier over time so the project can grow in a cleaner way.&lt;/p&gt;




&lt;h2&gt;
  
  
  Try it
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;npx starter-structure-cli my-app
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;GitHub:&lt;/strong&gt; &lt;a href="https://github.com/mohosin2126/starter-structure-cli" rel="noopener noreferrer"&gt;https://github.com/mohosin2126/starter-structure-cli&lt;/a&gt;&lt;br&gt;
&lt;strong&gt;npm:&lt;/strong&gt; &lt;a href="https://www.npmjs.com/package/starter-structure-cli" rel="noopener noreferrer"&gt;https://www.npmjs.com/package/starter-structure-cli&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;If you try it, I’d genuinely love feedback on:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;which stacks you want next&lt;/li&gt;
&lt;li&gt;how useful the generated structure feels&lt;/li&gt;
&lt;li&gt;what would make a tool like this part of your workflow&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>node</category>
      <category>javascript</category>
      <category>cli</category>
      <category>opensource</category>
    </item>
    <item>
      <title>I Built a Tool That Turns GitHub Repositories Into Architecture Dashboards - RepoLens Version 01</title>
      <dc:creator>Md Mohosin Ali Shah</dc:creator>
      <pubDate>Thu, 12 Mar 2026 15:32:08 +0000</pubDate>
      <link>https://dev.to/mohosin2126/i-built-a-tool-that-turns-github-repositories-into-architecture-dashboards-55e3</link>
      <guid>https://dev.to/mohosin2126/i-built-a-tool-that-turns-github-repositories-into-architecture-dashboards-55e3</guid>
      <description>&lt;h2&gt;
  
  
  RepoLens (Version 1) -  Know any repo fast.
&lt;/h2&gt;

&lt;p&gt;Understanding a new codebase is one of the hardest parts of software development.&lt;/p&gt;

&lt;p&gt;You open a repository and immediately face questions like:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;How are modules structured?&lt;/li&gt;
&lt;li&gt;Which components depend on each other?&lt;/li&gt;
&lt;li&gt;What APIs exist?&lt;/li&gt;
&lt;li&gt;What does the architecture actually look like?&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Developers often spend hours reading files just to understand the structure of a project.&lt;/p&gt;

&lt;p&gt;So I built &lt;strong&gt;RepoLens&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;RepoLens Version One&lt;/strong&gt; analyzes GitHub repositories and turns them into a structured architecture dashboard.&lt;/p&gt;

&lt;p&gt;Instead of manually exploring folders, you get:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;module mapping&lt;/li&gt;
&lt;li&gt;dependency graphs&lt;/li&gt;
&lt;li&gt;API endpoint discovery&lt;/li&gt;
&lt;li&gt;generated architecture documentation&lt;/li&gt;
&lt;li&gt;repository chat grounded in code&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Live Platform
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://repolensai.com" rel="noopener noreferrer"&gt;https://repolensai.com&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Open Source Repository
&lt;/h2&gt;

&lt;p&gt;The open-source analysis engine is available here:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/mohosin2126/repolens-community" rel="noopener noreferrer"&gt;https://github.com/mohosin2126/repolens-community&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  What RepoLens V1 Does
&lt;/h2&gt;

&lt;p&gt;RepoLens runs an analysis pipeline that:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Clones the repository&lt;/li&gt;
&lt;li&gt;Scans project files&lt;/li&gt;
&lt;li&gt;Detects languages and frameworks&lt;/li&gt;
&lt;li&gt;Parses modules and dependencies&lt;/li&gt;
&lt;li&gt;Extracts API routes&lt;/li&gt;
&lt;li&gt;Generates architecture summaries&lt;/li&gt;
&lt;li&gt;Generates project overview and onboarding docs&lt;/li&gt;
&lt;li&gt;Persists structured analysis results for each run&lt;/li&gt;
&lt;li&gt;Builds semantic chunks from repository content&lt;/li&gt;
&lt;li&gt;Generates embeddings for retrieval and search&lt;/li&gt;
&lt;li&gt;Grounds repo chat answers in indexed source files&lt;/li&gt;
&lt;li&gt;Tracks branch-specific analysis history&lt;/li&gt;
&lt;li&gt;Compares branches across key architecture signals&lt;/li&gt;
&lt;li&gt;Exports reports and comparison results&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Tech Stack
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Node.js&lt;/li&gt;
&lt;li&gt;Express&lt;/li&gt;
&lt;li&gt;MySQL&lt;/li&gt;
&lt;li&gt;Prisma&lt;/li&gt;
&lt;li&gt;Redis&lt;/li&gt;
&lt;li&gt;BullMQ&lt;/li&gt;
&lt;li&gt;React&lt;/li&gt;
&lt;li&gt;TypeScript&lt;/li&gt;
&lt;li&gt;ts-morph&lt;/li&gt;
&lt;li&gt;React Flow&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Why I Built It
&lt;/h2&gt;

&lt;p&gt;I wanted a tool that helps developers understand large repositories faster.&lt;/p&gt;

&lt;p&gt;Especially when:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;joining a new project&lt;/li&gt;
&lt;li&gt;reviewing unfamiliar codebases&lt;/li&gt;
&lt;li&gt;exploring open source projects&lt;/li&gt;
&lt;li&gt;onboarding new developers&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Open Core
&lt;/h2&gt;

&lt;p&gt;RepoLens follows an open-core model.&lt;/p&gt;

&lt;p&gt;The reusable repository analysis engine is open source, while the hosted platform builds on top of it with the full product experience.&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.amazonaws.com%2Fuploads%2Farticles%2Fdyy2zn2k74xj1dyeg925.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.amazonaws.com%2Fuploads%2Farticles%2Fdyy2zn2k74xj1dyeg925.png" alt="RepoLens screenshot" width="800" height="529"&gt;&lt;/a&gt;&lt;/p&gt;

</description>
      <category>ai</category>
      <category>webdev</category>
      <category>opensource</category>
      <category>productivity</category>
    </item>
    <item>
      <title>I Built a CLI That Scaffolds 30+ Fullstack Projects From Natural Language</title>
      <dc:creator>Md Mohosin Ali Shah</dc:creator>
      <pubDate>Sat, 07 Mar 2026 19:47:24 +0000</pubDate>
      <link>https://dev.to/mohosin2126/i-built-a-cli-that-scaffolds-30-fullstack-projects-from-natural-language-4mbl</link>
      <guid>https://dev.to/mohosin2126/i-built-a-cli-that-scaffolds-30-fullstack-projects-from-natural-language-4mbl</guid>
      <description>&lt;p&gt;Ever typed something like this and wished it just worked?&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npx starter-structure-cli my-app react vite ts tailwind express prisma mysql
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Well, now it does. I built &lt;strong&gt;starter-structure-cli&lt;/strong&gt; an open source Node.js CLI that generates production-ready starter projects from stack tokens you type in plain English.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Problem
&lt;/h2&gt;

&lt;p&gt;Every time I started a new project, I'd spend hours:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Setting up folder structure&lt;/li&gt;
&lt;li&gt;Wiring up the frontend to the backend&lt;/li&gt;
&lt;li&gt;Configuring ORMs, auth, styling&lt;/li&gt;
&lt;li&gt;Copy-pasting boilerplate from old projects&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I wanted one command that just gives me a working project with my exact stack.&lt;/p&gt;

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



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npx starter-structure-cli my-app
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That's it. You get an interactive prompt that walks you through picking your stack. Or skip the prompts entirely:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npx starter-structure-cli my-app react vite ts tailwind express prisma mysql
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It ships with &lt;strong&gt;30+ templates&lt;/strong&gt; across 6 categories:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Category&lt;/th&gt;
&lt;th&gt;Example&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Single App&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;react-vite-ts-tailwind&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Frontend-only&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;react-admin-dashboard&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Backend-only&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;express-prisma-mysql-jwt&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Fullstack&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;react-vite-ts-tailwind-express-prisma-mysql&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Monorepo (Client/Server)&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;nextjs-express-prisma&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Monorepo (Turbo + pnpm)&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;nextjs-api-nestjs-prisma&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Stacks covered: &lt;strong&gt;React, Next.js, Vue, Vite, Tailwind CSS, shadcn/ui, Express, NestJS, Prisma, Mongoose, Sequelize, MongoDB, MySQL, PostgreSQL, JWT, NextAuth&lt;/strong&gt; — and growing.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Cool Parts
&lt;/h2&gt;

&lt;h3&gt;
  
  
  1. Natural Language Matching
&lt;/h3&gt;

&lt;p&gt;You don't need to memorize template names. Just throw stack words at it:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npx starter-structure-cli my-app reactjs tailwind css nodejs prisma mysql
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The CLI normalizes aliases automatically:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;reactjs&lt;/code&gt; → &lt;code&gt;react&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;tailwindcss&lt;/code&gt; → &lt;code&gt;tailwind&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;typescript&lt;/code&gt; → &lt;code&gt;ts&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;next&lt;/code&gt; → &lt;code&gt;nextjs&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;It even ignores filler words like &lt;code&gt;with&lt;/code&gt;, &lt;code&gt;project&lt;/code&gt;, &lt;code&gt;app&lt;/code&gt;, and &lt;code&gt;css&lt;/code&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  2. Composable Template System
&lt;/h3&gt;

&lt;p&gt;Templates aren't just static folders. They're built from &lt;strong&gt;composable layers&lt;/strong&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;template-sources/
  bases/       → shared project skeletons
  layers/      → stack-specific overlays
  presets/     → final template definitions (JSON)
  components/  → reusable snippets (package.json, README, styles)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A preset JSON file defines how layers compose:&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;"output"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"templates/fullstack/react-vite-ts-tailwind-express-prisma-mysql"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"base"&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;"bases/fullstack/react-vite-ts-tailwind-express"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"layers"&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;"layers/backend-only/prisma-mysql"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"variables"&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;"DB"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"mysql"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This means adding a new database variant is just a new 5-line JSON preset — not a whole new template folder.&lt;/p&gt;

&lt;p&gt;The system also supports &lt;code&gt;{{ include: }}&lt;/code&gt; directives with &lt;strong&gt;cycle detection&lt;/strong&gt;, so templates can reference shared snippets without infinite loops.&lt;/p&gt;

&lt;h3&gt;
  
  
  3. Interactive + Non-Interactive
&lt;/h3&gt;

&lt;p&gt;Fully interactive with &lt;code&gt;@clack/prompts&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npx starter-structure-cli my-app
&lt;span class="c"&gt;# → Pick category → Pick stack options → Generate&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Or fully automated for CI/scripts:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npx starter-structure-cli my-app &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--category&lt;/span&gt; fullstack &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--frontend&lt;/span&gt; react &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--backend&lt;/span&gt; express &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--orm&lt;/span&gt; prisma &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--database&lt;/span&gt; mysql &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-y&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  4. Smart Defaults
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;If both JS and TS variants exist, &lt;strong&gt;TypeScript is preferred&lt;/strong&gt; unless you pass &lt;code&gt;--language js&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;__APP_NAME__&lt;/code&gt; placeholders are replaced in file names, folder names, and file contents&lt;/li&gt;
&lt;li&gt;Auto-installs dependencies with &lt;code&gt;--install&lt;/code&gt; (supports npm, pnpm, yarn)&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Generated Project Structures
&lt;/h2&gt;

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

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;my-app/
  client/    → React/Vue/Next.js frontend
  server/    → Express/NestJS backend
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Monorepo (Turbo):&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;my-app/
  apps/
    web/     → frontend
    api/     → backend
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Templates include starter structure for components, routes, hooks, services, controllers, and data modules — not just empty folders.&lt;/p&gt;

&lt;h2&gt;
  
  
  How I Built It
&lt;/h2&gt;

&lt;p&gt;The entire CLI is built with:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Pure Node.js&lt;/strong&gt; (ES Modules, zero build step)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;@clack/prompts&lt;/strong&gt; — beautiful terminal UI&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;picocolors&lt;/strong&gt; — lightweight terminal colors&lt;/li&gt;
&lt;li&gt;A custom &lt;strong&gt;template build pipeline&lt;/strong&gt; that composes presets at publish time&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The architecture is intentionally simple. No frameworks, no bundlers, no compilation. Just Node.js doing what it does best — file system operations and CLI parsing.&lt;/p&gt;

&lt;h2&gt;
  
  
  Try It
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npx starter-structure-cli my-app
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Or browse the templates:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npx starter-structure-cli &lt;span class="nt"&gt;--list&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;GitHub:&lt;/strong&gt; &lt;a href="https://github.com/mohosin2126/starter-structure-cli" rel="noopener noreferrer"&gt;github.com/mohosin2126/starter-structure-cli&lt;/a&gt;&lt;br&gt;
&lt;strong&gt;npm:&lt;/strong&gt; &lt;a href="https://www.npmjs.com/package/starter-structure-cli" rel="noopener noreferrer"&gt;npmjs.com/package/starter-structure-cli&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Star it if you find it useful ⭐  and feel free to open issues or contribute new templates!&lt;/p&gt;

</description>
      <category>javascript</category>
      <category>node</category>
      <category>cli</category>
      <category>opensource</category>
    </item>
    <item>
      <title>What a monorepo needs (the real requirements)</title>
      <dc:creator>Md Mohosin Ali Shah</dc:creator>
      <pubDate>Wed, 25 Feb 2026 15:35:57 +0000</pubDate>
      <link>https://dev.to/mohosin2126/what-a-monorepo-needs-the-real-requirements-1m28</link>
      <guid>https://dev.to/mohosin2126/what-a-monorepo-needs-the-real-requirements-1m28</guid>
      <description>&lt;p&gt;A good monorepo package manager should handle:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Workspaces&lt;/strong&gt; (multiple packages/apps)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Fast installs&lt;/strong&gt; for big repos&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Deterministic lockfile&lt;/strong&gt; (same deps everywhere)&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Good CI caching&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Minimal “dependency weirdness”&lt;/strong&gt; (phantom deps, hoisting surprises)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Good developer workflow&lt;/strong&gt; (run scripts across packages, filtering, etc.)&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  pnpm in monorepos (why it’s usually best)
&lt;/h2&gt;

&lt;h3&gt;
  
  
  1) Speed (especially after first install)
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;pnpm keeps a &lt;strong&gt;global content-addressable store&lt;/strong&gt; (one copy of each package version).&lt;/li&gt;
&lt;li&gt;Each project uses &lt;strong&gt;hardlinks&lt;/strong&gt; into &lt;code&gt;node_modules&lt;/code&gt;.
Result: repeated installs are very fast, and huge monorepos benefit a lot.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  2) Disk usage (big win)
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;With npm, each workspace can duplicate the same packages across subprojects.&lt;/li&gt;
&lt;li&gt;With pnpm, packages are &lt;strong&gt;reused&lt;/strong&gt; from the store.
Result: smaller repo footprint and faster docker/CI layers.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  3) Strict dependency isolation (less “hidden bugs”)
&lt;/h3&gt;

&lt;p&gt;pnpm is strict:&lt;br&gt;
If &lt;code&gt;package-a&lt;/code&gt; doesn’t declare &lt;code&gt;lodash&lt;/code&gt; in its &lt;code&gt;package.json&lt;/code&gt;, it &lt;strong&gt;can’t import it&lt;/strong&gt; just because some other package installed it.&lt;/p&gt;

&lt;p&gt;This prevents the classic monorepo bug:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;“It worked on my machine because it was hoisted… but CI broke later.”&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  4) Workspace tooling feels made for monorepos
&lt;/h3&gt;

&lt;p&gt;pnpm has very ergonomic workspace commands:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;run scripts across all packages&lt;/li&gt;
&lt;li&gt;filter to run on only affected packages&lt;/li&gt;
&lt;li&gt;recursive installs/builds are clean&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  5) CI caching is usually better
&lt;/h3&gt;

&lt;p&gt;Because pnpm’s store is reusable, caching is simpler and more effective.&lt;/p&gt;

&lt;p&gt;In many CI pipelines, pnpm installs become consistently faster after the first cached run.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Best for:&lt;/strong&gt;&lt;br&gt;
New monorepos, medium/large repos, teams that care about correctness and speed, TurboRepo/Nx repos.&lt;/p&gt;

&lt;h2&gt;
  
  
  npm in monorepos (when it’s still a good choice)
&lt;/h2&gt;

&lt;h3&gt;
  
  
  1) Maximum compatibility / minimum friction
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;npm ships with Node by default.&lt;/li&gt;
&lt;li&gt;Some older tools and scripts assume npm-style hoisting behavior.
That makes npm “boring and safe” in some enterprise setups.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  2) Simpler mental model for some teams
&lt;/h3&gt;

&lt;p&gt;If the team already knows npm and the repo is small-ish, npm workspaces can be “good enough.”&lt;/p&gt;

&lt;h3&gt;
  
  
  3) Fewer edge cases with legacy tooling
&lt;/h3&gt;

&lt;p&gt;pnpm’s strictness &lt;em&gt;can&lt;/em&gt; expose issues in older packages that assumed hoisting.&lt;br&gt;
That’s not pnpm’s fault—it’s correctness—but it can create migration work.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Best for:&lt;/strong&gt;&lt;br&gt;
Legacy repos, very strict “no new tooling” policy, or ecosystems with fragile dependencies.&lt;/p&gt;

&lt;h2&gt;
  
  
  Key Differences (quick but important)
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Dependency layout
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;npm&lt;/strong&gt;: often &lt;strong&gt;hoisted&lt;/strong&gt; dependencies (deps can appear available even if not declared)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;pnpm&lt;/strong&gt;: uses a &lt;strong&gt;symlinked structure&lt;/strong&gt; that enforces correctness&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Bug prevention
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;pnpm&lt;/strong&gt; catches missing dependencies earlier&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;npm&lt;/strong&gt; may hide missing dependency problems until later&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Performance
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;pnpm usually wins for bigger repos and repeated installs&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Recommendation (simple decision guide)
&lt;/h2&gt;

&lt;p&gt;Pick &lt;strong&gt;pnpm&lt;/strong&gt; if you want:&lt;br&gt;
fast installs&lt;br&gt;
lower disk usage&lt;br&gt;
fewer “phantom dependency” bugs&lt;br&gt;
best monorepo ergonomics&lt;/p&gt;

&lt;p&gt;Pick &lt;strong&gt;npm&lt;/strong&gt; if you want:&lt;br&gt;
maximum compatibility&lt;br&gt;
simplest onboarding (no extra tool install)&lt;br&gt;
legacy tooling expectations&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;My default in 2026:&lt;/strong&gt; &lt;strong&gt;pnpm&lt;/strong&gt; for almost every new monorepo.&lt;/p&gt;

&lt;h2&gt;
  
  
  Best-practice setup (works great)
&lt;/h2&gt;

&lt;p&gt;If you choose pnpm, do this:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Keep &lt;strong&gt;one lockfile&lt;/strong&gt; at repo root&lt;/li&gt;
&lt;li&gt;Use &lt;strong&gt;pnpm workspaces&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;Add a pinned version for consistency across the team&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Example idea (conceptual):&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;root &lt;code&gt;package.json&lt;/code&gt; with &lt;code&gt;"packageManager": "pnpm@X.Y.Z"&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;code&gt;pnpm-workspace.yaml&lt;/code&gt; listing packages like:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;apps/*&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;packages/*&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;

</description>
      <category>architecture</category>
      <category>javascript</category>
      <category>productivity</category>
      <category>tooling</category>
    </item>
    <item>
      <title>365 Days, 2,010 Contributions: What I Learned from a Year of Zero Missed Days</title>
      <dc:creator>Md Mohosin Ali Shah</dc:creator>
      <pubDate>Sun, 04 Jan 2026 05:30:21 +0000</pubDate>
      <link>https://dev.to/mohosin2126/365-days-2010-contributions-what-i-learned-from-a-year-of-zero-missed-days-15dc</link>
      <guid>https://dev.to/mohosin2126/365-days-2010-contributions-what-i-learned-from-a-year-of-zero-missed-days-15dc</guid>
      <description>&lt;p&gt;In 2025, I made a silent promise to myself: &lt;strong&gt;Show up.&lt;/strong&gt; No matter how I felt, no matter how busy the day was, I committed to pushing my craft forward every single day. Today, looking back at the green squares on my GitHub profile, the numbers tell one story, but the growth tells another.&lt;/p&gt;

&lt;h3&gt;
  
  
  The Stats
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Days Commited:&lt;/strong&gt; 365 / 365&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Total Contributions:&lt;/strong&gt; 2,010&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Days Missed:&lt;/strong&gt; 0&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;GitHub Handle:&lt;/strong&gt; &lt;a href="https://github.com/mohosin2126" rel="noopener noreferrer"&gt;@mohosin2126&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Beyond the Green Squares
&lt;/h3&gt;

&lt;p&gt;While the GitHub contribution graph is a great motivator, it only scratches the surface. In the beginning, I was focused on the "streak." But as the months progressed, the focus shifted from &lt;em&gt;quantity&lt;/em&gt; to &lt;em&gt;quality&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;I realized that 2,010 contributions aren't just lines of code; they are 2,010 moments of problem-solving, debugging, and architectural decision-making.&lt;/p&gt;

&lt;h3&gt;
  
  
  The Projects That Defined My Year
&lt;/h3&gt;

&lt;p&gt;My year was largely defined by three major pillars:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;@codethinker (Primary Focus):&lt;/strong&gt; This has been my main driver. Building and scaling this project required me to move beyond basic implementation and think deeply about user experience and system reliability.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;@orbillo &amp;amp; @perkoxofficial:&lt;/strong&gt; Contributing to these organizations allowed me to collaborate and understand different codebases, which is essential for any developer looking to grow.&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  What I Learned When I Refused to Take a Day Off
&lt;/h3&gt;

&lt;h4&gt;
  
  
  1. Logic Over Syntax
&lt;/h4&gt;

&lt;p&gt;When you code every day, syntax becomes second nature. This frees up your brain to focus on the truly hard parts: &lt;strong&gt;logic and architecture.&lt;/strong&gt; I found myself spending more time planning my approach and less time looking up documentation for basic functions.&lt;/p&gt;

&lt;h4&gt;
  
  
  2. The Power of "Non-Zero" Days
&lt;/h4&gt;

&lt;p&gt;There were days when I didn't feel like a "rockstar." There were days when I was tired or stuck. But I learned the value of the "Non-Zero Day." Even a small refactor or a documentation update keeps the momentum alive. Consistency beats intensity every single time.&lt;/p&gt;

&lt;h4&gt;
  
  
  3. Architecture is a Mindset
&lt;/h4&gt;

&lt;p&gt;By maintaining a year-long streak, I was forced to live with my own code for 365 days straight. Nothing teaches you the importance of clean, maintainable architecture quite like having to fix a bug in code you wrote six months ago during a "daily streak" rush.&lt;/p&gt;

&lt;h3&gt;
  
  
  Looking Ahead
&lt;/h3&gt;

&lt;p&gt;If 2025 was about &lt;strong&gt;discipline&lt;/strong&gt;, 2026 will be about &lt;strong&gt;depth&lt;/strong&gt;. I want to take the consistency I’ve built and apply it to even more complex architectural challenges and open-source contributions.&lt;/p&gt;

&lt;p&gt;To anyone thinking about starting their own streak: &lt;strong&gt;Don't wait for the perfect moment.&lt;/strong&gt; Just start. The growth happens in the struggle, not the success.&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;Let’s Connect!&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;GitHub:&lt;/strong&gt; &lt;a href="https://github.com/mohosin2126" rel="noopener noreferrer"&gt;mohosin2126&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Projects I'm working on:&lt;/strong&gt; @codethinker, @orbillo, @perkoxofficial&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;em&gt;How do you stay consistent in your coding journey? I’d love to hear your thoughts in the comments!&lt;/em&gt;&lt;/p&gt;




</description>
      <category>webdev</category>
      <category>programming</category>
      <category>productivity</category>
      <category>career</category>
    </item>
    <item>
      <title>Primary Key vs Foreign Key: A Complete Guide for Developers</title>
      <dc:creator>Md Mohosin Ali Shah</dc:creator>
      <pubDate>Mon, 29 Sep 2025 16:21:09 +0000</pubDate>
      <link>https://dev.to/mohosin2126/primary-key-vs-foreign-key-a-complete-guide-for-developers-fle</link>
      <guid>https://dev.to/mohosin2126/primary-key-vs-foreign-key-a-complete-guide-for-developers-fle</guid>
      <description>&lt;p&gt;Relational databases power everything from small web apps to enterprise-grade systems. At the heart of relational database design are &lt;strong&gt;Primary Keys&lt;/strong&gt; and &lt;strong&gt;Foreign Keys&lt;/strong&gt;. These concepts define how data is uniquely identified and how different tables connect to each other. Whether you are designing your first database or optimizing a production system, understanding these keys is essential.&lt;/p&gt;

&lt;h2&gt;
  
  
  What is a Primary Key
&lt;/h2&gt;

&lt;p&gt;A &lt;strong&gt;Primary Key&lt;/strong&gt; is a column or a group of columns in a database table that uniquely identifies each record in that table.&lt;br&gt;
It acts like a unique ID card for every row, ensuring there is no ambiguity when retrieving, updating, or deleting data.&lt;/p&gt;

&lt;h3&gt;
  
  
  Key Characteristics of a Primary Key
&lt;/h3&gt;

&lt;p&gt;• &lt;strong&gt;Uniqueness&lt;/strong&gt; Every row must have a unique primary key value.&lt;br&gt;
• &lt;strong&gt;Non Null&lt;/strong&gt; Primary keys cannot contain &lt;code&gt;NULL&lt;/code&gt; values.&lt;br&gt;
• &lt;strong&gt;Single Definition&lt;/strong&gt; Each table can have only one primary key but it may consist of multiple columns which is known as a composite key.&lt;br&gt;
• &lt;strong&gt;Immutable&lt;/strong&gt; The value of a primary key should rarely change because it serves as the permanent identifier of the row.&lt;/p&gt;

&lt;h3&gt;
  
  
  Example of a Primary Key
&lt;/h3&gt;

&lt;p&gt;Consider a &lt;code&gt;Users&lt;/code&gt; table&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;user_id (Primary Key)&lt;/th&gt;
&lt;th&gt;name&lt;/th&gt;
&lt;th&gt;email&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;Alice&lt;/td&gt;
&lt;td&gt;&lt;a href="mailto:alice@email.com"&gt;alice@email.com&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;td&gt;Bob&lt;/td&gt;
&lt;td&gt;&lt;a href="mailto:bob@email.com"&gt;bob@email.com&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;3&lt;/td&gt;
&lt;td&gt;Carol&lt;/td&gt;
&lt;td&gt;&lt;a href="mailto:carol@email.com"&gt;carol@email.com&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Here &lt;code&gt;user_id&lt;/code&gt; is the primary key. Even if two users share the same name or email domain their &lt;code&gt;user_id&lt;/code&gt; remains unique.&lt;/p&gt;

&lt;h2&gt;
  
  
  What is a Foreign Key
&lt;/h2&gt;

&lt;p&gt;A &lt;strong&gt;Foreign Key&lt;/strong&gt; is a column or a group of columns in one table that references the primary key of another table.&lt;br&gt;
Its purpose is to enforce &lt;strong&gt;referential integrity&lt;/strong&gt; making sure that relationships between tables remain valid.&lt;/p&gt;

&lt;h3&gt;
  
  
  Key Characteristics of a Foreign Key
&lt;/h3&gt;

&lt;p&gt;• &lt;strong&gt;Reference&lt;/strong&gt; Points to the primary key of another parent table.&lt;br&gt;
• &lt;strong&gt;Duplicates Allowed&lt;/strong&gt; Multiple rows in the child table can reference the same parent record.&lt;br&gt;
• &lt;strong&gt;Optional Relationship&lt;/strong&gt; Can allow &lt;code&gt;NULL&lt;/code&gt; values if the relationship is not mandatory.&lt;br&gt;
• &lt;strong&gt;Multiple Allowed&lt;/strong&gt; A table can have several foreign keys pointing to different parent tables.&lt;/p&gt;

&lt;h3&gt;
  
  
  Example of a Foreign Key
&lt;/h3&gt;

&lt;p&gt;Consider an &lt;code&gt;Orders&lt;/code&gt; table&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;order_id&lt;/th&gt;
&lt;th&gt;user_id (Foreign Key)&lt;/th&gt;
&lt;th&gt;product&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;101&lt;/td&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;Laptop&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;102&lt;/td&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;td&gt;Keyboard&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;103&lt;/td&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;Mouse&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Here the &lt;code&gt;user_id&lt;/code&gt; column in the &lt;code&gt;Orders&lt;/code&gt; table is a foreign key that references the &lt;code&gt;user_id&lt;/code&gt; column in the &lt;code&gt;Users&lt;/code&gt; table.&lt;br&gt;
This ensures that every order is linked to a valid user. If you try to insert an order with a &lt;code&gt;user_id&lt;/code&gt; that does not exist in the &lt;code&gt;Users&lt;/code&gt; table the database will reject it.&lt;/p&gt;

&lt;h2&gt;
  
  
  Primary Key vs Foreign Key Side by Side Comparison
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Feature&lt;/th&gt;
&lt;th&gt;Primary Key&lt;/th&gt;
&lt;th&gt;Foreign Key&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Purpose&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Uniquely identifies each record in a table&lt;/td&gt;
&lt;td&gt;Creates a relationship between two tables&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Uniqueness&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Must be unique across all rows&lt;/td&gt;
&lt;td&gt;Can have duplicate values&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Nullability&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Cannot contain &lt;code&gt;NULL&lt;/code&gt; values&lt;/td&gt;
&lt;td&gt;Can contain &lt;code&gt;NULL&lt;/code&gt; values if the relationship is optional&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Number per Table&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Only one primary key is allowed&lt;/td&gt;
&lt;td&gt;A table can have multiple foreign keys&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Reference&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Exists within its own table&lt;/td&gt;
&lt;td&gt;References the primary key of another table&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Integrity Role&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Guarantees uniqueness within the table itself&lt;/td&gt;
&lt;td&gt;Ensures valid links between child and parent tables&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;This comparison shows that while both keys are used to maintain order and integrity their roles are different.&lt;br&gt;
The primary key focuses on identifying rows within a single table while the foreign key focuses on linking one table to another.&lt;/p&gt;

&lt;h2&gt;
  
  
  Real World Analogy
&lt;/h2&gt;

&lt;p&gt;Think of a &lt;strong&gt;Primary Key&lt;/strong&gt; as a &lt;strong&gt;passport number&lt;/strong&gt;. Each passport number is unique to one person and cannot be duplicated.&lt;br&gt;
A &lt;strong&gt;Foreign Key&lt;/strong&gt; is like a &lt;strong&gt;visa stamp&lt;/strong&gt; inside the passport. It connects your identity to another country’s system.&lt;br&gt;
A visa is only valid if the passport exists just as a foreign key is only valid if the corresponding primary key exists.&lt;/p&gt;




&lt;h2&gt;
  
  
  Best Practices
&lt;/h2&gt;

&lt;p&gt;To build strong and scalable databases keep these best practices in mind&lt;/p&gt;

&lt;p&gt;• &lt;strong&gt;Use Simple and Stable Keys&lt;/strong&gt; Choose primary keys that are simple and unlikely to change such as an auto incrementing integer.&lt;br&gt;
• &lt;strong&gt;Avoid Changing Primary Key Values&lt;/strong&gt; Because other tables may rely on them changing a primary key can cause cascading issues.&lt;br&gt;
• &lt;strong&gt;Index Foreign Keys&lt;/strong&gt; Adding indexes on foreign key columns can significantly improve join performance.&lt;br&gt;
• &lt;strong&gt;Enforce Referential Integrity&lt;/strong&gt; Use database constraints &lt;code&gt;ON DELETE&lt;/code&gt; and &lt;code&gt;ON UPDATE&lt;/code&gt; to control how changes in the parent table affect child tables.&lt;br&gt;
• &lt;strong&gt;Avoid Using Business Data as Keys&lt;/strong&gt; Columns like email or phone numbers can change and should not be primary keys.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why These Keys Matter
&lt;/h2&gt;

&lt;p&gt;Without a primary key it would be impossible to uniquely identify records making operations like updates or deletions unreliable.&lt;br&gt;
Without a foreign key relationships between tables would be fragile allowing invalid or orphaned records to exist.&lt;br&gt;
Together primary and foreign keys create a structured dependable and maintainable database design.&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;Primary keys and foreign keys are not just database jargon. They are fundamental to creating relational databases that are efficient scalable and consistent.&lt;br&gt;
The primary key ensures that each row in a table is unique while the foreign key maintains valid connections between tables.&lt;br&gt;
Mastering these concepts will help you design systems that handle growth gracefully and maintain data integrity in the real world.&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>database</category>
      <category>programming</category>
      <category>beginners</category>
    </item>
    <item>
      <title>GitHub on Windows: The Complete Guide to SSH, GitHub Desktop, and No Keygen Commits</title>
      <dc:creator>Md Mohosin Ali Shah</dc:creator>
      <pubDate>Wed, 13 Aug 2025 18:08:16 +0000</pubDate>
      <link>https://dev.to/mohosin2126/github-on-windows-the-complete-guide-to-ssh-github-desktop-and-no-keygen-commits-4i9g</link>
      <guid>https://dev.to/mohosin2126/github-on-windows-the-complete-guide-to-ssh-github-desktop-and-no-keygen-commits-4i9g</guid>
      <description>&lt;p&gt;Working with GitHub on Windows can be painless and secure if you set it up the right way.&lt;br&gt;
In this article, you will learn three complete workflows&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;SSH from Command Prompt or PowerShell for passwordless, secure pushes and pulls&lt;/li&gt;
&lt;li&gt;GitHub Desktop the GUI option no SSH setup required&lt;/li&gt;
&lt;li&gt;No keygen methods working over HTTPS using a Personal Access Token or the GitHub web editor&lt;/li&gt;
&lt;/ol&gt;
&lt;h2&gt;
  
  
  Why SSH vs HTTPS on Windows
&lt;/h2&gt;

&lt;p&gt;SSH uses a key pair stored on your machine. No password or token prompts once configured.&lt;br&gt;
HTTPS uses your GitHub login or Personal Access Token PAT. Works out of the box with GitHub Desktop.&lt;br&gt;
Web editor lets you make commits right in your browser with no local setup needed.&lt;/p&gt;
&lt;h2&gt;
  
  
  SSH from Windows CMD or PowerShell
&lt;/h2&gt;
&lt;h3&gt;
  
  
  Step 1 Check for existing SSH keys
&lt;/h3&gt;


&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight batchfile"&gt;&lt;code&gt;&lt;span class="nb"&gt;dir&lt;/span&gt; &lt;span class="nv"&gt;%USERPROFILE%&lt;/span&gt;\.ssh
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;Look for&lt;br&gt;
&lt;code&gt;id_ed25519&lt;/code&gt; private&lt;br&gt;
&lt;code&gt;id_ed25519.pub&lt;/code&gt; public&lt;/p&gt;

&lt;p&gt;If they exist and you want to reuse them, skip to Step 4.&lt;/p&gt;
&lt;h3&gt;
  
  
  Step 2 Create a new SSH key
&lt;/h3&gt;


&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight batchfile"&gt;&lt;code&gt;&lt;span class="kd"&gt;ssh&lt;/span&gt;&lt;span class="na"&gt;-keygen -t &lt;/span&gt;&lt;span class="kd"&gt;ed25519&lt;/span&gt; &lt;span class="na"&gt;-C &lt;/span&gt;&lt;span class="s2"&gt;"you@example.com"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;Press Enter to accept default path&lt;br&gt;
&lt;code&gt;%USERPROFILE%\.ssh\id_ed25519&lt;/code&gt;&lt;br&gt;
Optionally set a passphrase.&lt;/p&gt;

&lt;p&gt;If ed25519 is not supported&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight batchfile"&gt;&lt;code&gt;&lt;span class="kd"&gt;ssh&lt;/span&gt;&lt;span class="na"&gt;-keygen -t &lt;/span&gt;&lt;span class="kd"&gt;rsa&lt;/span&gt; &lt;span class="na"&gt;-b &lt;/span&gt;&lt;span class="m"&gt;4096&lt;/span&gt; &lt;span class="na"&gt;-C &lt;/span&gt;&lt;span class="s2"&gt;"you@example.com"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Step 3 Start the SSH agent and load your key
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight powershell"&gt;&lt;code&gt;&lt;span class="n"&gt;Get-Service&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;ssh-agent&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Set-Service&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;-StartupType&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;Automatic&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="n"&gt;Start-Service&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;ssh-agent&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="n"&gt;ssh-add&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;$&lt;/span&gt;&lt;span class="nn"&gt;env&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="nv"&gt;USERPROFILE&lt;/span&gt;&lt;span class="nx"&gt;\.ssh\id_ed25519&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Step 4 Copy your public key
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight powershell"&gt;&lt;code&gt;&lt;span class="n"&gt;Get-Content&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;$&lt;/span&gt;&lt;span class="nn"&gt;env&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="nv"&gt;USERPROFILE&lt;/span&gt;&lt;span class="nx"&gt;\.ssh\id_ed25519.pub&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Set-Clipboard&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Step 5 Add the key to GitHub
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;Go to Settings then SSH and GPG keys then New SSH key&lt;/li&gt;
&lt;li&gt;Title it for example Windows Laptop&lt;/li&gt;
&lt;li&gt;Paste your key&lt;/li&gt;
&lt;li&gt;Save&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  Step 6 Test your connection
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight batchfile"&gt;&lt;code&gt;&lt;span class="kd"&gt;ssh&lt;/span&gt; &lt;span class="na"&gt;-T &lt;/span&gt;&lt;span class="kd"&gt;git&lt;/span&gt;@github.com
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If successful you will see&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Hi &amp;lt;username&amp;gt; You have successfully authenticated...
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Step 7 Use SSH in Git
&lt;/h3&gt;

&lt;p&gt;Clone&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight batchfile"&gt;&lt;code&gt;&lt;span class="kd"&gt;git&lt;/span&gt; &lt;span class="kd"&gt;clone&lt;/span&gt; &lt;span class="kd"&gt;git&lt;/span&gt;@github.com:OWNER/REPO.git
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Switch existing repo from HTTPS to SSH&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight batchfile"&gt;&lt;code&gt;&lt;span class="kd"&gt;git&lt;/span&gt; &lt;span class="kd"&gt;remote&lt;/span&gt; &lt;span class="kd"&gt;set&lt;/span&gt;&lt;span class="na"&gt;-url &lt;/span&gt;&lt;span class="kd"&gt;origin&lt;/span&gt; &lt;span class="kd"&gt;git&lt;/span&gt;@github.com:OWNER/REPO.git
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Step 8 Commit and push
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight batchfile"&gt;&lt;code&gt;&lt;span class="kd"&gt;git&lt;/span&gt; &lt;span class="kd"&gt;add&lt;/span&gt; .
&lt;span class="kd"&gt;git&lt;/span&gt; &lt;span class="kd"&gt;commit&lt;/span&gt; &lt;span class="na"&gt;-m &lt;/span&gt;&lt;span class="s2"&gt;"feat: first SSH commit from Windows"&lt;/span&gt;
&lt;span class="kd"&gt;git&lt;/span&gt; &lt;span class="kd"&gt;push&lt;/span&gt; &lt;span class="kd"&gt;origin&lt;/span&gt; &lt;span class="kd"&gt;main&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  GitHub Desktop The GUI Method No SSH Needed
&lt;/h2&gt;

&lt;p&gt;GitHub Desktop uses HTTPS by default and stores credentials securely. Perfect if you want to skip SSH.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 1 Install and sign in
&lt;/h3&gt;

&lt;p&gt;Download from desktop.github.com&lt;br&gt;
Sign in with your GitHub account.&lt;/p&gt;
&lt;h3&gt;
  
  
  Step 2 Add a repository
&lt;/h3&gt;

&lt;p&gt;Clone from GitHub File → Clone Repository&lt;br&gt;
Add existing project File → Add Local Repository&lt;/p&gt;
&lt;h3&gt;
  
  
  Step 3 Ensure HTTPS remote
&lt;/h3&gt;

&lt;p&gt;Repository → Repository Settings&lt;br&gt;
Ensure URL is &lt;code&gt;https://github.com/...&lt;/code&gt;&lt;/p&gt;
&lt;h3&gt;
  
  
  Step 4 Commit changes
&lt;/h3&gt;

&lt;p&gt;Edit files in your project folder.&lt;br&gt;
In Desktop, select changed files, write a commit message, click Commit to branch.&lt;/p&gt;
&lt;h3&gt;
  
  
  Step 5 Push to GitHub
&lt;/h3&gt;

&lt;p&gt;Click Push origin.&lt;br&gt;
To create a PR, click Create Pull Request.&lt;/p&gt;
&lt;h2&gt;
  
  
  No Keygen Options
&lt;/h2&gt;
&lt;h3&gt;
  
  
  Option 1 HTTPS with Personal Access Token
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;Create a fine grained PAT in GitHub Settings → Developer settings → Personal Access Tokens&lt;/li&gt;
&lt;li&gt;Switch remote to HTTPS
&lt;/li&gt;
&lt;/ol&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight batchfile"&gt;&lt;code&gt;&lt;span class="kd"&gt;git&lt;/span&gt; &lt;span class="kd"&gt;remote&lt;/span&gt; &lt;span class="kd"&gt;set&lt;/span&gt;&lt;span class="na"&gt;-url &lt;/span&gt;&lt;span class="kd"&gt;origin&lt;/span&gt; &lt;span class="kd"&gt;https&lt;/span&gt;://github.com/OWNER/REPO.git
&lt;span class="kd"&gt;git&lt;/span&gt; &lt;span class="kd"&gt;push&lt;/span&gt; &lt;span class="kd"&gt;origin&lt;/span&gt; &lt;span class="kd"&gt;main&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;ol&gt;
&lt;li&gt;Use your GitHub username and PAT when prompted.&lt;/li&gt;
&lt;/ol&gt;
&lt;h3&gt;
  
  
  Option 2 GitHub Web Commits
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;Open repo on github.com&lt;/li&gt;
&lt;li&gt;Add file → Create new file or edit existing&lt;/li&gt;
&lt;li&gt;Write changes and commit directly to main or via new branch and PR&lt;/li&gt;
&lt;/ol&gt;
&lt;h2&gt;
  
  
  Troubleshooting on Windows
&lt;/h2&gt;

&lt;p&gt;Permission denied publickey&lt;br&gt;
Check if your key is loaded&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight powershell"&gt;&lt;code&gt;&lt;span class="n"&gt;ssh-add&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;-l&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If none re-add&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight powershell"&gt;&lt;code&gt;&lt;span class="n"&gt;ssh-add&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;$&lt;/span&gt;&lt;span class="nn"&gt;env&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="nv"&gt;USERPROFILE&lt;/span&gt;&lt;span class="nx"&gt;\.ssh\id_ed25519&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Verify your public key is in GitHub settings.&lt;/p&gt;

&lt;p&gt;GitHub Desktop cannot push&lt;br&gt;
Switch to HTTPS in Repository Settings.&lt;/p&gt;

&lt;h2&gt;
  
  
  Quick SSH Cheat Sheet Windows
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight powershell"&gt;&lt;code&gt;&lt;span class="n"&gt;ssh-keygen&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;-t&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;ed25519&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;-C&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"you@example.com"&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="n"&gt;Get-Service&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;ssh-agent&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Set-Service&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;-StartupType&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;Automatic&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="n"&gt;Start-Service&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;ssh-agent&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="n"&gt;ssh-add&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;$&lt;/span&gt;&lt;span class="nn"&gt;env&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="nv"&gt;USERPROFILE&lt;/span&gt;&lt;span class="nx"&gt;\.ssh\id_ed25519&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="n"&gt;Get-Content&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;$&lt;/span&gt;&lt;span class="nn"&gt;env&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="nv"&gt;USERPROFILE&lt;/span&gt;&lt;span class="nx"&gt;\.ssh\id_ed25519.pub&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Set-Clipboard&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="nx"&gt;ssh&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;-T&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;git&lt;/span&gt;&lt;span class="err"&gt;@&lt;/span&gt;&lt;span class="nx"&gt;github.com&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="n"&gt;git&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;add&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="n"&gt;git&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;commit&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;-m&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"message"&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="n"&gt;git&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;push&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;origin&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;main&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;SSH is best for developers who want secure passwordless Git operations.&lt;br&gt;
GitHub Desktop is ideal for a quick GUI based workflow.&lt;br&gt;
HTTPS with PAT or Web Commits let you skip SSH entirely.&lt;/p&gt;

&lt;p&gt;Choose the method that fits your workflow and you will never dread pushing to GitHub on Windows again.&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>programming</category>
      <category>github</category>
      <category>beginners</category>
    </item>
    <item>
      <title>Build High-Performance Websites with React.js, Next.js, Vue.js and Tailwind CSS</title>
      <dc:creator>Md Mohosin Ali Shah</dc:creator>
      <pubDate>Sat, 19 Jul 2025 18:10:50 +0000</pubDate>
      <link>https://dev.to/mohosin2126/build-high-performance-websites-with-reactjs-nextjs-vuejs-and-tailwind-css-6e9</link>
      <guid>https://dev.to/mohosin2126/build-high-performance-websites-with-reactjs-nextjs-vuejs-and-tailwind-css-6e9</guid>
      <description>&lt;p&gt;Hello Dev Community,&lt;/p&gt;

&lt;p&gt;I'm &lt;strong&gt;Md Mohosin Ali&lt;/strong&gt;, a Full Stack Developer based in Bangladesh, specializing in &lt;strong&gt;React.js, Next.js, Vue.js&lt;/strong&gt;, and &lt;strong&gt;Tailwind CSS&lt;/strong&gt;. With over two years of professional experience, I help startups, designers, and businesses transform their design files (Figma, PSD, Adobe XD) into fully responsive, high-performance websites and web applications.&lt;/p&gt;

&lt;h2&gt;
  
  
  Services I Provide
&lt;/h2&gt;

&lt;p&gt;I offer complete frontend and backend development services, with a focus on performance, scalability, and pixel-perfect precision.&lt;/p&gt;

&lt;h3&gt;
  
  
  Design to Code Conversion
&lt;/h3&gt;

&lt;p&gt;Transform Figma, PSD, or XD designs into dynamic web applications using modern frameworks:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Semantic HTML5&lt;/li&gt;
&lt;li&gt;Optimized performance with Next.js and Vue.js&lt;/li&gt;
&lt;li&gt;Tailwind CSS for responsive, mobile-first design&lt;/li&gt;
&lt;li&gt;SEO best practices included&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Gig Link:&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
&lt;a href="https://www.fiverr.com/mohosin2126/convert-figma-psd-xd-to-responsive-reactjs-nextjs-website-with-tailwind-css" rel="noopener noreferrer"&gt;Convert Figma, PSD, or XD to React/Next.js&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Custom Landing Page Development
&lt;/h3&gt;

&lt;p&gt;Develop clean and modern landing pages optimized for conversions. Built with:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Next.js / React.js / Vue.js&lt;/li&gt;
&lt;li&gt;Tailwind CSS&lt;/li&gt;
&lt;li&gt;Responsive structure and reusable components&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Gig Link:&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
&lt;a href="http://fiverr.com/mohosin2126/design-a-custom-nextjs-and-react-landing-page-with-tailwind-css" rel="noopener noreferrer"&gt;Design a Custom React/Next.js Landing Page&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Full Website Development
&lt;/h3&gt;

&lt;p&gt;Get scalable websites or MVPs tailored for your startup or business:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Multi-page websites or SPAs&lt;/li&gt;
&lt;li&gt;Clean, modular code&lt;/li&gt;
&lt;li&gt;API integration and state management&lt;/li&gt;
&lt;li&gt;Production-ready deployment setup&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Gig Link:&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
&lt;a href="https://www.fiverr.com/mohosin2126/build-modern-responsive-websites-with-reactjs-nextjs-tailwind-css" rel="noopener noreferrer"&gt;Build Responsive Websites with React/Next.js&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  API Integration &amp;amp; Backend Logic
&lt;/h3&gt;

&lt;p&gt;Connect your frontend to a secure, scalable backend with RESTful or GraphQL APIs:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Node.js + Express.js with MongoDB&lt;/li&gt;
&lt;li&gt;JWT or OAuth2 authentication&lt;/li&gt;
&lt;li&gt;Secure data handling, efficient queries, and well-documented endpoints&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Gig Link:&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
&lt;a href="https://www.fiverr.com/mohosin2126/integrate-rest-api-or-graphql-into-your-nextjs-or-reactjs-website" rel="noopener noreferrer"&gt;Integrate REST API or GraphQL into Your Web App&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Tech Stack
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Frontend&lt;/strong&gt;: React.js, Next.js, Vue.js, Tailwind CSS, Material UI, Shadcn UI&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Backend&lt;/strong&gt;: Node.js, Express.js, MongoDB, Prisma ORM, Firebase&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;State Management&lt;/strong&gt;: Redux, Context API, TanStack Query&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;API&lt;/strong&gt;: REST, GraphQL, Axios, Apollo Client&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Dev Tools&lt;/strong&gt;: Git, Vercel, Postman, JWT, Role-Based Access&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Recent Projects
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Career Props – Job Portal&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
A scalable HR platform with real-time updates, advanced search, and user roles.&lt;br&gt;&lt;br&gt;
Live: &lt;a href="https://career-props.vercel.app" rel="noopener noreferrer"&gt;https://career-props.vercel.app&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Other notable projects:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Addition (E-Commerce for Influencers)&lt;/li&gt;
&lt;li&gt;Truthful Taxes (Financial Services)&lt;/li&gt;
&lt;li&gt;HireMaster (Job Portal)&lt;/li&gt;
&lt;li&gt;NestEgg (Financial Property Dashboard)&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Why Work With Me?
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Professional and scalable codebase&lt;/li&gt;
&lt;li&gt;Responsive, mobile-friendly design&lt;/li&gt;
&lt;li&gt;SEO optimization and fast loading times&lt;/li&gt;
&lt;li&gt;Clean and maintainable architecture&lt;/li&gt;
&lt;li&gt;Unlimited revisions on standard and premium packages&lt;/li&gt;
&lt;li&gt;Clear documentation and post-delivery support&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Get in Touch
&lt;/h2&gt;

&lt;p&gt;If you're looking for a developer who values quality, speed, and clean code, I invite you to explore my Fiverr profile and contact me for collaboration.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;My Fiverr Profile:&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
&lt;a href="https://www.fiverr.com/mohosin2126" rel="noopener noreferrer"&gt;https://www.fiverr.com/mohosin2126&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Let’s build something exceptional—custom-coded and performance-optimized.&lt;/p&gt;

</description>
      <category>react</category>
      <category>nextjs</category>
      <category>tailwindcss</category>
      <category>freelance</category>
    </item>
  </channel>
</rss>
