<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0"><channel><title><![CDATA[Viraj Nikam | Blogs]]></title><description><![CDATA[Viraj Nikam | Blogs]]></description><link>https://blog.virajnikam.in</link><generator>RSS for Node</generator><lastBuildDate>Fri, 16 Oct 2026 15:29:24 GMT</lastBuildDate><atom:link href="https://blog.virajnikam.in/rss.xml" rel="self" type="application/rss+xml"/><language><![CDATA[en]]></language><ttl>60</ttl><item><title><![CDATA[Guide to Auth Tokens]]></title><description><![CDATA[1.1 Why do we need tokens at all?
HTTP is stateless. Every request arrives with no memory of the one before it, so the server has no idea that the person calling /api/orders is the same one who logged]]></description><link>https://blog.virajnikam.in/guide-to-auth-tokens</link><guid isPermaLink="true">https://blog.virajnikam.in/guide-to-auth-tokens</guid><dc:creator><![CDATA[Viraj Nikam]]></dc:creator><pubDate>Wed, 14 Oct 2026 12:07:09 GMT</pubDate><content:encoded><![CDATA[<h2>1.1 Why do we need tokens at all?</h2>
<p>HTTP is stateless. Every request arrives with no memory of the one before it, so the server has no idea that the person calling <code>/api/orders</code> is the same one who logged in a minute ago.</p>
<p>The most obvious fix is to send the email and password with every request. It works, but it causes real problems:</p>
<ul>
<li><p><strong>The password travels everywhere.</strong> Every call carries your most sensitive secret, so every proxy, log line and browser extension becomes a place it can leak from.</p>
</li>
<li><p><strong>Every request pays the password cost.</strong> Passwords are stored with deliberately slow hashes (bcrypt, argon2). Verifying one on every API call burns CPU and slows everything down.</p>
</li>
<li><p><strong>No expiry.</strong> A leaked password works until the user changes it.</p>
</li>
<li><p><strong>No revoking one device.</strong> To log out a stolen phone, you'd have to change the password for every device.</p>
</li>
<li><p><strong>No limits.</strong> The password is all-powerful. You can't hand a client "read-only access for 15 minutes."</p>
</li>
</ul>
<p>So the goal is to send the password once, then carry something else that proves we already logged in. There are two common ways to do that:</p>
<table>
<thead>
<tr>
<th>Approach</th>
<th>How it works</th>
<th>Trade-off</th>
</tr>
</thead>
<tbody><tr>
<td>Server-side session</td>
<td>Server stores the session and gives the client an ID</td>
<td>The server must keep session state, and every server behind your load balancer needs access to the same store</td>
</tr>
<tr>
<td>Token (e.g. JWT)</td>
<td>Server signs a small piece of data and gives it to the client</td>
<td>Stateless and easy to use across services and mobile apps, but harder to take back</td>
</tr>
</tbody></table>
<p>Neither is wrong. Tokens are the usual choice for APIs, mobile apps and multi-service setups, and that's what this guide builds.</p>
<h2>1.2 The access token</h2>
<p>After a successful login, the server signs a small piece of data and hands it back. That's a JWT.</p>
<pre><code class="language-js">import jwt from "jsonwebtoken";

app.post("/api/login", async (req, res) =&gt; {
  const user = await verifyCredentials(req.body);
  if (!user) return res.status(401).send("Invalid credentials");

  const accessToken = jwt.sign(
    { sub: user.publicId, role: user.role },
    process.env.ACCESS_SECRET,
    { expiresIn: "15m" }
  );
  res.json({ accessToken });
});
</code></pre>
<p>The client sends it as <code>Authorization: Bearer &lt;token&gt;</code>, and the server checks the signature:</p>
<pre><code class="language-js">function requireAuth(req, res, next) {
  const token = req.headers.authorization?.split(" ")[1];
  if (!token) return res.sendStatus(401);
  try {
    req.user = jwt.verify(token, process.env.ACCESS_SECRET);
    next();
  } catch {
    res.sendStatus(401); // expired or tampered: client should refresh or re-login
  }
}
</code></pre>
<p>The server stores nothing. It only checks that its own signature is intact. That's the appeal of stateless auth, and also the catch: <strong>you can't take a token back.</strong> If someone steals it, it works until it expires.</p>
<h2>1.3 So make it short-lived</h2>
<p>That's why the token above expires in 15 minutes. But now users must log in every 15 minutes, which nobody will tolerate.</p>
<h2>1.4 The refresh token</h2>
<p>Add a second, long-lived token whose only job is to get new access tokens. It never calls your API directly.</p>
<pre><code class="language-js">const refreshToken = jwt.sign(
  { sub: user.publicId },
  process.env.REFRESH_SECRET,
  { expiresIn: "7d" }
);

res.cookie("refreshToken", refreshToken, {
  httpOnly: true,
  secure: true,
  sameSite: "strict",
  path: "/api/refresh",
});
</code></pre>
<p>If you think of the access token as a closure over the user's identity that dies quickly, the refresh token is the function that re-creates it.</p>
<p>One thing most tutorials skip is <strong>rotation</strong>. A stolen refresh token is effectively a 7-day password. So every time it's used, issue a new one and invalidate the old one. If an already-used refresh token shows up again, someone has a copy, so revoke that whole session. That requires storing something server-side (a token ID or hash), which means this part is no longer stateless. That's fine. Pretending it can be is how you end up with unrevokable sessions.</p>
<h2>1.5 Where we are</h2>
<p>We now have two tokens: a short-lived one that opens doors, and a long-lived one that only makes new short-lived ones. The login flow works.</p>
]]></content:encoded></item><item><title><![CDATA[Build Desktop Applications with Tauri]]></title><description><![CDATA["JavaScript developers can only build web apps."
I hear this a lot, and it's not true anymore. With Tauri, you can build a desktop app using the frontend skills you already have. In this post I'll wal]]></description><link>https://blog.virajnikam.in/build-desktop-applications-with-tauri</link><guid isPermaLink="true">https://blog.virajnikam.in/build-desktop-applications-with-tauri</guid><category><![CDATA[Tauri]]></category><category><![CDATA[Rust]]></category><category><![CDATA[desktop]]></category><dc:creator><![CDATA[Viraj Nikam]]></dc:creator><pubDate>Mon, 04 Aug 2025 21:46:51 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1754343971969/98d3b76a-c312-47e6-8547-19d74b999ebc.webp" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>"JavaScript developers can only build web apps."</p>
<p>I hear this a lot, and it's not true anymore. With Tauri, you can build a desktop app using the frontend skills you already have. In this post I'll walk through how I set it up, and the parts that confused me along the way.</p>
<h2>1. Why Tauri?</h2>
<p>Tauri has two parts. Your UI runs inside the operating system's own webview, and the backend is written in Rust. Because it doesn't bundle a whole browser the way Electron does, the installers are small. You also get Rust's speed and safety for anything heavy.</p>
<h2>2. Setting up a new project</h2>
<p>If you're new to Tauri, start from scratch. It's much easier to see what each file does.</p>
<pre><code class="language-bash">npm create tauri-app@latest
</code></pre>
<p>The CLI asks a few questions:</p>
<ul>
<li><p><strong>Identifier:</strong> usually a reverse domain, like com.yourcompany.app</p>
</li>
<li><p><strong>Frontend language:</strong> TypeScript/JavaScript (I'd pick this)</p>
</li>
<li><p><strong>Package manager:</strong> pnpm, yarn, npm or bun</p>
</li>
<li><p><strong>UI template:</strong> React, Vue, Svelte, Solid and so on</p>
</li>
</ul>
<p>Then install and run:</p>
<pre><code class="language-bash">cd project_name
npm install
npm run tauri dev
</code></pre>
<p>A native window opens with your app inside.</p>
<h3>2.1 Adding Tauri to an existing React project</h3>
<pre><code class="language-bash">npm install --save-dev @tauri-apps/cli
npm install @tauri-apps/api
npx tauri init
</code></pre>
<p>tauri init creates a src-tauri folder with the Rust side. Add "tauri": "tauri" to the scripts in package.json, then npm run tauri dev will work.</p>
<h2>3. How the frontend talks to Rust</h2>
<p>Think of it as two programs. React renders the UI, and Rust has the native powers (filesystem, OS APIs, heavy logic). They talk through <strong>commands</strong>: you write a Rust function, then call it from JavaScript with invoke.</p>
<p>Rust side:</p>
<pre><code class="language-rust">#[tauri::command]
fn greet(name: &amp;str) -&gt; String {
    format!("Hello, {}!", name)
}

pub fn run() {
    tauri::Builder::default()
        .invoke_handler(tauri::generate_handler![greet])
        .run(tauri::generate_context!())
        .expect("error while running tauri application");
}
</code></pre>
<p>React side:</p>
<pre><code class="language-jsx">import { useState } from "react";
import { invoke } from "@tauri-apps/api/core";

function App() {
  const [name, setName] = useState("");
  const [greeting, setGreeting] = useState("");

  async function greetUser() {
    setGreeting(await invoke("greet", { name }));
  }

  return (
    &lt;div&gt;
      &lt;input value={name} onChange={(e) =&gt; setName(e.target.value)} /&gt;
      &lt;button onClick={greetUser}&gt;Greet&lt;/button&gt;
      &lt;p&gt;{greeting}&lt;/p&gt;
    &lt;/div&gt;
  );
}

export default App;
</code></pre>
<p>The string "greet" must match the Rust function name, and the argument keys must match the Rust parameter names.</p>
<h2>3.1 The setup hook</h2>
<p>.setup(|app| { ... }) runs once, after Tauri initializes and before the app starts. It's the right place to touch windows, register state, start background tasks or load plugins.</p>
<pre><code class="language-rust">use tauri::{Emitter, Manager};

.setup(|app| {
    let window = app.get_webview_window("main").unwrap();
    window.set_title("My App").unwrap();

    app.emit("backend-ready", "Rust side is up")?;
    Ok(())
})
</code></pre>
<p>The |app| { } part is just a Rust closure, similar to an arrow function in JS. Don't forget to return Ok(()) at the end.</p>
<p>To receive the event in React:</p>
<pre><code class="language-jsx">import { listen } from "@tauri-apps/api/event";

listen("backend-ready", (event) =&gt; {
  console.log(event.payload);
});
</code></pre>
<p>One catch: an event emitted inside setup can fire before your React code has registered a listener, so you might miss it. For anything critical, let the frontend ask for the data with invoke.</p>
<h2>3.2 Running things in the background</h2>
<p>Events can go the other way too. Here Rust fires an event every 30 seconds, and React reacts to it. I use this pattern to trigger data sync.</p>
<pre><code class="language-rust">// awake_interval.rs
use std::time::Duration;
use tauri::{AppHandle, Emitter};

pub async fn background_timer(app_handle: AppHandle) {
    let mut interval = tokio::time::interval(Duration::from_secs(30));
    loop {
        interval.tick().await;
        if let Err(e) = app_handle.emit("background_sync_trigger", ()) {
            eprintln!("Failed to emit event: {}", e);
        }
    }
}
</code></pre>
<pre><code class="language-rust">// lib.rs
mod awake_interval;

pub fn run() {
    tauri::Builder::default()
        .setup(|app| {
            let handle = app.handle().clone();
            tauri::async_runtime::spawn(awake_interval::background_timer(handle));
            Ok(())
        })
        .run(tauri::generate_context!())
        .expect("error while running tauri application");
}
</code></pre>
<pre><code class="language-jsx">// useBackgroundSync.js
import { useEffect, useState } from "react";
import { listen } from "@tauri-apps/api/event";

export default function useBackgroundSync(syncFn) {
  const [count, setCount] = useState(0);

  useEffect(() =&gt; {
    const unlisten = listen("background_sync_trigger", async () =&gt; {
      setCount((c) =&gt; c + 1);
      try {
        await syncFn?.();
      } catch (error) {
        console.error("Sync error:", error);
      }
    });

    return () =&gt; {
      unlisten.then((fn) =&gt; fn());
    };
  }, [syncFn]);

  return count;
}
</code></pre>
<p>Wrap syncFn in useCallback where you use the hook, otherwise the listener gets re-registered on every render.</p>
<h2>3.3 Controlling the window</h2>
<p>You can also control how the window behaves. For example, instead of quitting when the user clicks the close button, you can hide the window so the app keeps running in the background:</p>
<pre><code class="language-rust">.setup(|app| {
    if let Some(window) = app.get_webview_window("main") {
        let w = window.clone();
        window.on_window_event(move |event| {
            if let tauri::WindowEvent::CloseRequested { api, .. } = event {
                api.prevent_close();
                let _ = w.hide();
            }
        });
    }
    Ok(())
})
</code></pre>
<p>Be careful with this. If you hide the window, the user needs a way to bring it back, usually a system tray icon. Blocking the close button with no way back is a bad experience.</p>
<p>For launching on startup, use the official autostart plugin:</p>
<pre><code class="language-bash">npm run tauri add autostart
</code></pre>
<p>After that, you can enable it from Rust using the plugin's manager.</p>
<h2>4. Making network calls</h2>
<p>The webview's normal fetch still works, but it follows browser rules, so the server has to allow CORS. In a desktop app that gets annoying, because you often don't control the server.</p>
<p>The Tauri HTTP plugin solves this by making the request from the Rust side. It also lets you restrict which URLs the app can reach.</p>
<pre><code class="language-bash">npm run tauri add http
</code></pre>
<p>This installs both the Rust crate and the JS package. Then allow your URLs in src-tauri/capabilities/default.json:</p>
<pre><code class="language-json">{
  "identifier": "default",
  "windows": ["main"],
  "permissions": [
    "core:default",
    {
      "identifier": "http:default",
      "allow": [
        { "url": "http://localhost:9000/*" },
        { "url": "https://your-api.com/*" }
      ]
    }
  ]
}
</code></pre>
<p>Any URL not on this list is blocked. Now import fetch from the plugin:</p>
<pre><code class="language-js">import { fetch } from "@tauri-apps/plugin-http";

export const userLogin = async ({ email, password }) =&gt; {
  const BASE_URL = import.meta.env.VITE_BASE_URL;

  const response = await fetch(`${BASE_URL}/auth/login`, {
    method: "POST",
    headers: { "Content-Type": "application/json" },
    body: JSON.stringify({ email, password }),
  });

  return response.json();
};
</code></pre>
<h2>5. Wrapping up</h2>
<p>You don't need to learn Rust to start with Tauri. Commands, events and the SETUP hook are enough to get a real app running, and you can pick up Rust as you go. 🚀</p>
]]></content:encoded></item><item><title><![CDATA[IndexedDB - browsers own storage system.]]></title><description><![CDATA[Introduction.
In a recent article, I introduced Dexie.js, a wrapper library that simplifies working with IndexedDB. I should have published this article first, the one on using IndexedDB with its nati]]></description><link>https://blog.virajnikam.in/indexeddb-browsers-own-storage-system</link><guid isPermaLink="true">https://blog.virajnikam.in/indexeddb-browsers-own-storage-system</guid><category><![CDATA[indexeddb]]></category><category><![CDATA[JavaScript]]></category><dc:creator><![CDATA[Viraj Nikam]]></dc:creator><pubDate>Sat, 19 Jul 2025 10:37:09 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1752921384486/9685cc9c-232b-4d36-966a-62e5794554a2.webp" length="0" type="image/jpeg"/><content:encoded><![CDATA[<h3>Introduction.</h3>
<p>In a recent article, I introduced Dexie.js, a wrapper library that simplifies working with IndexedDB. I should have published this article first, the one on using IndexedDB with its native API. You only truly appreciate the abstraction Dexie provides once you’ve felt the pain of writing raw IndexedDB code yourself.</p>
<p>So I won’t revisit Dexie here, since my earlier post already covers it in detail. If you’re curious how it smooths things over, read that article after finishing this one.</p>
<h3>Why IndexedDB?</h3>
<p>You might wonder: if we have localStorage which is easy to use, why do we need IndexedDB at all?</p>
<p>That’s a fair question but only partially correct. Let's talk about localStorage first:</p>
<ul>
<li><p>localStorage has a <strong>limited capacity</strong>, usually around <strong>5–10MB</strong>.</p>
</li>
<li><p>Its operations are <strong>synchronous</strong>, meaning large data inserts or updates can block the UI and make the app unresponsive.</p>
</li>
<li><p>It’s suitable for <strong>small, frequent, and simple data</strong> operations.</p>
</li>
</ul>
<p>In contrast, <strong>IndexedDB</strong> offers:</p>
<ul>
<li><p><strong>Asynchronous</strong> operations which means it doesn’t block the main thread.</p>
</li>
<li><p>Support for <strong>structured data</strong> stored in <strong>object stores</strong> (which you can think of as tables).</p>
</li>
<li><p><strong>Indexed querying</strong>, allowing you to query data more efficiently.</p>
</li>
<li><p>Support for <strong>versioning</strong> and schema upgrades.</p>
</li>
</ul>
<p>NOTE : WE CAN ONLY INCREMENT THE DATABASE VERSION, IF WE TRY TO DECREMENT THE VERSION THE onerror EVENT WILL TRIGGER.</p>
<h3>Let’s Code: Setting Up IndexedDB with Native API</h3>
<p>IndexedDB uses an <strong>event-driven</strong> architecture. So, the sequence in which you write operations doesn’t matter as much as how you handle events.</p>
<pre><code class="language-javascript">// Open a database connection
const database = indexedDB.open("MyDatabase", 1); 
// Pass: database name, and version number
</code></pre>
<p>step 1 : Create objectStore (Tables) and Indexes.</p>
<pre><code class="language-javascript">database.onupgradeneeded = (event) =&gt; {
    const db = event.target.result;

    if (!db.objectStoreNames.contains("students")) {
        const store = db.createObjectStore("students", { keyPath: "id" });

        // Indexes: (indexName, keyPath, options)
        store.createIndex("name", "name", { unique: false });
        store.createIndex("email", "email", { unique: true });
    }
};
</code></pre>
<p>step 2 : Insert Data</p>
<pre><code class="language-javascript">database.onsuccess = (event) =&gt; {
    const db = event.target.result;

    const transaction = db.transaction("students", "readwrite");
    const store = transaction.objectStore("students");

    const request = store.add({
        id: 1,
        name: "Viraj Nikam",
        email: "viraj@gmail.com"
    });

    request.onsuccess = (event) =&gt; {
        console.log("Data inserted successfully!", event.target.result);
    };

    request.onerror = (event) =&gt; {
        console.error("Failed to insert data:", event.target.error);
    };
};
</code></pre>
<h3>IndexedDB Native API – CRUD Operations</h3>
<p><strong>1. Add Data (Create)</strong></p>
<pre><code class="language-javascript">const request = store.add({
    id: 1,
    name: "Viraj Nikam",
    email: "viraj@gmail.com"
});
</code></pre>
<p><strong>2. Get Data (Read)</strong></p>
<p>a. Get by primary key (id)</p>
<pre><code class="language-javascript">const getRequest = store.get(1);

getRequest.onsuccess = (event) =&gt; {
    console.log("Record fetched:", event.target.result);
};

getRequest.onerror = (event) =&gt; {
    console.error("Error fetching record:", event.target.error);
};
</code></pre>
<p>b. Get by Index</p>
<pre><code class="language-javascript">const index = store.index("email");
const emailRequest = index.get("viraj@gmail.com");

emailRequest.onsuccess = (event) =&gt; {
    console.log("Record fetched by email:", event.target.result);
};
</code></pre>
<p><strong>3. Update Data</strong></p>
<p>To update, you first fetch the record, modify it, and then use put() (not add(), since add() will fail if the key already exists):</p>
<pre><code class="language-javascript">const getRequest = store.get(1);

getRequest.onsuccess = (event) =&gt; {
    const data = event.target.result;
    data.name = "Viraj S. Nikam"; // Updating the name

    const updateRequest = store.put(data);

    updateRequest.onsuccess = () =&gt; {
        console.log("Record updated successfully!");
    };
};
</code></pre>
<p><strong>4. Delete Data</strong></p>
<pre><code class="language-javascript">const deleteRequest = store.delete(1);

deleteRequest.onsuccess = () =&gt; {
    console.log("Record deleted successfully!");
};

deleteRequest.onerror = (event) =&gt; {
    console.error("Failed to delete record:", event.target.error);
};
</code></pre>
<h3>Final Execution :</h3>
<pre><code class="language-javascript">const database = indexedDB.open("MyDatabase", 1) // Here you need to pass name of the database and it's version

// add tables to your db over here at onupgradeneeded
database.onupgradeneeded((evt)=&gt;{
    const dbInstance = evt.target.result;

    if(!dbInstance.objectStoreNames.contains("students")){
            const request = dbInstance.createObjectStore("students", {keyPath : "id"});
            //createIndex used to define the columns, Its necessary to define Indexes because it helps you at time of queries
            request.createIndex("name", "name", {unique : false}) // takes column name, accessar key for that perticular column, and object of the constraints
            request.createIndex("email", "email", {unique : true})
    }
})

//perform your queries over here
database.onsuccess = (event) =&gt; {
    const db = event.target.result;
    const transaction = db.transaction("students", "readwrite");
    const store = transaction.objectStore("students");

    // Add
    const addRequest = store.add({
        id: 2,
        name: "Jane Doe",
        email: "jane@example.com"
    });

    // Read by ID
    const getRequest = store.get(2);
    getRequest.onsuccess = () =&gt; console.log("Fetched:", getRequest.result);

    // Update
    getRequest.onsuccess = () =&gt; {
        const data = getRequest.result;
        data.name = "Jane D.";
        const updateRequest = store.put(data);
        updateRequest.onsuccess = () =&gt; console.log("Updated!");
    };

    // Delete
    const deleteRequest = store.delete(1);
    deleteRequest.onsuccess = () =&gt; console.log("Deleted record with ID 1");
};
</code></pre>
<h3>Wrapping Up.</h3>
<p>Working with native IndexedDB can feel a bit messy and confused due to its event-based flow. That’s exactly why libraries like <strong>Dexie.js</strong> exist to make your life easier.</p>
<p>But understanding the native API is crucial for debugging, customization, and appreciating how much a wrapper is actually helping you.</p>
<p>Thank You !☺️</p>
]]></content:encoded></item><item><title><![CDATA[Dexie.js : The Smart Way to Handel Local Database in JavaScript]]></title><description><![CDATA[Introduction
If you’ve ever worked with IndexedDB, you know it can be a bit painful—lots of boilerplate and a complex API just to do simple things. That’s where Dexie.js comes in. It’s a lightweight wrapper that makes IndexedDB simple, clean, and dev...]]></description><link>https://blog.virajnikam.in/dexiejs-learn-the-basics</link><guid isPermaLink="true">https://blog.virajnikam.in/dexiejs-learn-the-basics</guid><category><![CDATA[Dexie.js]]></category><category><![CDATA[JavaScript]]></category><category><![CDATA[React]]></category><category><![CDATA[offline]]></category><dc:creator><![CDATA[Viraj Nikam]]></dc:creator><pubDate>Tue, 08 Jul 2025 19:03:15 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1752001007448/b60008f1-f777-4588-bf2b-99486755b061.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<h3 id="heading-introduction">Introduction</h3>
<p>If you’ve ever worked with <strong>IndexedDB</strong>, you know it can be a bit painful—lots of boilerplate and a complex API just to do simple things. That’s where <strong>Dexie.js</strong> comes in. It’s a lightweight wrapper that makes IndexedDB simple, clean, and developer-friendly.</p>
<p>I recently started building an <strong>offline-first application</strong>, and I needed something more powerful than localStorage and less messy than raw IndexedDB. After exploring a few options, <strong>Dexie.js</strong> turned out to be the perfect fit.</p>
<p>In this article, I’ll walk you through the basics—setting up a database, performing CRUD operations, and integrating it with <strong>React</strong> (don’t worry if you’re not into React, the concepts will still make sense).</p>
<h3 id="heading-why-i-chose-dexiejs-for-offline-data-management">Why I Chose Dexie.js for Offline Data Management.</h3>
<p>Dexie.js is a smart wrapper around <strong>IndexedDB</strong> that makes working with local databases in JavaScript much easier and cleaner. If you’ve ever tried IndexedDB directly, you know how tricky it can be. Dexie simplifies that headache.</p>
<p>While building an <strong>offline-first application</strong>, one big challenge is choosing the right way to manage local data. localStorage is too limited, and working directly with IndexedDB feels like wrestling with unnecessary complexity.</p>
<p><strong>Dexie.js</strong>—a lightweight and developer-friendly solution that keeps things simple while still giving you the power of IndexedDB under the hood.</p>
<h3 id="heading-lets-write-the-code">Let’s write the code.</h3>
<p>First, we need to set up the database instance:</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">import</span> Dexie <span class="hljs-keyword">from</span> <span class="hljs-string">"dexie"</span>;

<span class="hljs-keyword">const</span> db = <span class="hljs-keyword">new</span> Dexie(<span class="hljs-string">'myDatabase'</span>);
            db.version(<span class="hljs-number">1</span>).stores({
                    <span class="hljs-attr">user</span> : <span class="hljs-string">'++id, name, email, password, roleId'</span>,
                    <span class="hljs-attr">tasks</span> : <span class="hljs-string">'++id, title, description, isCompleted, createdAt, updatedAt'</span>
            })
</code></pre>
<p>That’s it! This small setup gives you two tables: user and tasks. Now you can start performing different operations without the hassle of raw IndexedDB code.</p>
<h3 id="heading-crud-operations-with-dexiejs"><strong>CRUD Operations with Dexie.js</strong></h3>
<p>Below are the most common operations:</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">import</span> db <span class="hljs-keyword">from</span> <span class="hljs-string">'path-to-db'</span>
<span class="hljs-comment">//Add Task</span>
<span class="hljs-keyword">const</span> addTaskToDB = <span class="hljs-keyword">async</span>(data)=&gt;{
    <span class="hljs-keyword">await</span> db.tasks.add(data)
}

<span class="hljs-comment">//Delete Task</span>
<span class="hljs-keyword">const</span> deleteTaskById = <span class="hljs-keyword">async</span>(id)=&gt;{
    <span class="hljs-keyword">await</span> db.tasks.delete(id)
}

<span class="hljs-comment">//Update Task</span>
<span class="hljs-keyword">const</span> updateTask = <span class="hljs-keyword">async</span>(data)=&gt;{
    <span class="hljs-keyword">await</span> db.tasks.put(data)
}

<span class="hljs-comment">//Get Task by Id</span>
<span class="hljs-keyword">const</span> getTaskById = <span class="hljs-keyword">async</span> (id)=&gt;{
    <span class="hljs-keyword">return</span> db.tasks.get(id)
}

<span class="hljs-comment">//Get all Tasks</span>
<span class="hljs-keyword">const</span> getAllTasks = <span class="hljs-keyword">async</span>()=&gt;{
    <span class="hljs-keyword">return</span> db.tasks.toArray();
}

<span class="hljs-keyword">export</span> {addTaskToDB}
</code></pre>
<h3 id="heading-using-dexie-in-a-react-component"><strong>Using Dexie in a React Component</strong></h3>
<p>Here’s a quick example to show how we can integrate it with React:</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">import</span> {addTaskToDB} <span class="hljs-keyword">from</span> <span class="hljs-string">'actions'</span>
<span class="hljs-keyword">const</span> MyReactComponent = <span class="hljs-function">()=&gt;</span>{
    <span class="hljs-keyword">const</span> [newTask, setNewTask] = useState({
            <span class="hljs-attr">title</span> : <span class="hljs-string">""</span>,
            <span class="hljs-attr">description</span> : <span class="hljs-string">""</span>
      });

    <span class="hljs-keyword">const</span> addTask = <span class="hljs-keyword">async</span>()=&gt;{
        <span class="hljs-keyword">await</span> addTaskToDB(newTask);
         globalThis.alert(<span class="hljs-string">"Task Added"</span>)
    }

    <span class="hljs-keyword">const</span> handelTaskInputChange = <span class="hljs-function">(<span class="hljs-params">evt</span>)=&gt;</span>{
        <span class="hljs-keyword">const</span> {name, value} = evt.target;
        setNewTask(<span class="hljs-function">(<span class="hljs-params">prev</span>)=&gt;</span>({...prev, [name]: value}))
     }


    <span class="hljs-keyword">return</span>(
        <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">form</span> <span class="hljs-attr">onSubmit</span>=<span class="hljs-string">{addTask}</span>&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">input</span> <span class="hljs-attr">type</span>=<span class="hljs-string">'text'</span> <span class="hljs-attr">onChange</span>=<span class="hljs-string">{handelTaskInputChange}</span> <span class="hljs-attr">value</span>=<span class="hljs-string">{newTask.title}/</span>&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">input</span> <span class="hljs-attr">type</span>=<span class="hljs-string">'text'</span> <span class="hljs-attr">onChange</span>=<span class="hljs-string">{handelTaskInputChange}</span> <span class="hljs-attr">value</span>=<span class="hljs-string">{newTask.description}</span>&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">button</span>&gt;</span>Add Task +<span class="hljs-tag">&lt;/<span class="hljs-name">button</span>&gt;</span>
        <span class="hljs-tag">&lt;/<span class="hljs-name">form</span>&gt;</span>
    )
}</span>
</code></pre>
<h3 id="heading-advance-queries"><strong>Advance Queries</strong></h3>
<ol>
<li><p>Suppose you need to update specific fields (like name) for multiple rows in a Dexie.js table based on their id. A common real-world scenario is when you have an array of objects, and you want to update the database to reflect new values where the id matches.</p>
<p> Let’s break this down with an example:</p>
</li>
</ol>
<pre><code class="lang-javascript"><span class="hljs-keyword">const</span> data = [
  { <span class="hljs-attr">id</span>: <span class="hljs-number">1</span>, <span class="hljs-attr">name</span>: <span class="hljs-string">"Sarthak"</span> },
  { <span class="hljs-attr">id</span>: <span class="hljs-number">2</span>, <span class="hljs-attr">name</span>: <span class="hljs-string">"Shubham"</span> },
  { <span class="hljs-attr">id</span>: <span class="hljs-number">3</span>, <span class="hljs-attr">name</span>: <span class="hljs-string">"Rahul"</span> }
];

<span class="hljs-keyword">await</span> db.tableName
  .where(<span class="hljs-string">"id"</span>)
  .anyOf(data.map(<span class="hljs-function">(<span class="hljs-params">row</span>) =&gt;</span> row.id))
  .modify(<span class="hljs-function">(<span class="hljs-params">record</span>) =&gt;</span> {
    <span class="hljs-keyword">const</span> updated = data.find(<span class="hljs-function">(<span class="hljs-params">row</span>) =&gt;</span> row.id === record.id);
    <span class="hljs-keyword">if</span> (updated) {
      record.name = updated.name;
    }
  });
</code></pre>
<h3 id="heading-final-thoughts"><strong>Final Thoughts</strong></h3>
<p>If you’re tired of the limitations of localStorage or the complexity of IndexedDB, Dexie.js is the perfect middle ground. It’s fast, reliable, and a great tool for building <strong>offline-first applications</strong>.</p>
<p>I hope this guide helped you get started. If you’ve used Dexie before, share your experience in the comments!</p>
]]></content:encoded></item><item><title><![CDATA[React Hook Form]]></title><description><![CDATA[Lesson 1: Story behind.
Before I discovered React Hook Form, I realized I was a complete noob when it came to handling form data in React. I used to manage form state and validations using local state variables. While this approach works for simple f...]]></description><link>https://blog.virajnikam.in/react-hook-form</link><guid isPermaLink="true">https://blog.virajnikam.in/react-hook-form</guid><category><![CDATA[React]]></category><category><![CDATA[react-hook-form]]></category><category><![CDATA[forms]]></category><category><![CDATA[Next.js]]></category><dc:creator><![CDATA[Viraj Nikam]]></dc:creator><pubDate>Fri, 27 Jun 2025 13:37:34 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1750998019497/803b8534-10d2-4161-97e5-6fff311f00d9.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<h3 id="heading-lesson-1-story-behind">Lesson 1: Story behind.</h3>
<p>Before I discovered <strong>React Hook Form</strong>, I realized I was a complete <strong>noob</strong> when it came to handling form data in React. I used to manage form state and validations using local state variables. While this approach works for <strong>simple forms</strong>, it quickly becomes <strong>messy and hard to scale</strong> as your form grows.</p>
<p>To make things more maintainable, I thought of lifting the state to a <strong>global level</strong> using something like <strong>Context API</strong> or even a state management library like Redux or Zustand. But that also felt like <strong>overkill</strong> for just managing form inputs and validations.</p>
<h3 id="heading-lesson-2-introduction">Lesson 2: Introduction.</h3>
<p><mark>React Hook Form is a lightweight library which uses react hooks to manage the form state and validations. </mark> It fits right into your existing react component structure, making it easy to build and manage forms without a lot of extra code.</p>
<h3 id="heading-lesson-3-lets-write-and-understand-the-code">Lesson 3: let's write and understand the code.</h3>
<p>Managing form state usually means keeping track of local state with the useState hook, context api or any other state management library like redux or zustand. React hook form takes care of this by providing you its own context api.</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">import</span> {useForm, FormProvider} <span class="hljs-keyword">from</span> <span class="hljs-string">'react-hook-form'</span>;


<span class="hljs-keyword">const</span> ReactHookFormExample = <span class="hljs-function">()=&gt;</span>{

  <span class="hljs-keyword">const</span> rfhMethods = useForm({
    <span class="hljs-attr">defaultValues</span> : {
            <span class="hljs-attr">name</span>: <span class="hljs-string">"Jhon doe"</span>
            <span class="hljs-attr">mobile</span> : <span class="hljs-number">4523652145</span>
        }
    });

  <span class="hljs-keyword">const</span> {register, reset, handelSubmit, <span class="hljs-attr">formState</span>:{errors}} = rfhMethods;

  <span class="hljs-keyword">const</span> onSubmit = <span class="hljs-function">(<span class="hljs-params">formData</span>)=&gt;</span>{
    <span class="hljs-built_in">console</span>.log(formData, <span class="hljs-string">"FORM_DATA"</span>);
    reset();
  }


<span class="hljs-keyword">return</span> (
    <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">FormProvider</span> {<span class="hljs-attr">...rfhMethods</span>}&gt;</span>
       <span class="hljs-tag">&lt;<span class="hljs-name">form</span>&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">className</span>=<span class="hljs-string">'user-name'</span>&gt;</span>
                <span class="hljs-tag">&lt;<span class="hljs-name">input</span> <span class="hljs-attr">type</span>=<span class="hljs-string">"text"</span> {<span class="hljs-attr">...register</span>("<span class="hljs-attr">name</span>")} /&gt;</span>
                 {errors.name &amp;&amp; <span class="hljs-tag">&lt;<span class="hljs-name">span</span>&gt;</span>{errors.name.message}<span class="hljs-tag">&lt;/<span class="hljs-name">span</span>&gt;</span>}
            <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">className</span>=<span class="hljs-string">'user-mobile'</span>&gt;</span>
                 <span class="hljs-tag">&lt;<span class="hljs-name">input</span> <span class="hljs-attr">type</span>=<span class="hljs-string">"number"</span> {<span class="hljs-attr">...register</span>("<span class="hljs-attr">mobile</span>")}&gt;</span>
                  {errors.mobile &amp;&amp; <span class="hljs-tag">&lt;<span class="hljs-name">span</span>&gt;</span>{errors.mobile.message}<span class="hljs-tag">&lt;/<span class="hljs-name">span</span>&gt;</span>}
            <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
       <span class="hljs-tag">&lt;/<span class="hljs-name">form</span>&gt;</span>
    <span class="hljs-tag">&lt;/<span class="hljs-name">FormProvider</span>&gt;</span>
        )
}</span>
</code></pre>
<p>In the above example, useForm provides several methods &amp; properties to manage the form:</p>
<ol>
<li><p><strong>register</strong> : a function to register the fields.</p>
</li>
<li><p><strong>handelSubmit</strong> : a function to handel the form submission.</p>
</li>
<li><p><strong>formState</strong> : its an object containing the form state with validation errors.</p>
</li>
</ol>
<p>FormProvider is the context provider given to us by react hook form, to handel the form state in the nested components. eg:</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">const</span> {useFormContext} <span class="hljs-keyword">from</span> <span class="hljs-string">'react-hook-form'</span>;

<span class="hljs-keyword">const</span> nestedChildComponent = <span class="hljs-function">()=&gt;</span>{
    <span class="hljs-keyword">const</span> {register} = useFormContext();

<span class="hljs-keyword">return</span> (
        <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">className</span>=<span class="hljs-string">'user-city'</span>&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">select</span> {<span class="hljs-attr">...register</span>("<span class="hljs-attr">city</span>")}&gt;</span>
                <span class="hljs-tag">&lt;<span class="hljs-name">option</span> <span class="hljs-attr">value</span>=<span class="hljs-string">'pune'</span>&gt;</span>Pune<span class="hljs-tag">&lt;/<span class="hljs-name">option</span>&gt;</span>
                <span class="hljs-tag">&lt;<span class="hljs-name">option</span> <span class="hljs-attr">value</span>=<span class="hljs-string">'amdabad'</span>&gt;</span>Amdabad<span class="hljs-tag">&lt;/<span class="hljs-name">option</span>&gt;</span>
                <span class="hljs-tag">&lt;<span class="hljs-name">option</span> <span class="hljs-attr">value</span>=<span class="hljs-string">'mumbai'</span>&gt;</span>Mumbai<span class="hljs-tag">&lt;/<span class="hljs-name">option</span>&gt;</span>
            <span class="hljs-tag">&lt;/<span class="hljs-name">select</span>&gt;</span>
        <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span></span>
       )
}
</code></pre>
<p>Here, with the useFormContext method you can access all the methods that useForm provides.</p>
<h3 id="heading-lesson-4-traditional-forms-in-react-why-its-a-struggle">Lesson 4 : Traditional Forms in React – Why It's a Struggle</h3>
<p>When you're building forms using plain React (also called "traditional forms"), things can quickly get messy. You have to manually handle every input, track changes, validate data, and manage errors — all by yourself. It may sound simple at first, but even a basic form can turn into a lot of boilerplate code.</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">import</span> React, {useState} <span class="hljs-keyword">from</span> <span class="hljs-string">"react"</span>;

<span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> <span class="hljs-keyword">const</span> TraditionalForms = <span class="hljs-function">()=&gt;</span>{
    <span class="hljs-keyword">const</span> [formData, setFormData] = useState({<span class="hljs-attr">name</span>:<span class="hljs-string">""</span>, <span class="hljs-attr">email</span>:<span class="hljs-string">""</span>});
    <span class="hljs-keyword">const</span> [errors, setErrors] = useState({});

    <span class="hljs-keyword">const</span> handelInputChange = <span class="hljs-function">(<span class="hljs-params">evt</span>)=&gt;</span>{
        <span class="hljs-keyword">const</span> {name, value} = evt?.target;
            setFormData(<span class="hljs-function">(<span class="hljs-params">prev</span>)=&gt;</span>({...prev, [name]: value}))
     }

    <span class="hljs-keyword">const</span> onSubmit = <span class="hljs-function">(<span class="hljs-params">evt</span>)=&gt;</span>{
        evt.preventDefault();

        <span class="hljs-keyword">const</span> errors = {};
        <span class="hljs-keyword">if</span>(formData.name?.length &lt; <span class="hljs-number">2</span>) errors.name = <span class="hljs-string">"name should contain atlist 2 characters"</span>;
        <span class="hljs-keyword">if</span>(!<span class="hljs-regexp">/\S+@\S+\.\S+/</span>.test(formData.email)) errors.email = <span class="hljs-string">"enter valid email"</span>;

        setErrors(errors);

        <span class="hljs-keyword">if</span>(<span class="hljs-built_in">Object</span>.keys(errors).length===<span class="hljs-number">0</span>){
            <span class="hljs-built_in">console</span>.log(<span class="hljs-string">"FORM_DATA"</span>, formData);
          }

    };


    <span class="hljs-keyword">return</span> (
        <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">form</span> <span class="hljs-attr">onSubmit</span>=<span class="hljs-string">{onSubmit}</span>&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">input</span> <span class="hljs-attr">type</span>=<span class="hljs-string">'text'</span> <span class="hljs-attr">name</span>=<span class="hljs-string">'name'</span> <span class="hljs-attr">value</span>=<span class="hljs-string">{formData.name}</span> <span class="hljs-attr">onChange</span>=<span class="hljs-string">{handelInputChange}</span> /&gt;</span>
            {errors?.name &amp;&amp; <span class="hljs-tag">&lt;<span class="hljs-name">span</span>&gt;</span>{errors.name}<span class="hljs-tag">&lt;/<span class="hljs-name">span</span>&gt;</span>}

            <span class="hljs-tag">&lt;<span class="hljs-name">input</span> <span class="hljs-attr">type</span>=<span class="hljs-string">'email'</span> <span class="hljs-attr">name</span>=<span class="hljs-string">'email'</span> <span class="hljs-attr">value</span>=<span class="hljs-string">{formData.email}</span> <span class="hljs-attr">onChange</span>=<span class="hljs-string">{handelInputChange}</span> /&gt;</span>
            {errors?.email &amp;&amp; <span class="hljs-tag">&lt;<span class="hljs-name">span</span>&gt;</span>{errors.email}<span class="hljs-tag">&lt;/<span class="hljs-name">span</span>&gt;</span>}

            <span class="hljs-tag">&lt;<span class="hljs-name">button</span> <span class="hljs-attr">type</span>=<span class="hljs-string">'submit'</span>&gt;</span> Submit <span class="hljs-tag">&lt;/<span class="hljs-name">button</span>&gt;</span> //type : "submit" | "button" | "reset" by default to submit
        <span class="hljs-tag">&lt;/<span class="hljs-name">form</span>&gt;</span></span>
           )
</code></pre>
<p>If you're just starting out, building forms manually is a great way to learn. But as your project grows, you'll definitely want to use a form library to avoid drowning in state management and validation logic.</p>
<p><strong>Traditional forms teach you the basics — but modern form libraries help you move faster and write cleaner code.</strong></p>
]]></content:encoded></item></channel></rss>