<?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[Navayuvan's Blog]]></title><description><![CDATA[I write about building real-world software with AI, focusing on what broke, what scaled, and what finally worked, so you can design workflows that fit your own ]]></description><link>https://blogs.navayuvan.com</link><image><url>https://cdn.hashnode.com/res/hashnode/image/upload/v1766638304733/94744ee2-8ee7-44a6-81d0-032ce20bf29c.png</url><title>Navayuvan&apos;s Blog</title><link>https://blogs.navayuvan.com</link></image><generator>RSS for Node</generator><lastBuildDate>Wed, 15 Apr 2026 08:55:26 GMT</lastBuildDate><atom:link href="https://blogs.navayuvan.com/rss.xml" rel="self" type="application/rss+xml"/><language><![CDATA[en]]></language><ttl>60</ttl><item><title><![CDATA[Why Using Multiple Claude Code Accounts Is Broken — And the Setup That Finally Fixed It]]></title><description><![CDATA[I use multiple Claude Code accounts.
One is my office account with the Pro plan. Another is a shared account I use with my colleagues on the Max plan.
At first, I assumed this would be simple.
Log out]]></description><link>https://blogs.navayuvan.com/multiple-claude-code-workspace</link><guid isPermaLink="true">https://blogs.navayuvan.com/multiple-claude-code-workspace</guid><category><![CDATA[claude-code]]></category><category><![CDATA[claude.ai]]></category><category><![CDATA[AI]]></category><category><![CDATA[Developer Workflow]]></category><category><![CDATA[Software Engineering]]></category><category><![CDATA[AI Development Services]]></category><category><![CDATA[ai agents]]></category><category><![CDATA[coding agents]]></category><category><![CDATA[developer experience]]></category><dc:creator><![CDATA[Navayuvan Subramanian]]></dc:creator><pubDate>Fri, 03 Apr 2026 13:22:03 GMT</pubDate><enclosure url="https://cdn.hashnode.com/uploads/covers/6544f59782acc66595e68746/78fcf973-183a-497f-84d5-9cadf36e7d6f.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>I use multiple Claude Code accounts.</p>
<p>One is my office account with the Pro plan. Another is a shared account I use with my colleagues on the Max plan.</p>
<p>At first, I assumed this would be simple.</p>
<p>Log out. Log in. Maybe switch profiles. Done.</p>
<p>But that is not how Claude Code works.</p>
<p>Every time I switched accounts, I either lost my project history, lost my custom commands, or had to keep setting everything up again.</p>
<p>And the worst part?</p>
<p>The only thing that actually needed to be different between accounts was the authentication.</p>
<p>Everything else — my project history, my commands, my MCP setup, my CLAUDE.md instructions, my settings — should have stayed the same.</p>
<p>Instead, Claude Code treats everything as one giant config folder.</p>
<p>So if you want multiple accounts, you end up duplicating everything.</p>
<p>That quickly becomes painful.</p>
<h2>The Actual Problem</h2>
<p>Claude Code stores almost everything inside a single config directory.</p>
<p>That includes:</p>
<ul>
<li><p>Authentication</p>
</li>
<li><p>Settings</p>
</li>
<li><p>Project history</p>
</li>
<li><p>Custom commands</p>
</li>
<li><p>MCP configuration</p>
</li>
<li><p>CLAUDE.md instructions</p>
</li>
</ul>
<p>The issue is that authentication is account-specific.</p>
<p>But the rest is not.</p>
<p>I do not want separate project histories for every account. I do not want to recreate the same custom commands three times. I definitely do not want to copy-paste the same CLAUDE.md file every time I make a change.</p>
<p>After a few switches, my setup looked like this:</p>
<ul>
<li><p>One account had the latest commands</p>
</li>
<li><p>Another had the right MCP config</p>
</li>
<li><p>A third one had the actual project history I wanted</p>
</li>
</ul>
<p>Everything was fragmented.</p>
<img src="https://cdn.hashnode.com/uploads/covers/6544f59782acc66595e68746/e32c17eb-b4ea-4815-aa25-311925aa7e35.svg" alt="" style="display:block;margin:0 auto" />

<p>And every time I switched accounts, something would silently stop working.</p>
<h2>The Setup That Actually Works</h2>
<p>The fix was separating authentication from everything else.</p>
<p>Instead of giving every account its own full Claude config, I created:</p>
<ul>
<li><p>One shared folder for everything common</p>
</li>
<li><p>One small folder per account containing only auth</p>
</li>
</ul>
<p>My setup now looks like this:</p>
<p>The fix was separating authentication from everything else.</p>
<p>Instead of giving every account its own full Claude config, I created:</p>
<ul>
<li><p>One shared folder for everything common</p>
</li>
<li><p>One small folder per account containing only auth</p>
</li>
</ul>
<p>My setup now looks like this:</p>
<pre><code class="language-bash">~/.claude-shared/
  settings.json
  CLAUDE.md
  projects/
  commands/
  mcp.json

~/.claude-office-pro/
  auth.json
  settings.json -&gt; ~/.claude-shared/settings.json
  CLAUDE.md -&gt; ~/.claude-shared/CLAUDE.md
  projects -&gt; ~/.claude-shared/projects
  commands -&gt; ~/.claude-shared/commands
  mcp.json -&gt; ~/.claude-shared/mcp.json

~/.claude-shared-max/
  auth.json
  settings.json -&gt; ~/.claude-shared/settings.json
  CLAUDE.md -&gt; ~/.claude-shared/CLAUDE.md
  projects -&gt; ~/.claude-shared/projects
  commands -&gt; ~/.claude-shared/commands
  mcp.json -&gt; ~/.claude-shared/mcp.json
</code></pre>
<img src="https://cdn.hashnode.com/uploads/covers/6544f59782acc66595e68746/5d5f77d7-b5ce-4681-91c1-7994060a2c3d.svg" alt="" style="display:block;margin:0 auto" />

<p>The only real file that is different between accounts is <code>auth.json</code>.</p>
<p>Everything else is shared using symlinks.</p>
<p>Which means:</p>
<ul>
<li><p>I log into each account once</p>
</li>
<li><p>All accounts use the same project history</p>
</li>
<li><p>All accounts use the same commands</p>
</li>
<li><p>All accounts use the same CLAUDE.md instructions</p>
</li>
<li><p>Changing something in one place updates it everywhere</p>
</li>
</ul>
<p>This was the first setup that actually felt usable.</p>
<h2>Setting It Up</h2>
<p>First, create the shared folder and separate account folders.</p>
<pre><code class="language-bash">mkdir -p ~/.claude-shared
mkdir -p ~/.claude-office-pro
mkdir -p ~/.claude-shared-max
</code></pre>
<p>Then move all the common files into the shared folder.</p>
<pre><code class="language-bash">mv ~/.claude/settings.json ~/.claude-shared/
mv ~/.claude/CLAUDE.md ~/.claude-shared/
mv ~/.claude/projects ~/.claude-shared/
mv ~/.claude/commands ~/.claude-shared/
mv ~/.claude/mcp.json ~/.claude-shared/
</code></pre>
<p>Now create symlinks inside each account folder.</p>
<pre><code class="language-bash">ln -s ~/.claude-shared/settings.json ~/.claude-office-pro/settings.json
ln -s ~/.claude-shared/CLAUDE.md ~/.claude-office-pro/CLAUDE.md
ln -s ~/.claude-shared/projects ~/.claude-office-pro/projects
ln -s ~/.claude-shared/commands ~/.claude-office-pro/commands
ln -s ~/.claude-shared/mcp.json ~/.claude-office-pro/mcp.json

ln -s ~/.claude-shared/settings.json ~/.claude-shared-max/settings.json
ln -s ~/.claude-shared/CLAUDE.md ~/.claude-shared-max/CLAUDE.md
ln -s ~/.claude-shared/projects ~/.claude-shared-max/projects
ln -s ~/.claude-shared/commands ~/.claude-shared-max/commands
ln -s ~/.claude-shared/mcp.json ~/.claude-shared-max/mcp.json
</code></pre>
<p>Then create aliases so switching accounts becomes one command.</p>
<pre><code class="language-bash">alias claude-office='CLAUDE_CONFIG_DIR=~/.claude-office-pro claude'
alias claude-shared='CLAUDE_CONFIG_DIR=~/.claude-shared-max claude'
</code></pre>
<img src="https://cdn.hashnode.com/uploads/covers/6544f59782acc66595e68746/fd44232c-6cd4-4709-b36e-4dd6f863190c.svg" alt="" style="display:block;margin:0 auto" />

<p>Now when I want to use my office Pro account, I run:</p>
<pre><code class="language-bash">claude-office
</code></pre>
<p>And when I want to switch to the shared Max account that I use with my colleagues:</p>
<pre><code class="language-bash">claude-shared
</code></pre>
<p>That is it.</p>
<p>No logging in again. No copying files. No broken commands.</p>
<h2>The Bigger Lesson</h2>
<p>This looked like a Claude Code problem.</p>
<p>But it is actually a common problem with developer tools.</p>
<p>Most tools bundle:</p>
<ul>
<li><p>Identity</p>
</li>
<li><p>Configuration</p>
</li>
<li><p>User data</p>
</li>
</ul>
<p>Into one folder.</p>
<p>That works fine until you need multiple accounts.</p>
<p>Then suddenly the thing you actually want to isolate — authentication — is tied to a bunch of things that should have been shared.</p>
<p>The best setups separate those concerns.</p>
<p>Authentication should be isolated. Everything else should be reusable.</p>
<p>Once I changed my Claude setup to follow that idea, switching accounts stopped feeling like context switching.</p>
<p>It finally felt like the same workspace, just with a different login.</p>
<p>And honestly, that is probably how it should have worked in the first place.</p>
]]></content:encoded></item><item><title><![CDATA[Why Most AI Coding Workflows Fail — and How Mine Cut 2 Weeks of Work to 2 Days]]></title><description><![CDATA[Strong suggestion: don’t copy my workflow — read through the failures, mistakes, and learnings, and use them to craft a workflow that works for you.
If you want to jump straight to my current workflow, go here →👉 Iteration 7: My Current Workflow (Wh...]]></description><link>https://blogs.navayuvan.com/why-most-ai-coding-workflows-fail-and-how-mine-cut-2-weeks-of-work-to-2-days</link><guid isPermaLink="true">https://blogs.navayuvan.com/why-most-ai-coding-workflows-fail-and-how-mine-cut-2-weeks-of-work-to-2-days</guid><category><![CDATA[Developer]]></category><category><![CDATA[AI]]></category><category><![CDATA[coding]]></category><category><![CDATA[cursor]]></category><category><![CDATA[cursor ai]]></category><category><![CDATA[software development]]></category><category><![CDATA[Software Engineering]]></category><category><![CDATA[software architecture]]></category><dc:creator><![CDATA[Navayuvan Subramanian]]></dc:creator><pubDate>Tue, 23 Dec 2025 04:36:22 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1766463176811/a59c3e08-b6be-4d37-9506-980ca0bdb04f.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<blockquote>
<p><strong>Strong suggestion: don’t copy my workflow — read through the failures, mistakes, and learnings, and use them to craft a workflow that works for you.</strong></p>
<p>If you want to jump straight to my <strong>current workflow</strong>, go here →<br />👉 <a target="_blank" href="https://blogs.navayuvan.dev/why-most-ai-coding-workflows-fail-and-how-mine-cut-2-weeks-of-work-to-2-days#heading-iteration-7-my-current-workflow-what-finally-worked">Iteration 7: My Current Workflow (What Finally Worked)</a></p>
</blockquote>
<p>I’ve been thinking about writing this for a while.</p>
<p>I’m still learning, but over the past year, I’ve been coding with AI almost every day — both in personal projects and in production systems at work. I’m not claiming this is <em>the</em> best way to use AI, but this is the workflow that helped me compress what usually takes <strong>three weeks into two days</strong>.</p>
<p>This blog isn’t about a single “aha” moment.<br />It’s about <strong>iterations</strong> — where things worked, where they broke, and what each failure taught me.</p>
<hr />
<h2 id="heading-iteration-1-ai-felt-like-magic">Iteration 1: AI Felt Like Magic</h2>
<p>When I first started coding with AI, I was honestly impressed.</p>
<p>Things that used to take real mental effort were getting done effortlessly:</p>
<ul>
<li><p>Solving a LeetCode problem</p>
</li>
<li><p>Creating Simple UI with React and Stylings</p>
</li>
<li><p>Writing a simple API to CRUD a model</p>
</li>
</ul>
<p>AI handled all of this extremely well.</p>
<p>At this stage, I was mostly using Cursor for <strong>personal projects</strong>. These projects were small, the codebase was tiny, and the AI had very little context to reason about.</p>
<p>Naturally, I assumed:</p>
<blockquote>
<p>“If this works so well for personal projects, it should work even better for office projects.”</p>
</blockquote>
<p>That assumption didn’t last long.</p>
<hr />
<h2 id="heading-iteration-2-production-codebases-change-everything">Iteration 2: Production Codebases Change Everything</h2>
<p>Around 6–8 months ago, my company gave me a paid Cursor license.<br />I started using it on our <strong>actual product codebase</strong>.</p>
<p>The difference was immediate.</p>
<p>Same prompts.<br />Same workflow.<br />Completely different results.</p>
<p>In personal projects, the AI was dealing with hundreds or a few thousand lines of code.<br />In production, it had to reason about <strong>tens of thousands of lines</strong>, multiple modules, and years of architectural decisions.</p>
<p>That’s when things started breaking.</p>
<hr />
<h2 id="heading-iteration-3-ask-plan-build-worked-until-it-didnt">Iteration 3: Ask → Plan → Build (Worked… Until It Didn’t)</h2>
<p>To regain control, I introduced structure.</p>
<p>Before implementing any feature, I started doing this:</p>
<ol>
<li><p>Switch Cursor to <strong>Ask mode</strong></p>
</li>
<li><p>Ask it to understand specific folders or modules</p>
</li>
<li><p>Explain the feature I wanted to build</p>
</li>
<li><p>Ask it to give me a <strong>plan</strong></p>
</li>
<li><p>Switch to <strong>Agent mode</strong> and ask it to implement the plan</p>
</li>
</ol>
<p>This was before Cursor even had a dedicated Plan mode.</p>
<p>And honestly — it worked really well.</p>
<p>I completed multiple backend-heavy features using this approach, and I was genuinely surprised by how well the AI understood the code and executed changes.</p>
<p>Until I hit a full-stack feature.</p>
<hr />
<h2 id="heading-iteration-4-full-stack-complexity-broke-the-workflow">Iteration 4: Full-Stack Complexity Broke the Workflow</h2>
<p>The next feature involved everything:</p>
<ul>
<li><p>Frontend</p>
</li>
<li><p>Backend</p>
</li>
<li><p>Queues and background jobs</p>
</li>
<li><p>Socket events</p>
</li>
<li><p>Real-time UI updates</p>
</li>
<li><p>State management</p>
</li>
</ul>
<p>The number of moving parts exploded.</p>
<p>I followed the same workflow.</p>
<p>The output was bad.</p>
<ul>
<li><p>TypeScript types replaced with <code>any</code> and <code>unknown</code></p>
</li>
<li><p>Method signature mismatches</p>
</li>
<li><p>Code written just to “make it work”</p>
</li>
<li><p>Hidden bugs everywhere</p>
</li>
</ul>
<p>That’s when I realized something important:</p>
<blockquote>
<p>The workflow didn’t change.<br />The <strong>task complexity did</strong>.</p>
</blockquote>
<hr />
<h2 id="heading-iteration-5-treating-ai-like-a-fresh-developer">Iteration 5: Treating AI Like a Fresh Developer</h2>
<p>I asked myself a simple question:</p>
<blockquote>
<p>“If I were giving this task to a fresh developer, how would I do it?”</p>
</blockquote>
<p>I wouldn’t explain everything at once.</p>
<p>So I split the feature into <strong>independent chunks</strong>:</p>
<ol>
<li><p>Backend</p>
</li>
<li><p>Frontend</p>
</li>
</ol>
<p>The backend agent didn’t know about the frontend.<br />The frontend agent only knew the API contract.</p>
<p>For the backend, I gave very clear instructions:</p>
<ul>
<li><p>Queue behaviour</p>
</li>
<li><p>API design</p>
</li>
<li><p>Event flow</p>
</li>
</ul>
<p>Then I handed the API contract to the frontend.</p>
<p>This worked <strong>beautifully</strong>.</p>
<p>Until I tried spinning up a new microservice.</p>
<hr />
<h2 id="heading-iteration-6-plan-mode-enters-and-still-fails-at-scale">Iteration 6: Plan Mode Enters (And Still Fails at Scale)</h2>
<p>Around this time, <strong>Cursor launched Plan mode</strong>, and naturally, I started using it heavily.</p>
<p>On paper, this felt like the missing piece.</p>
<p>The idea was simple:</p>
<ul>
<li><p>Create a detailed plan in markdown</p>
</li>
<li><p>Let the agent refer to it instead of holding everything in memory</p>
</li>
<li><p>Execute step by step</p>
</li>
</ul>
<p>For medium-sized features, this worked <em>really</em> well.</p>
<p>But then I hit a much bigger task.</p>
<p>This feature involved:</p>
<ul>
<li><p>Creating a new microservice</p>
</li>
<li><p>Linking it with existing services</p>
</li>
<li><p>Implementing cutting edge tools</p>
</li>
<li><p>Adopting modern Node.js and TypeScript patterns</p>
</li>
<li><p>Handling cross-service communication</p>
</li>
</ul>
<p>I brainstormed deeply with the AI and created a <em>pixel-perfect plan</em>.</p>
<p>The plan itself was solid.</p>
<p>The execution wasn’t.</p>
<ul>
<li><p><code>any</code> everywhere</p>
</li>
<li><p>Interface mismatches</p>
</li>
<li><p>Build failures</p>
</li>
<li><p>Runtime issues</p>
</li>
</ul>
<p>That’s when I finally understood the real problem:</p>
<blockquote>
<p><strong>Even with Plan mode, a single massive plan is still too much context.</strong></p>
</blockquote>
<p>Plan mode helped — but it didn’t remove the need to <strong>break the problem down further</strong>.</p>
<hr />
<h2 id="heading-why-this-fails-even-for-humans">Why This Fails (Even for Humans)</h2>
<p>Imagine your CTO hands you:</p>
<ul>
<li><p>An 800–900 page tech requirement document, includes</p>
</li>
<li><p>Multiple microservices</p>
</li>
<li><p>Multiple protocols like Kafka or gRPC</p>
</li>
<li><p>Multiple external dependencies</p>
</li>
</ul>
<p>…and asks you to <strong>implement everything in a single go</strong>.</p>
<p>You wouldn’t be productive.</p>
<p>That’s why we have:</p>
<ul>
<li><p>Epics</p>
</li>
<li><p>User stories</p>
</li>
<li><p>Tasks</p>
</li>
</ul>
<p>But when it comes to AI, we forget this and treat it like a <strong>supernatural entity</strong>.</p>
<p>It’s not.</p>
<p>It has limits.</p>
<hr />
<h2 id="heading-iteration-7-my-current-workflow-what-finally-worked">Iteration 7: My Current Workflow (What Finally Worked)</h2>
<p>This is the workflow that actually stuck.</p>
<h3 id="heading-step-1-start-with-high-level-architecture">Step 1: Start With High-Level Architecture</h3>
<p>No function signatures.<br />No deep implementation details.</p>
<p>Just:</p>
<ul>
<li><p>Models</p>
</li>
<li><p>Responsibilities</p>
</li>
<li><p>Clear system boundaries</p>
</li>
</ul>
<hr />
<h3 id="heading-step-2-let-ai-propose-mid-level-phases">Step 2: Let AI Propose Mid-Level Phases</h3>
<p>I ask the AI to break the feature into phases.</p>
<p>Usually, I get something like:</p>
<ul>
<li><p>8 phases</p>
</li>
<li><p>Each phase with 4–5 bullet points</p>
</li>
</ul>
<p>Now the big feature is split into <strong>manageable chunks</strong>.</p>
<hr />
<h3 id="heading-step-3-create-a-separate-plan-for-each-phase">Step 3: Create a Separate Plan for Each Phase</h3>
<p>Each phase gets its own plan:</p>
<ul>
<li><p>Separate document</p>
</li>
<li><p>Less than ~500 lines</p>
</li>
<li><p>Focused and crisp</p>
</li>
</ul>
<p>A 5,000-line plan is no better than no plan.</p>
<hr />
<h3 id="heading-step-4-manual-review-is-mandatory">Step 4: Manual Review Is Mandatory</h3>
<p>Before building anything, I review each plan:</p>
<ul>
<li><p>Fix naming</p>
</li>
<li><p>Correct modeling flaws</p>
</li>
<li><p>Adjust structure</p>
</li>
</ul>
<p>You <strong>cannot blindly trust AI</strong>.<br />You still own the architecture.</p>
<hr />
<h3 id="heading-step-5-build-phase-by-phase">Step 5: Build Phase by Phase</h3>
<p>Slow.<br />Deliberate.<br />Controlled.</p>
<p>Phase 1 → Build<br />Phase 2 → Build<br />…<br />Phase 8 → Build</p>
<p>This is where everything finally clicked.</p>
<hr />
<h2 id="heading-the-result">The Result</h2>
<p>The entire feature was completed in <strong>2–3 days</strong>.</p>
<p>Without this workflow, it would have easily taken <strong>weeks</strong>.</p>
<p>The code was:</p>
<ul>
<li><p>Clean</p>
</li>
<li><p>Strongly typed</p>
</li>
<li><p>No unnecessary <code>any</code></p>
</li>
<li><p>Easy to reason about and maintain</p>
</li>
</ul>
<hr />
<h2 id="heading-final-thoughts">Final Thoughts</h2>
<p>AI productivity gains are real — but only if you respect its limits.</p>
<p>Think of AI as:</p>
<blockquote>
<p>A very fast fresh developer who needs <strong>clear, scoped, precise instructions</strong></p>
</blockquote>
<p>This is my <strong>7th iteration</strong> of this workflow, and it’ll iterate as the complexity increases.</p>
<p>If you think I missed something, tell me.<br />If you’ve found a better approach, share it.</p>
<p>Let’s learn from each other and write <strong>better code with AI</strong>, not just more code.</p>
<p>Cheers!</p>
]]></content:encoded></item><item><title><![CDATA[TypeScript Won’t Save Your Product: The Case for Clean Code Practices]]></title><description><![CDATA[You think using TypeScript is gonna keep your product alive, definitely NOT 😂 Here's why!
People are obsessed with TypeScript, and I don't say it's wrong.
But when it's used incorrectly (without proper clean code principles), even it'll cause a disa...]]></description><link>https://blogs.navayuvan.com/typescript-wont-save-your-product-the-case-for-clean-code-practices</link><guid isPermaLink="true">https://blogs.navayuvan.com/typescript-wont-save-your-product-the-case-for-clean-code-practices</guid><category><![CDATA[TypeScript]]></category><category><![CDATA[clean code]]></category><category><![CDATA[Programming Tips]]></category><category><![CDATA[Software Engineering]]></category><category><![CDATA[developer productivity]]></category><dc:creator><![CDATA[Navayuvan Subramanian]]></dc:creator><pubDate>Sat, 14 Dec 2024 15:39:02 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1734190679040/f00c64e3-e032-43ae-b9f1-968160be8816.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>You think using TypeScript is gonna keep your product alive, definitely NOT 😂 Here's why!</p>
<p>People are obsessed with TypeScript, and I don't say it's wrong.</p>
<p>But when it's used incorrectly (without proper clean code principles), even it'll cause a disaster instead of saving us.</p>
<p>Let me give you an example:</p>
<h3 id="heading-the-problem">The Problem</h3>
<pre><code class="lang-typescript"><span class="hljs-keyword">const</span> getLogs = <span class="hljs-function">(<span class="hljs-params">
  userId: <span class="hljs-built_in">string</span>,
  <span class="hljs-keyword">from</span>?: <span class="hljs-built_in">Date</span>,
  to?: <span class="hljs-built_in">Date</span>,
  sort?: <span class="hljs-built_in">Map</span>&lt;<span class="hljs-built_in">string</span>, <span class="hljs-built_in">number</span>&gt;,
  limit?: <span class="hljs-built_in">number</span>,
  lastLogId?: <span class="hljs-built_in">string</span>
</span>) =&gt;</span> {};
</code></pre>
<p>This function might look fine at first glance, but it's not.</p>
<p>Now think about the consumer:</p>
<p>How will they know the order of all those optional parameters?</p>
<p>Having this many parameters in a function not only makes it harder to maintain but also makes it prone to errors.</p>
<h3 id="heading-the-solution">The Solution</h3>
<p>Here's a cleaner approach:</p>
<pre><code class="lang-typescript"><span class="hljs-keyword">interface</span> LogsOptions {
  interval?: {
    <span class="hljs-keyword">from</span>?: <span class="hljs-built_in">Date</span>;
    to?: <span class="hljs-built_in">Date</span>;
  };
  pagination?: {
    limit?: <span class="hljs-built_in">number</span>;
    lastLogId?: <span class="hljs-built_in">string</span>;
  };
  sort?: <span class="hljs-built_in">Map</span>&lt;<span class="hljs-built_in">string</span>, <span class="hljs-built_in">number</span>&gt;;
}

<span class="hljs-keyword">const</span> getLogs = <span class="hljs-function">(<span class="hljs-params">userId: <span class="hljs-built_in">string</span>, options?: LogsOptions</span>) =&gt;</span> {};
</code></pre>
<p>This approach is:<br />✅ Cleaner<br />✅ Easier to maintain<br />✅ Less prone to errors</p>
<p>Consumers can immediately understand what parameters are available without guessing.</p>
<h3 id="heading-moral-of-the-story">Moral of the story</h3>
<p>TypeScript alone won't save your product. Regardless of the language or framework you use, clean code principles play a vital role in your product's longevity.</p>
<p>Frameworks and languages come and go, but the fundamentals stay the same.</p>
<p>💬 What practices do you follow to keep your product alive? Share your thoughts in the comments!</p>
<p>Cheers,<br /><a target="_blank" href="https://navayuvan.dev">Navayuvan Subramanian</a></p>
]]></content:encoded></item><item><title><![CDATA[Why Small PRs Aren’t Always the Best Solution: Lessons from Startup Development]]></title><description><![CDATA[We often hear experts say, "Compose small PRs! The smaller the PRs, the easier they are to understand." But that's not always right! 🤔
I used to believe that small PRs were the cleanest and best way to develop.
But in recent times, I've realized thi...]]></description><link>https://blogs.navayuvan.com/why-small-prs-arent-always-the-best-solution-lessons-from-startup-development</link><guid isPermaLink="true">https://blogs.navayuvan.com/why-small-prs-arent-always-the-best-solution-lessons-from-startup-development</guid><category><![CDATA[software development]]></category><category><![CDATA[code review]]></category><category><![CDATA[Programming Tips]]></category><category><![CDATA[Developer]]></category><category><![CDATA[GitHub]]></category><category><![CDATA[Git]]></category><dc:creator><![CDATA[Navayuvan Subramanian]]></dc:creator><pubDate>Sat, 14 Dec 2024 15:33:07 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1734190294915/82627702-c9c0-4e8d-a433-f64cc9d526da.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>We often hear experts say, "<strong><em>Compose small PRs! The smaller the PRs, the easier they are to understand.</em></strong>" But that's not always right! 🤔</p>
<p>I used to believe that small PRs were the cleanest and best way to develop.</p>
<p>But in recent times, I've realized this approach doesn't always work.</p>
<p>This might be effective for companies moving at a slower pace.</p>
<p>However, for startups pushing out at least one release a week, this can slow things down significantly. 🐢</p>
<p>In most startups, developers own large chunks of features.</p>
<p>Splitting these into smaller tasks and raising separate PRs not only wastes time but also makes reviewers lose context about the feature as a whole. 😵‍💫</p>
<p>But then, reviewing a PR with 100+ file changes is no walk in the park either. So, what's the solution?</p>
<p>Here's what works for me:</p>
<h3 id="heading-provide-a-clear-description-of-the-changes">Provide a clear description of the changes.</h3>
<p>You don't need to be overly detailed, but a high-level summary of the changes goes a long way.</p>
<h3 id="heading-avoid-unnecessary-changes">Avoid unnecessary changes</h3>
<p>Skip alignment changes or edits unrelated to the feature you're working on.</p>
<h3 id="heading-annotate-unrelated-changes">Annotate unrelated changes</h3>
<p>If some unrelated changes are unavoidable, add comments explaining why they're included. This helps reviewers stay on track.</p>
<h3 id="heading-streamline-priority-reviews">Streamline priority reviews.</h3>
<p>Let's be real---no one jumps to review a massive PR immediately. If it's a priority, set up a quick call with the reviewers to explain the changes. It makes the review process faster and more efficient.</p>
<h3 id="heading-respect-the-reviewers-time">Respect the reviewer's time</h3>
<p>Ask your reviewers when they can reasonably review the PR and follow up after the deadline. Respecting their time is key.</p>
<h3 id="heading-follow-up-without-annoying">Follow up without annoying</h3>
<p>Reviewers have their own priorities, and forgetting your PR is natural. Gently remind them without being pushy to get it reviewed.</p>
<hr />
<p>This is how I approach or request PR reviews at my workplace.</p>
<p>How do you handle your PR reviews? Let me know in the comments! 💬</p>
<p>And don't forget to follow <a target="_blank" href="https://navayuvan.dev">Navayuvan Subramanian</a> for more tech insights! 🚀</p>
]]></content:encoded></item><item><title><![CDATA[Know what is Developer's Bias and stay away from it!]]></title><description><![CDATA[We developers have a problem: we think we can build anything and use it ourselves instead of paying for it. But that's not correct! 🚫
Let me make it clear with an example: Todoist.
It's a simple to-do app with a monthly subscription of $5.
Now, we m...]]></description><link>https://blogs.navayuvan.com/know-what-is-developers-bias-and-stay-away-from-it</link><guid isPermaLink="true">https://blogs.navayuvan.com/know-what-is-developers-bias-and-stay-away-from-it</guid><category><![CDATA[Productivity]]></category><category><![CDATA[development]]></category><dc:creator><![CDATA[Navayuvan Subramanian]]></dc:creator><pubDate>Sat, 14 Dec 2024 15:23:25 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1734189683831/8f2c7bdd-b325-4f37-822a-b9292fadac1e.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>We developers have a problem: we think we can build anything and use it ourselves instead of paying for it. But that's not correct! 🚫</p>
<p>Let me make it clear with an example: <a target="_blank" href="https://todoist.com/">Todoist</a>.</p>
<p>It's a simple to-do app with a monthly subscription of $5.</p>
<p>Now, we might think, "I can develop that app myself." But, hear me out!</p>
<p>The payment we provide them includes:<br />👉 The research they've done to make a feature usable.<br />👉 The efforts they've put in to integrate with other applications. (Slack, G-Cal, etc.)<br />👉 Client app support across multiple platforms (browser extensions, desktop, web apps, etc.).<br />👉 Constant improvement of existing features or the development of new ones.<br />👉 And many more.</p>
<p>Now, can we do all of this consistently just to save $5? Absolutely not! 🙅‍♂️</p>
<p>The efforts involved in doing these are enormous, and there's an entire company dedicated to it. So, instead of building it ourselves, paying for the subscription seems like a smarter choice. 💡</p>
<p>Does that mean we should never build an app for ourselves? Not at all. 🙌</p>
<p>Here's when it makes sense to build the app ourselves:<br />- When there are no apps on the market suitable for our needs.<br />- When the amount we'd pay for an app is less than or equal to the value of the time we save by not developing it ourselves.<br />- When the existing apps in the market have major pain points, solving them could even lead to a new startup or business idea.</p>
<p>If any of these criteria are met, then building it ourselves might be the way to go! 🚀</p>
<p>In the end, it's all about balancing time and effort. ⚖️</p>
<p>So, the next time you think about building an app yourself, take a moment to reflect on whether it's really worth the time and effort you're about to invest. Then, decide wisely! ✅</p>
<p>And, don't forget to follow me. I'll be dropping some interesting stuffs soon 😉</p>
<p>Your fellow developer,<br /><a target="_blank" href="https://navayuvan.dev">Navayuvan Subramanian</a></p>
]]></content:encoded></item><item><title><![CDATA[Efficient API Consumption in React TypeScript]]></title><description><![CDATA[APIs are playing an important role in software development today. In the world of cloud computing, no application can function without APIs. The way you architect and consume these APIs can significantly impact your app's performance.
Let’s explore b...]]></description><link>https://blogs.navayuvan.com/efficient-api-consumption-in-react-typescript</link><guid isPermaLink="true">https://blogs.navayuvan.com/efficient-api-consumption-in-react-typescript</guid><category><![CDATA[React]]></category><category><![CDATA[React Native]]></category><category><![CDATA[JavaScript]]></category><category><![CDATA[TypeScript]]></category><category><![CDATA[react-query]]></category><category><![CDATA[axios]]></category><dc:creator><![CDATA[Navayuvan Subramanian]]></dc:creator><pubDate>Sat, 24 Aug 2024 10:04:04 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1724504050330/0823aac9-f399-459a-ad36-02bee841b3aa.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>APIs are playing an important role in software development today. In the world of cloud computing, no application can function without APIs. The way you architect and consume these APIs can significantly impact your app's performance.</p>
<p>Let’s explore building a robust HTTP Client in React TypeScript, integrating caching with Tanstack Query (formerly React Query), and creating custom hooks that streamline your API calls.</p>
<h2 id="heading-understanding-http-apis">🌐 Understanding HTTP APIs</h2>
<p>HTTP (HyperText Transfer Protocol) is the universal language systems use to communicate. Whether it’s fetching data, updating a resource, or deleting something from the server, HTTP has got us covered. Here’s a quick refresher on the HTTP methods:</p>
<ul>
<li><p><strong>GET</strong>: Grabs the data you need from the server.</p>
</li>
<li><p><strong>POST</strong>: Sends data to create something new on the server.</p>
</li>
<li><p><strong>PUT</strong>: Updates an existing resource entirely.</p>
</li>
<li><p><strong>PATCH</strong>: Updates a part of an existing resource.</p>
</li>
<li><p><strong>DELETE</strong>: Removes something from the server.</p>
</li>
</ul>
<p>Each method is like a tool in your toolbox—pick the right one for the job, and your APIs will be a joy to work with.</p>
<h2 id="heading-why-axios">💡 Why Axios?</h2>
<p>So, why are we using <strong>Axios</strong> for our HTTP Client? Imagine having a personal assistant who handles all the boring tasks like adding headers, dealing with JSON, and managing timeouts—Axios does just that! It’s a library that simplifies HTTP requests, making our code cleaner and easier to maintain.</p>
<h2 id="heading-creating-the-http-client">🛠️ Creating the HTTP Client</h2>
<h3 id="heading-step-1-installing-axios">Step 1: Installing Axios</h3>
<p>Before we dive into the fun stuff, let’s install Axios:</p>
<pre><code class="lang-bash">npm install axios
</code></pre>
<h3 id="heading-step-2-setting-up-the-http-client">Step 2: Setting Up the HTTP Client</h3>
<p>Now, let’s set up our HTTP Client to make API calls a breeze.</p>
<pre><code class="lang-typescript"><span class="hljs-keyword">import</span> axios, { AxiosInstance, AxiosRequestConfig, AxiosResponse } <span class="hljs-keyword">from</span> <span class="hljs-string">"axios"</span>;

<span class="hljs-keyword">class</span> HttpClient {
  <span class="hljs-keyword">private</span> instance: AxiosInstance;

  <span class="hljs-keyword">constructor</span>(<span class="hljs-params">baseURL: <span class="hljs-built_in">string</span></span>) {
    <span class="hljs-built_in">this</span>.instance = axios.create({
      baseURL,
      withCredentials: <span class="hljs-literal">true</span>,
    });

    <span class="hljs-built_in">this</span>.instance.interceptors.request.use(<span class="hljs-built_in">this</span>.handleRequest);
    <span class="hljs-built_in">this</span>.instance.interceptors.response.use(<span class="hljs-built_in">this</span>.handleResponse);
  }

  <span class="hljs-keyword">private</span> handleRequest = (config: AxiosRequestConfig): <span class="hljs-function"><span class="hljs-params">AxiosRequestConfig</span> =&gt;</span> {
    <span class="hljs-keyword">const</span> token = <span class="hljs-built_in">localStorage</span>.getItem(<span class="hljs-string">"authToken"</span>); <span class="hljs-comment">// Replace with your token logic</span>
    <span class="hljs-keyword">if</span> (token) {
      <span class="hljs-keyword">if</span> (!config.headers) {
        config.headers = {
          Authorization: <span class="hljs-string">`Bearer <span class="hljs-subst">${token}</span>`</span>,
        };
        <span class="hljs-keyword">return</span> config;
      }

      config.headers[<span class="hljs-string">"Authorization"</span>] = <span class="hljs-string">`Bearer <span class="hljs-subst">${token}</span>`</span>;
    }
    <span class="hljs-keyword">return</span> config;
  };

  <span class="hljs-keyword">private</span> handleResponse = (response: AxiosResponse): <span class="hljs-function"><span class="hljs-params">AxiosResponse</span> =&gt;</span> {
    <span class="hljs-keyword">return</span> response;
  };

  <span class="hljs-keyword">public</span> get&lt;T&gt;(
    url: <span class="hljs-built_in">string</span>,
    config?: AxiosRequestConfig
  ): <span class="hljs-built_in">Promise</span>&lt;AxiosResponse&lt;T&gt;&gt; {
    <span class="hljs-keyword">return</span> <span class="hljs-built_in">this</span>.instance.get&lt;T&gt;(url, config);
  }

  <span class="hljs-keyword">public</span> post&lt;T&gt;(
    url: <span class="hljs-built_in">string</span>,
    data: <span class="hljs-built_in">any</span>,
    config?: AxiosRequestConfig
  ): <span class="hljs-built_in">Promise</span>&lt;AxiosResponse&lt;T&gt;&gt; {
    <span class="hljs-keyword">return</span> <span class="hljs-built_in">this</span>.instance.post&lt;T&gt;(url, data, config);
  }

  <span class="hljs-keyword">public</span> put&lt;T&gt;(
    url: <span class="hljs-built_in">string</span>,
    data: <span class="hljs-built_in">any</span>,
    config?: AxiosRequestConfig
  ): <span class="hljs-built_in">Promise</span>&lt;AxiosResponse&lt;T&gt;&gt; {
    <span class="hljs-keyword">return</span> <span class="hljs-built_in">this</span>.instance.put&lt;T&gt;(url, data, config);
  }

  <span class="hljs-keyword">public</span> patch&lt;T&gt;(
    url: <span class="hljs-built_in">string</span>,
    data: <span class="hljs-built_in">any</span>,
    config?: AxiosRequestConfig
  ): <span class="hljs-built_in">Promise</span>&lt;AxiosResponse&lt;T&gt;&gt; {
    <span class="hljs-keyword">return</span> <span class="hljs-built_in">this</span>.instance.patch&lt;T&gt;(url, data, config);
  }

  <span class="hljs-keyword">public</span> <span class="hljs-keyword">delete</span>&lt;T&gt;(
    url: <span class="hljs-built_in">string</span>,
    config?: AxiosRequestConfig
  ): <span class="hljs-built_in">Promise</span>&lt;AxiosResponse&lt;T&gt;&gt; {
    <span class="hljs-keyword">return</span> <span class="hljs-built_in">this</span>.instance.delete&lt;T&gt;(url, config);
  }
}

<span class="hljs-keyword">const</span> httpClient = <span class="hljs-keyword">new</span> HttpClient(<span class="hljs-string">"https://jsonplaceholder.typicode.com"</span>);
<span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> httpClient;
</code></pre>
<h3 id="heading-create-a-user-model">Create a User Model</h3>
<pre><code class="lang-typescript"><span class="hljs-keyword">export</span> <span class="hljs-keyword">interface</span> Address {
  street: <span class="hljs-built_in">string</span>;
  suite: <span class="hljs-built_in">string</span>;
  city: <span class="hljs-built_in">string</span>;
  zipcode: <span class="hljs-built_in">string</span>;
  geo: {
    lat: <span class="hljs-built_in">string</span>;
    lng: <span class="hljs-built_in">string</span>;
  };
}

<span class="hljs-keyword">export</span> <span class="hljs-keyword">interface</span> Company {
  name: <span class="hljs-built_in">string</span>;
  catchPhrase: <span class="hljs-built_in">string</span>;
  bs: <span class="hljs-built_in">string</span>;
}

<span class="hljs-keyword">export</span> <span class="hljs-keyword">interface</span> User {
  id: <span class="hljs-built_in">number</span>;
  name: <span class="hljs-built_in">string</span>;
  username: <span class="hljs-built_in">string</span>;
  email: <span class="hljs-built_in">string</span>;
  address: Address;
  phone: <span class="hljs-built_in">string</span>;
  website: <span class="hljs-built_in">string</span>;
  company: Company;
}
</code></pre>
<h2 id="heading-handling-api-requests">🔄 Handling API Requests</h2>
<h3 id="heading-creating-a-user">Creating a User</h3>
<pre><code class="lang-typescript"><span class="hljs-keyword">const</span> createUser = <span class="hljs-keyword">async</span> (userData: UserData) =&gt; {
  <span class="hljs-keyword">const</span> response = <span class="hljs-keyword">await</span> httpClient.post&lt;User&gt;(<span class="hljs-string">'/users'</span>, userData);
  <span class="hljs-keyword">return</span> response.data;
};
</code></pre>
<h3 id="heading-fetching-users">Fetching Users</h3>
<pre><code class="lang-typescript"><span class="hljs-keyword">const</span> fetchUsers = <span class="hljs-keyword">async</span> () =&gt; {
  <span class="hljs-keyword">const</span> response = <span class="hljs-keyword">await</span> httpClient.get&lt;User[]&gt;(<span class="hljs-string">'/users'</span>);
  <span class="hljs-keyword">return</span> response.data;
};
</code></pre>
<h3 id="heading-updating-a-user">Updating a User</h3>
<pre><code class="lang-typescript"><span class="hljs-keyword">const</span> updateUser = <span class="hljs-keyword">async</span> (userId: <span class="hljs-built_in">string</span>, userData: UserData) =&gt; {
  <span class="hljs-keyword">const</span> response = <span class="hljs-keyword">await</span> httpClient.put&lt;User&gt;(<span class="hljs-string">`/users/<span class="hljs-subst">${userId}</span>`</span>, userData);
  <span class="hljs-keyword">return</span> response.data;
};
</code></pre>
<h3 id="heading-deleting-a-user">Deleting a User</h3>
<pre><code class="lang-typescript"><span class="hljs-keyword">const</span> deleteUser = <span class="hljs-keyword">async</span> (userId: <span class="hljs-built_in">string</span>) =&gt; {
  <span class="hljs-keyword">const</span> response = <span class="hljs-keyword">await</span> httpClient.delete(<span class="hljs-string">`/users/<span class="hljs-subst">${userId}</span>`</span>);
  <span class="hljs-keyword">return</span> response.data;
};
</code></pre>
<h2 id="heading-the-power-of-caching-with-tanstack-query">🗄️ The Power of Caching with Tanstack Query</h2>
<p>When consuming APIs, one of the biggest challenges is managing the state of the data—especially when it comes to caching. Caching allows us to store responses so that repeated requests for the same data can be served faster, improving the overall performance of our application. This is where <strong>Tanstack Query</strong> comes in. Tanstack Query is a powerful data-fetching library that makes it simple to manage server-state in your React apps, with built-in caching, synchronization, and more.</p>
<h3 id="heading-installation">Installation</h3>
<p>To get started, install Tanstack Query:</p>
<pre><code class="lang-bash">npm install @tanstack/react-query
</code></pre>
<h2 id="heading-creating-custom-hooks-for-api-requests">⚙️ Creating Custom Hooks for API Requests</h2>
<p>Creating custom hooks makes your API calls reusable and easier to manage. For each type of request (fetch, create, update, delete), we'll create a custom hook using Tanstack Query and the HttpClient we built earlier.</p>
<h3 id="heading-setting-up-query-keys">Setting Up Query Keys</h3>
<p>First, let's define some constants for our query keys:</p>
<pre><code class="lang-typescript"><span class="hljs-comment">// queryKeys.ts</span>
<span class="hljs-keyword">export</span> <span class="hljs-keyword">const</span> QUERY_KEYS = {
  USERS: <span class="hljs-string">'users'</span>,
  USER: <span class="hljs-function">(<span class="hljs-params">userId: <span class="hljs-built_in">string</span></span>) =&gt;</span> [<span class="hljs-string">'user'</span>, userId],
};
</code></pre>
<h3 id="heading-custom-hooks-examples">Custom Hooks Examples</h3>
<h4 id="heading-fetching-users-1">Fetching Users</h4>
<pre><code class="lang-typescript"><span class="hljs-keyword">import</span> { useQuery } <span class="hljs-keyword">from</span> <span class="hljs-string">'@tanstack/react-query'</span>;
<span class="hljs-keyword">import</span> httpClient <span class="hljs-keyword">from</span> <span class="hljs-string">'../api/HttpClient'</span>;
<span class="hljs-keyword">import</span> { QUERY_KEYS } <span class="hljs-keyword">from</span> <span class="hljs-string">'../api/queryKeys'</span>;
<span class="hljs-keyword">import</span> { User } <span class="hljs-keyword">from</span> <span class="hljs-string">'../api/interfaces'</span>;

<span class="hljs-keyword">const</span> useFetchUsers = <span class="hljs-function">() =&gt;</span> {
  <span class="hljs-keyword">return</span> useQuery&lt;User[]&gt;({
    queryKey: [QUERY_KEYS.USERS],
    queryFn: <span class="hljs-function">() =&gt;</span> httpClient.get&lt;User[]&gt;(<span class="hljs-string">"/users"</span>).then(<span class="hljs-function">(<span class="hljs-params">res</span>) =&gt;</span> res.data),
  });
};
</code></pre>
<h4 id="heading-creating-a-user-1">Creating a User</h4>
<pre><code class="lang-typescript"><span class="hljs-keyword">import</span> { useMutation, useQueryClient } <span class="hljs-keyword">from</span> <span class="hljs-string">'@tanstack/react-query'</span>;
<span class="hljs-keyword">import</span> httpClient <span class="hljs-keyword">from</span> <span class="hljs-string">'../api/HttpClient'</span>;
<span class="hljs-keyword">import</span> { QUERY_KEYS } <span class="hljs-keyword">from</span> <span class="hljs-string">'../api/queryKeys'</span>;
<span class="hljs-keyword">import</span> { User } <span class="hljs-keyword">from</span> <span class="hljs-string">'../api/interfaces'</span>;

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

  <span class="hljs-keyword">return</span> useMutation({
    mutationFn: <span class="hljs-function">(<span class="hljs-params">newUser: Omit&lt;User, <span class="hljs-string">"id"</span>&gt;</span>) =&gt;</span>
      httpClient.post&lt;User&gt;(<span class="hljs-string">"/users"</span>, newUser),
    onSuccess: <span class="hljs-function">() =&gt;</span> {
      queryClient.invalidateQueries({ queryKey: [QUERY_KEYS.USERS] });
    },
  });
};
</code></pre>
<h4 id="heading-updating-a-user-1">Updating a User</h4>
<pre><code class="lang-typescript"><span class="hljs-keyword">import</span> { useMutation, useQueryClient } <span class="hljs-keyword">from</span> <span class="hljs-string">'@tanstack/react-query'</span>;
<span class="hljs-keyword">import</span> httpClient <span class="hljs-keyword">from</span> <span class="hljs-string">'../api/HttpClient'</span>;
<span class="hljs-keyword">import</span> { QUERY_KEYS } <span class="hljs-keyword">from</span> <span class="hljs-string">'../api/queryKeys'</span>;
<span class="hljs-keyword">import</span> { User } <span class="hljs-keyword">from</span> <span class="hljs-string">'../api/interfaces'</span>;

<span class="hljs-keyword">const</span> useUpdateUser = <span class="hljs-function">(<span class="hljs-params">userId: <span class="hljs-built_in">number</span></span>) =&gt;</span> {
  <span class="hljs-keyword">const</span> queryClient = useQueryClient();

  <span class="hljs-keyword">return</span> useMutation({
    mutationFn: <span class="hljs-function">(<span class="hljs-params">updatedUser: Partial&lt;Omit&lt;User, <span class="hljs-string">"id"</span>&gt;&gt;</span>) =&gt;</span>
      httpClient.put&lt;User&gt;(<span class="hljs-string">`/users/<span class="hljs-subst">${userId}</span>`</span>, updatedUser),
    onSuccess: <span class="hljs-function">() =&gt;</span> {
      queryClient.invalidateQueries({ queryKey: [QUERY_KEYS.USERS] });
      queryClient.invalidateQueries({
        queryKey: [QUERY_KEYS.USER(userId.toString())],
      });
    },
  });
};
</code></pre>
<h4 id="heading-deleting-a-user-1">Deleting a User</h4>
<pre><code class="lang-typescript"><span class="hljs-keyword">import</span> { useMutation, useQueryClient } <span class="hljs-keyword">from</span> <span class="hljs-string">'@tanstack/react-query'</span>;
<span class="hljs-keyword">import</span> httpClient <span class="hljs-keyword">from</span> <span class="hljs-string">'../api/HttpClient'</span>;
<span class="hljs-keyword">import</span> { QUERY_KEYS } <span class="hljs-keyword">from</span> <span class="hljs-string">'../api/queryKeys'</span>;

<span class="hljs-keyword">const</span> useDeleteUser = <span class="hljs-function">(<span class="hljs-params">userId: <span class="hljs-built_in">number</span></span>) =&gt;</span> {
  <span class="hljs-keyword">const</span> queryClient = useQueryClient();

  <span class="hljs-keyword">return</span> useMutation({
    mutationFn: <span class="hljs-function">() =&gt;</span> httpClient.delete(<span class="hljs-string">`/users/<span class="hljs-subst">${userId}</span>`</span>),
    onSuccess: <span class="hljs-function">() =&gt;</span> {
      queryClient.invalidateQueries({ queryKey: [QUERY_KEYS.USERS] });
    },
  });
};
</code></pre>
<h2 id="heading-consumption-of-hooks">Consumption of Hooks!</h2>
<p>Here's a sample of how you can consume those hooks. We're using <a target="_blank" href="https://jsonplaceholder.typicode.com/">jsonplaceholder</a> for mock APIs. You can see it in HttpClient file.</p>
<pre><code class="lang-typescript">
<span class="hljs-keyword">const</span> App: React.FC = <span class="hljs-function">() =&gt;</span> {
  <span class="hljs-keyword">const</span> { data: users } = useFetchUsers();
  <span class="hljs-keyword">const</span> createUser = useCreateUser();
  <span class="hljs-keyword">const</span> updateUser = useUpdateUser(<span class="hljs-number">1</span>); <span class="hljs-comment">// Example user ID</span>
  <span class="hljs-keyword">const</span> deleteUser = useDeleteUser(<span class="hljs-number">1</span>); <span class="hljs-comment">// Example user ID</span>

  <span class="hljs-keyword">return</span> (
      &lt;div&gt;
        &lt;h1&gt;Users&lt;/h1&gt;
        &lt;button onClick={<span class="hljs-function">() =&gt;</span> createUser.mutate(newUser)}&gt;Create User&lt;/button&gt;
        &lt;button
          onClick={<span class="hljs-function">() =&gt;</span>
            updateUser.mutate({
              name: <span class="hljs-string">"Updated User"</span>,
            })
          }
        &gt;
          Update User
        &lt;/button&gt;
        &lt;button onClick={<span class="hljs-function">() =&gt;</span> deleteUser.mutate()}&gt;Delete User&lt;/button&gt;

        &lt;ul&gt;
          {users?.map(<span class="hljs-function">(<span class="hljs-params">user: User</span>) =&gt;</span> (
            &lt;li key={user.id}&gt;
              {user.id} - {user.name}
            &lt;/li&gt;
          ))}
        &lt;/ul&gt;
      &lt;/div&gt;
  );
};
</code></pre>
<h2 id="heading-wrapping-it-up">📦 Wrapping It Up</h2>
<p>Efficiently consuming APIs is crucial for building performant and scalable applications. By setting up a robust HTTP Client with Axios, handling state with Tanstack Query, and creating custom hooks for each API request, you’ll be well on your way to mastering API consumption in React TypeScript.</p>
<p>Remember, the key is to keep your code modular and reusable. As your application grows, these practices will help you maintain a clean and efficient codebase.</p>
]]></content:encoded></item><item><title><![CDATA[Introducing you to Never Forget!]]></title><description><![CDATA[Motivation
Have you ever missed an important task because your reminder got buried in the avalanche of notifications? I used to struggle with this too, until I developed something that completely transformed how I manage tasks.
Like many of you, I re...]]></description><link>https://blogs.navayuvan.com/introducing-you-to-never-forget</link><guid isPermaLink="true">https://blogs.navayuvan.com/introducing-you-to-never-forget</guid><category><![CDATA[Productivity]]></category><category><![CDATA[Mobile Development]]></category><category><![CDATA[Flutter]]></category><category><![CDATA[TODOLIST]]></category><category><![CDATA[Todoist]]></category><category><![CDATA[Node.js]]></category><dc:creator><![CDATA[Navayuvan Subramanian]]></dc:creator><pubDate>Thu, 22 Aug 2024 02:00:14 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1724291742158/72a1b5d8-383d-412b-9479-43dc67f1233f.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<h1 id="heading-motivation">Motivation</h1>
<p>Have you ever missed an important task because your reminder got buried in the avalanche of notifications? I used to struggle with this too, until I developed something that completely transformed how I manage tasks.</p>
<p>Like many of you, I relied on standard reminder apps, but they just weren’t cutting it. Sure, they send notifications with a sound, but with the constant barrage of alerts, spotting the right one can feel like finding a needle in a haystack.</p>
<p>I needed something more—a reminder that not only grabs my attention but refuses to be ignored until I act on it. After searching for an app that could do this and coming up empty-handed, I decided to create my own solution.</p>
<p>While building a full-fledged app like Todoist was beyond my immediate scope, I developed a helper application to complement it: NeverForget.</p>
<p>The idea is simple yet powerful: when you add a task to Todoist with a date and time, NeverForget will notify you at the perfect moment.</p>
<p>But here’s the game-changer—the notification is persistent and can’t be cleared until you tap “Done.”</p>
<p>For the past four months, NeverForget has helped me remember and complete tasks I would have otherwise overlooked. 📝</p>
<p>Let me show you how it works.</p>
<h1 id="heading-how-to-add-reminders-in-neverforget">How to Add Reminders in NeverForget</h1>
<p>First things first: You can’t directly add and manage tasks in NeverForget. It’s designed to be a helper application.</p>
<p>If you’re a Todoist user, adding tasks with specific properties will trigger notifications in NeverForget. Here’s how you can set it up.</p>
<h2 id="heading-connect-todoist-with-neverforget">Connect Todoist with NeverForget</h2>
<ol>
<li><p>Download NeverForget from the Play Store and create an account or log in.</p>
</li>
<li><p>If you’re a new user, you’ll be prompted to "Connect to Todoist."</p>
</li>
<li><p>Click on that option and proceed to log into your Todoist account.</p>
</li>
<li><p>Once your accounts are connected, you’ll land on the Empty Home page.</p>
</li>
</ol>
<h2 id="heading-add-a-reminder-from-todoist-to-neverforget">Add a Reminder from Todoist to NeverForget</h2>
<ol>
<li><p>Open Todoist and create a label named "Reminder" or "Reminders."</p>
</li>
<li><p>To add a reminder, ensure your task meets two criteria:</p>
<ol>
<li><p>It includes the “Reminder” label we just created.</p>
</li>
<li><p>It has a due date and time to trigger the notification.</p>
</li>
</ol>
</li>
</ol>
<p>And that’s it! Once you’ve set this up, NeverForget will ensure you never miss an important task again.</p>
<h1 id="heading-whats-next">What’s Next?</h1>
<p>We’re excited to announce that more integrations, like Google Calendar, are on the way! Stay tuned as we continue to expand NeverForget’s capabilities, making it even easier to stay on top of your tasks across multiple platforms.</p>
]]></content:encoded></item></channel></rss>