<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: ramsha</title>
    <description>The latest articles on DEV Community by ramsha (@ramshakomal).</description>
    <link>https://dev.to/ramshakomal</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F3913688%2F5e3aa65b-e031-4494-be8b-5b09c13d5a38.jpeg</url>
      <title>DEV Community: ramsha</title>
      <link>https://dev.to/ramshakomal</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/ramshakomal"/>
    <language>en</language>
    <item>
      <title>JWT Authentication — 7 Common Mistakes Developers Make (And How to Fix Them)</title>
      <dc:creator>ramsha</dc:creator>
      <pubDate>Fri, 08 May 2026 19:44:39 +0000</pubDate>
      <link>https://dev.to/ramshakomal/jwt-authentication-7-common-mistakes-developers-make-and-how-to-fix-them-3l3l</link>
      <guid>https://dev.to/ramshakomal/jwt-authentication-7-common-mistakes-developers-make-and-how-to-fix-them-3l3l</guid>
      <description>&lt;p&gt;&lt;em&gt;I've seen these mistakes in codebases over and over again. Don't be that developer.&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Why JWT Gets Misused So Often
&lt;/h2&gt;

&lt;p&gt;JWT (JSON Web Tokens) looks simple on the surface. You generate a token, send it to the client, verify it on the server. Easy, right?&lt;br&gt;
Wrong.&lt;/p&gt;

&lt;p&gt;Most developers copy a tutorial, get it "working," and ship it to production without realizing they've left massive security holes open. I've been there too.&lt;/p&gt;

&lt;p&gt;Here are the 7 mistakes I see most often — and exactly how to fix them.&lt;/p&gt;
&lt;h2&gt;
  
  
  Mistake #1 — Storing JWT in localStorage
&lt;/h2&gt;

&lt;p&gt;This is probably the most common mistake and one of the most dangerous.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// ❌ WRONG — don't do this&lt;/span&gt;
&lt;span class="nx"&gt;localStorage&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;setItem&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;token&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;jwt&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Why it's dangerous:&lt;/strong&gt;&lt;br&gt;
localStorage is accessible by any JavaScript on the page. If your app has even one XSS vulnerability, an attacker can steal every user's token in seconds.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The fix:&lt;/strong&gt;&lt;br&gt;
Store your JWT in an &lt;strong&gt;httpOnly cookie&lt;/strong&gt; instead. JavaScript can't touch it.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// ✅ CORRECT — set it server side&lt;/span&gt;
&lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;cookie&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;token&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;jwt&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;httpOnly&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;secure&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;sameSite&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;strict&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;maxAge&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;24&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;60&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;60&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;1000&lt;/span&gt; &lt;span class="c1"&gt;// 1 day&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  Mistake #2 — No Token Expiration
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// ❌ WRONG — token lives forever&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;token&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;jwt&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;sign&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;userId&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;user&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;_id&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;JWT_SECRET&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A token with no expiry is a permanent key to your kingdom. If it gets stolen, the attacker has access forever.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The fix:&lt;/strong&gt;&lt;br&gt;
Always set an expiration. Short-lived tokens are safer.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// ✅ CORRECT&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;token&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;jwt&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;sign&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;userId&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;user&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;_id&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;JWT_SECRET&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;expiresIn&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;15m&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;For better UX, pair short-lived access tokens with &lt;strong&gt;refresh tokens&lt;/strong&gt;.&lt;/p&gt;




&lt;h2&gt;
  
  
  Mistake #3 — Weak or Hardcoded Secret Keys
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// ❌ WRONG&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;token&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;jwt&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;sign&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;payload&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;secret123&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;token&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;jwt&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;sign&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;payload&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;myappjwtsecret&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If your secret is weak or committed to GitHub, your entire auth system is broken.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The fix:&lt;/strong&gt;&lt;br&gt;
Use a long, random secret stored in environment variables only.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;node &lt;span class="nt"&gt;-e&lt;/span&gt; &lt;span class="s2"&gt;"console.log(require('crypto').randomBytes(64).toString('hex'))"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// ✅ CORRECT&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;token&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;jwt&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;sign&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;payload&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;JWT_SECRET&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Never commit your .env file to GitHub. Ever.&lt;/p&gt;

&lt;h2&gt;
  
  
  Mistake #4 — Not Verifying the Token Properly
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// ❌ WRONG — just decoding, not verifying&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;decoded&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;jwt&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;decode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;token&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;jwt.decode() just base64 decodes the token. It does NOT verify the signature. Anyone can forge a token and your app will accept it.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The fix:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// ✅ CORRECT&lt;/span&gt;
&lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;decoded&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;jwt&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;verify&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;token&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;JWT_SECRET&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;user&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;decoded&lt;/span&gt;
  &lt;span class="nf"&gt;next&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;catch &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;status&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;401&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;message&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Invalid token&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="err"&gt;##&lt;/span&gt; &lt;span class="nx"&gt;Mistake&lt;/span&gt; &lt;span class="err"&gt;#&lt;/span&gt;&lt;span class="mi"&gt;5&lt;/span&gt; &lt;span class="err"&gt;—&lt;/span&gt; &lt;span class="nx"&gt;Putting&lt;/span&gt; &lt;span class="nx"&gt;Sensitive&lt;/span&gt; &lt;span class="nx"&gt;Data&lt;/span&gt; &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="nx"&gt;the&lt;/span&gt; &lt;span class="nx"&gt;Payload&lt;/span&gt;

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

&lt;/div&gt;

&lt;p&gt;&lt;br&gt;
javascript&lt;br&gt;
// ❌ WRONG&lt;br&gt;
const token = jwt.sign({&lt;br&gt;
  userId: user._id,&lt;br&gt;
  password: user.password,&lt;br&gt;
  creditCard: user.cardNumber&lt;br&gt;
}, process.env.JWT_SECRET)&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;
JWT payloads are base64 encoded, not encrypted. Anyone can decode and read them.

**The fix:**

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

&lt;/div&gt;

&lt;p&gt;&lt;br&gt;
javascript&lt;br&gt;
// ✅ CORRECT — only what you need&lt;br&gt;
const token = jwt.sign({&lt;br&gt;
  userId: user._id,&lt;br&gt;
  role: user.role&lt;br&gt;
}, process.env.JWT_SECRET, { expiresIn: '15m' })&lt;br&gt;
&lt;/p&gt;

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

## Mistake #6 — No Refresh Token Strategy

Short access tokens expire fast. Most developers just make them last 30 days instead. That's the wrong solution.

**The fix:**
- Access token: 15 minutes
- Refresh token: 7-30 days, stored in httpOnly cookie

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

&lt;/div&gt;

&lt;p&gt;&lt;br&gt;
javascript&lt;br&gt;
app.post('/refresh', (req, res) =&amp;gt; {&lt;br&gt;
  const refreshToken = req.cookies.refreshToken&lt;br&gt;
  try {&lt;br&gt;
    const decoded = jwt.verify(refreshToken, process.env.REFRESH_SECRET)&lt;br&gt;
    const newAccessToken = jwt.sign(&lt;br&gt;
      { userId: decoded.userId },&lt;br&gt;
      process.env.JWT_SECRET,&lt;br&gt;
      { expiresIn: '15m' }&lt;br&gt;
    )&lt;br&gt;
    res.json({ accessToken: newAccessToken })&lt;br&gt;
  } catch {&lt;br&gt;
    res.status(401).json({ message: 'Please login again' })&lt;br&gt;
  }&lt;br&gt;
})&lt;br&gt;
&lt;/p&gt;

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

## Mistake #7 — Not Handling Token Revocation

Once issued, JWT can't be invalidated before expiry. If a user logs out, their token still works.

**The fix:**
Maintain a token blacklist in Redis.

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

&lt;/div&gt;

&lt;p&gt;&lt;br&gt;
javascript&lt;br&gt;
// On logout&lt;br&gt;
app.post('/logout', async (req, res) =&amp;gt; {&lt;br&gt;
  const token = req.cookies.token&lt;br&gt;
  await redisClient.set(&lt;code&gt;blacklist_${token}&lt;/code&gt;, '1', { EX: 900 })&lt;br&gt;
  res.clearCookie('token')&lt;br&gt;
  res.json({ message: 'Logged out' })&lt;br&gt;
})&lt;/p&gt;

&lt;p&gt;// In auth middleware&lt;br&gt;
const isBlacklisted = await redisClient.get(&lt;code&gt;blacklist_${token}&lt;/code&gt;)&lt;br&gt;
if (isBlacklisted) {&lt;br&gt;
  return res.status(401).json({ message: 'Token revoked' })&lt;br&gt;
}&lt;/p&gt;



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


---

## Quick Reference — JWT Checklist

| Practice | Status |
|----------|--------|
| Store in httpOnly cookie | ✅ Do this |
| Set expiration time | ✅ Always |
| Use strong secret in .env | ✅ Required |
| Use jwt.verify() not jwt.decode() | ✅ Always |
| Keep payload minimal | ✅ Less is more |
| Implement refresh tokens | ✅ For better UX |
| Handle token revocation | ✅ For production |

---

## Final Thought

JWT done wrong is worse than no auth at all — it gives you a false sense of security.

The good news? All of these fixes are simple once you know about them. Implement them once properly and you never have to worry again.

If you want all of this already built and production-ready, I packaged it into a MERN boilerplate — proper httpOnly cookies, refresh tokens, protected routes, clean folder structure, everything.

**🔗 Free version:** github.com/komalkhann001-sketch/mern-boilerplate

**🚀 Full production-ready version:** payhip.com/b/ZfbnM
*(One-time purchase — less than the cost of one hour of your freelance rate)*

*Found this useful? Drop a reaction and share it with a developer who needs to see this 🚀*
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

</description>
      <category>webdev</category>
      <category>javascript</category>
      <category>security</category>
      <category>beginners</category>
    </item>
    <item>
      <title>How I Built a MERN Boilerplate That Saves 2 Weeks of Development Time And why every freelancer should stop rebuilding the same thing over and over</title>
      <dc:creator>ramsha</dc:creator>
      <pubDate>Tue, 05 May 2026 10:39:12 +0000</pubDate>
      <link>https://dev.to/ramshakomal/how-i-built-a-mern-boilerplate-that-saves-2-weeks-of-development-time-and-why-every-freelancer-48ec</link>
      <guid>https://dev.to/ramshakomal/how-i-built-a-mern-boilerplate-that-saves-2-weeks-of-development-time-and-why-every-freelancer-48ec</guid>
      <description>&lt;p&gt;And why every freelancer should stop rebuilding the same thing over and over*&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Every time I started a new project&lt;/strong&gt; — whether it was for a client, a startup idea, or just practice — I found myself doing the &lt;strong&gt;exact same thing&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Setting up Express. Configuring MongoDB. Writing JWT auth. Building login/register. Protecting routes. Structuring folders.&lt;/p&gt;

&lt;p&gt;Again. And again. And again.&lt;/p&gt;

&lt;p&gt;Two weeks. Gone. Before writing a single line of actual business logic.&lt;/p&gt;

&lt;p&gt;I got tired of it.&lt;/p&gt;

&lt;h2&gt;
  
  
  So I Built Something
&lt;/h2&gt;

&lt;p&gt;I decided to stop wasting time and build a &lt;strong&gt;production-ready MERN boilerplate&lt;/strong&gt; once — properly — so I never have to do it again.&lt;/p&gt;

&lt;h3&gt;
  
  
  ✅ Authentication (The Annoying Part — Done Right)
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;User Register &amp;amp; Login&lt;/li&gt;
&lt;li&gt;JWT Authentication with refresh logic&lt;/li&gt;
&lt;li&gt;Protected Routes on both frontend and backend&lt;/li&gt;
&lt;li&gt;Proper error handling&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  ✅ React Dashboard
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Clean, responsive layout&lt;/li&gt;
&lt;li&gt;Ready to plug in your own features&lt;/li&gt;
&lt;li&gt;Component structure that actually scales&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  ✅ Backend Setup
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Node.js + Express — structured properly&lt;/li&gt;
&lt;li&gt;MongoDB integration with Mongoose&lt;/li&gt;
&lt;li&gt;Environment variables configured&lt;/li&gt;
&lt;li&gt;MVC folder structure&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  ✅ Production Ready
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Clean folder structure from day one&lt;/li&gt;
&lt;li&gt;No tutorial-quality spaghetti code&lt;/li&gt;
&lt;li&gt;Deploy-ready configuration&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  What This Actually Saves You
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Task&lt;/th&gt;
&lt;th&gt;Normal Time&lt;/th&gt;
&lt;th&gt;With Boilerplate&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Auth setup&lt;/td&gt;
&lt;td&gt;3-4 days&lt;/td&gt;
&lt;td&gt;✅ Done&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;JWT + Protected Routes&lt;/td&gt;
&lt;td&gt;2 days&lt;/td&gt;
&lt;td&gt;✅ Done&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Dashboard layout&lt;/td&gt;
&lt;td&gt;3 days&lt;/td&gt;
&lt;td&gt;✅ Done&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;MongoDB connection + models&lt;/td&gt;
&lt;td&gt;1 day&lt;/td&gt;
&lt;td&gt;✅ Done&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Folder structure + cleanup&lt;/td&gt;
&lt;td&gt;1 day&lt;/td&gt;
&lt;td&gt;✅ Done&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Total&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;~2 weeks&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;~1 hour&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h2&gt;
  
  
  Who Is This For?
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;A &lt;strong&gt;freelancer&lt;/strong&gt; who bills by project — stop wasting billable hours on setup&lt;/li&gt;
&lt;li&gt;A &lt;strong&gt;startup founder&lt;/strong&gt; who needs an MVP fast&lt;/li&gt;
&lt;li&gt;A &lt;strong&gt;student&lt;/strong&gt; building portfolio projects and wants real-world structure&lt;/li&gt;
&lt;li&gt;A &lt;strong&gt;developer&lt;/strong&gt; who is tired of copy-pasting the same auth code&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  The Folder Structure
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;mern-boilerplate/
├── client/
│   ├── src/
│   │   ├── components/
│   │   ├── pages/
│   │   ├── context/
│   │   └── routes/
├── server/
│   ├── controllers/
│   ├── middleware/
│   ├── models/
│   └── routes/
└── .env.example
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



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

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Frontend:&lt;/strong&gt; React.js, React Router, Axios, Context API&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Backend:&lt;/strong&gt; Node.js, Express.js&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Database:&lt;/strong&gt; MongoDB, Mongoose&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Auth:&lt;/strong&gt; JWT, bcrypt&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Deployment:&lt;/strong&gt; Ready for Vercel + Railway/Render&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  What Developers Are Saying
&lt;/h2&gt;

&lt;p&gt;&lt;em&gt;"Saved me an entire week on my last client project"&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;"Finally a boilerplate that doesn't feel like a tutorial project"&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;"The folder structure alone was worth it"&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Get It
&lt;/h2&gt;

&lt;p&gt;👉 &lt;strong&gt;GitHub:&lt;/strong&gt; github.com/komalkhann001-sketch/mern-boilerplate&lt;/p&gt;

&lt;p&gt;If you want the full production-ready version with complete setup guide and future updates, I have packaged it as a one-time purchase — less than the cost of &lt;strong&gt;one hour of your freelance rate&lt;/strong&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Final Thought
&lt;/h2&gt;

&lt;p&gt;Stop rebuilding the same foundation every project. Your time is worth more than that.&lt;/p&gt;

&lt;p&gt;The best developers are not the ones who write everything from scratch — they are the ones who &lt;strong&gt;ship faster&lt;/strong&gt; by being smart about what they reuse.&lt;/p&gt;

&lt;p&gt;Build the product. Not the boilerplate.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Found this useful? Drop a reaction and share with a developer friend who needs to hear this&lt;/em&gt; 🚀&lt;/p&gt;

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