<?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: Hamid Karimi</title>
    <description>The latest articles on DEV Community by Hamid Karimi (@hkarimi).</description>
    <link>https://dev.to/hkarimi</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F3649553%2F8bb234d8-7527-48b2-b3e2-4d1421d34a61.png</url>
      <title>DEV Community: Hamid Karimi</title>
      <link>https://dev.to/hkarimi</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/hkarimi"/>
    <language>en</language>
    <item>
      <title>Building Vidiflow: A Production-Grade Video Downloader Backend in TypeScript</title>
      <dc:creator>Hamid Karimi</dc:creator>
      <pubDate>Sun, 05 Jul 2026 13:18:24 +0000</pubDate>
      <link>https://dev.to/hkarimi/building-vidiflow-a-production-grade-video-downloader-backend-in-typescript-38gn</link>
      <guid>https://dev.to/hkarimi/building-vidiflow-a-production-grade-video-downloader-backend-in-typescript-38gn</guid>
      <description>&lt;h1&gt;
  
  
  I Built a Production-Ready Video Downloader Backend in TypeScript — Here's What I Learned
&lt;/h1&gt;

&lt;p&gt;I just completed &lt;strong&gt;V2 of Vidiflow&lt;/strong&gt;, a production-ready backend API for downloading videos from multiple platforms.&lt;/p&gt;

&lt;p&gt;This article is the full story of what I built, why I built it this way, and what I learned along the way.&lt;/p&gt;




&lt;h1&gt;
  
  
  The Challenge
&lt;/h1&gt;

&lt;p&gt;Most video downloader tutorials online are either:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Too simple — basic proof-of-concept projects that don't scale&lt;/li&gt;
&lt;li&gt;Too vague — "just use this library" without discussing architecture&lt;/li&gt;
&lt;li&gt;Not production-ready — missing authentication, error handling, queues, testing, or documentation&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I wanted to build something different.&lt;/p&gt;

&lt;p&gt;I wanted to build a backend that could:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Handle thousands of users&lt;/li&gt;
&lt;li&gt;Support multiple video platforms&lt;/li&gt;
&lt;li&gt;Be easy to extend&lt;/li&gt;
&lt;li&gt;Stay maintainable for years&lt;/li&gt;
&lt;/ul&gt;




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

&lt;p&gt;&lt;strong&gt;Vidiflow Backend&lt;/strong&gt; is a TypeScript/Node.js API that can:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;✅ Accept video URLs from YouTube, TikTok, and more&lt;/li&gt;
&lt;li&gt;✅ Extract real metadata (title, formats, quality options)&lt;/li&gt;
&lt;li&gt;✅ Return direct download URLs&lt;/li&gt;
&lt;li&gt;✅ Track download history&lt;/li&gt;
&lt;li&gt;✅ Manage favorite videos&lt;/li&gt;
&lt;li&gt;✅ Handle authentication (JWT, refresh tokens, OAuth-ready)&lt;/li&gt;
&lt;li&gt;✅ Process downloads asynchronously&lt;/li&gt;
&lt;li&gt;✅ Provide real-time download progress via WebSockets&lt;/li&gt;
&lt;li&gt;✅ Include complete Swagger/OpenAPI documentation&lt;/li&gt;
&lt;/ul&gt;




&lt;h1&gt;
  
  
  The Architecture (The Hard Part)
&lt;/h1&gt;

&lt;p&gt;Rather than jumping straight into coding, I designed the architecture first.&lt;/p&gt;

&lt;p&gt;That single decision probably saved me more time than anything else during the project.&lt;/p&gt;

&lt;h2&gt;
  
  
  Feature-Based Modular Structure
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;src/
└── modules/
    ├── auth/
    ├── downloads/
    ├── users/
    └── providers/
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Every feature owns its own code.&lt;/p&gt;

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

&lt;ul&gt;
&lt;li&gt;Authentication lives inside &lt;code&gt;auth&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Download logic lives inside &lt;code&gt;downloads&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Platform implementations live inside &lt;code&gt;providers&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;A new developer can instantly find where something belongs.&lt;/p&gt;




&lt;h2&gt;
  
  
  Three-Layer Pattern
&lt;/h2&gt;

&lt;p&gt;Every feature follows the same structure:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Routes
    ↓
Controllers
    ↓
Services
    ↓
Repositories
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Routes
&lt;/h3&gt;

&lt;p&gt;Only define HTTP endpoints.&lt;/p&gt;

&lt;h3&gt;
  
  
  Controllers
&lt;/h3&gt;

&lt;p&gt;Very thin.&lt;/p&gt;

&lt;p&gt;They:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;validate requests&lt;/li&gt;
&lt;li&gt;call services&lt;/li&gt;
&lt;li&gt;return responses&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Nothing else.&lt;/p&gt;

&lt;h3&gt;
  
  
  Services
&lt;/h3&gt;

&lt;p&gt;Contain all business logic.&lt;/p&gt;

&lt;p&gt;This makes them:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;reusable&lt;/li&gt;
&lt;li&gt;testable&lt;/li&gt;
&lt;li&gt;framework-independent&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Repositories
&lt;/h3&gt;

&lt;p&gt;Only communicate with the database.&lt;/p&gt;

&lt;p&gt;If I ever switch ORMs or introduce caching, the rest of the application doesn't change.&lt;/p&gt;




&lt;h1&gt;
  
  
  Key Technical Decisions
&lt;/h1&gt;

&lt;h2&gt;
  
  
  1. Provider Architecture
&lt;/h2&gt;

&lt;p&gt;Instead of hardcoding YouTube logic into the download service, I created a provider interface.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kr"&gt;interface&lt;/span&gt; &lt;span class="nx"&gt;IProvider&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="nf"&gt;canHandle&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;url&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="nx"&gt;boolean&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="nf"&gt;getVideoInfo&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;url&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="nb"&gt;Promise&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;VideoInfo&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="nf"&gt;download&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;url&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;options&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="nb"&gt;Promise&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;DownloadResult&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now every platform becomes its own provider.&lt;/p&gt;

&lt;p&gt;Adding a new platform only requires:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;one new provider file&lt;/li&gt;
&lt;li&gt;one registration line&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Nothing else changes.&lt;/p&gt;

&lt;p&gt;This makes the system extremely easy to extend.&lt;/p&gt;




&lt;h2&gt;
  
  
  2. Video Extraction with yt-dlp
&lt;/h2&gt;

&lt;p&gt;Instead of writing extraction logic myself, I integrated &lt;strong&gt;yt-dlp&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;The backend:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;launches yt-dlp as a subprocess&lt;/li&gt;
&lt;li&gt;reads its JSON output&lt;/li&gt;
&lt;li&gt;converts it into a clean API response&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Why?&lt;/p&gt;

&lt;p&gt;Because video extraction is incredibly difficult.&lt;/p&gt;

&lt;p&gt;Platforms constantly change.&lt;/p&gt;

&lt;p&gt;yt-dlp already solves that problem.&lt;/p&gt;

&lt;p&gt;My backend simply provides a stable interface around it.&lt;/p&gt;




&lt;h2&gt;
  
  
  3. Asynchronous Downloads with BullMQ + Redis
&lt;/h2&gt;

&lt;p&gt;Downloads are processed in the background.&lt;/p&gt;

&lt;p&gt;Instead of waiting for a long download to finish:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight http"&gt;&lt;code&gt;&lt;span class="err"&gt;POST /downloads
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;immediately returns:&lt;br&gt;
&lt;/p&gt;

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

&lt;/div&gt;



&lt;p&gt;The worker processes the job separately.&lt;/p&gt;

&lt;p&gt;Clients receive updates through WebSockets (or polling if needed).&lt;/p&gt;

&lt;p&gt;Benefits:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;instant API responses&lt;/li&gt;
&lt;li&gt;better scalability&lt;/li&gt;
&lt;li&gt;no blocked requests&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  4. JWT + Refresh Tokens
&lt;/h2&gt;

&lt;p&gt;Authentication follows the standard production pattern.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Access Token&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;expires in 15 minutes&lt;/li&gt;
&lt;li&gt;sent in Authorization header&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Refresh Token&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;valid for 7 days&lt;/li&gt;
&lt;li&gt;stored hashed in the database&lt;/li&gt;
&lt;li&gt;sent via httpOnly cookie&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;On logout, refresh tokens are revoked.&lt;/p&gt;

&lt;p&gt;This means stolen access tokens only live briefly, while users can still stay logged in securely.&lt;/p&gt;




&lt;h2&gt;
  
  
  5. Centralized Error Handling
&lt;/h2&gt;

&lt;p&gt;Every service throws typed errors.&lt;/p&gt;

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

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;NotFoundError&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Video not found&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;instead of&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;status&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;404&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;message&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Video not found&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;A single middleware converts every error into a consistent HTTP response.&lt;/p&gt;

&lt;p&gt;Controllers stay clean.&lt;/p&gt;

&lt;p&gt;No repetitive &lt;code&gt;try/catch&lt;/code&gt; blocks.&lt;/p&gt;




&lt;h1&gt;
  
  
  Designing for Future Growth
&lt;/h1&gt;

&lt;p&gt;One of my goals was making sure V2 could naturally grow into V3.&lt;/p&gt;

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

&lt;p&gt;Want payments?&lt;/p&gt;

&lt;p&gt;→ Add a new &lt;code&gt;billing&lt;/code&gt; module.&lt;/p&gt;

&lt;p&gt;Need caching?&lt;/p&gt;

&lt;p&gt;→ Add Redis inside repositories.&lt;/p&gt;

&lt;p&gt;Need mobile apps?&lt;/p&gt;

&lt;p&gt;→ Reuse the same API.&lt;/p&gt;

&lt;p&gt;Need microservices later?&lt;/p&gt;

&lt;p&gt;→ Module boundaries already exist.&lt;/p&gt;

&lt;p&gt;Need analytics?&lt;/p&gt;

&lt;p&gt;→ Add logging around services.&lt;/p&gt;

&lt;p&gt;That's what "designing for scale" means.&lt;/p&gt;

&lt;p&gt;Not premature optimization.&lt;/p&gt;

&lt;p&gt;Just clean boundaries.&lt;/p&gt;




&lt;h1&gt;
  
  
  V1 → V2 Journey
&lt;/h1&gt;

&lt;h2&gt;
  
  
  V1
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Mock data&lt;/li&gt;
&lt;li&gt;Placeholder extraction&lt;/li&gt;
&lt;li&gt;Architecture prototype&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  V2
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Real yt-dlp integration&lt;/li&gt;
&lt;li&gt;WebSocket progress updates&lt;/li&gt;
&lt;li&gt;Swagger documentation&lt;/li&gt;
&lt;li&gt;Real download URLs&lt;/li&gt;
&lt;li&gt;Better error handling&lt;/li&gt;
&lt;li&gt;TikTok provider implementation&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Even though TikTok extraction is less reliable because of platform restrictions, the provider architecture handled that without affecting the rest of the application.&lt;/p&gt;




&lt;h1&gt;
  
  
  Project Size
&lt;/h1&gt;

&lt;p&gt;Approximately:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;2,500 lines of code&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;

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

&lt;p&gt;Just intentionally written.&lt;/p&gt;




&lt;h1&gt;
  
  
  What I Learned
&lt;/h1&gt;

&lt;h2&gt;
  
  
  1. Architecture First, Code Second
&lt;/h2&gt;

&lt;p&gt;I spent around two hours designing before writing code.&lt;/p&gt;

&lt;p&gt;That probably saved ten hours of refactoring later.&lt;/p&gt;




&lt;h2&gt;
  
  
  2. Modular &amp;gt; DRY
&lt;/h2&gt;

&lt;p&gt;Not every duplicated function deserves extraction.&lt;/p&gt;

&lt;p&gt;But every feature deserves its own home.&lt;/p&gt;

&lt;p&gt;Organization matters more than chasing perfect DRY.&lt;/p&gt;




&lt;h2&gt;
  
  
  3. Typed Errors &amp;gt; Strings
&lt;/h2&gt;

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

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;ValidationError&lt;/span&gt;&lt;span class="p"&gt;(...)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;is much better than:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;status&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;400&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;message&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;...&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;TypeScript catches mistakes before runtime.&lt;/p&gt;




&lt;h2&gt;
  
  
  4. Secrets Belong in &lt;code&gt;.env&lt;/code&gt;
&lt;/h2&gt;

&lt;p&gt;Hardcoded configuration eventually becomes technical debt.&lt;/p&gt;

&lt;p&gt;Even development values belong inside:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight properties"&gt;&lt;code&gt;&lt;span class="err"&gt;.env&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;or&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight properties"&gt;&lt;code&gt;&lt;span class="err"&gt;.env.example&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  5. Documentation Isn't Optional
&lt;/h2&gt;

&lt;p&gt;Adding Swagger took less than an hour.&lt;/p&gt;

&lt;p&gt;It will save countless hours for anyone using the API.&lt;/p&gt;

&lt;p&gt;Documentation is part of the product.&lt;/p&gt;




&lt;h2&gt;
  
  
  6. Test While You Build
&lt;/h2&gt;

&lt;p&gt;Every endpoint was tested in Postman before moving to the next one.&lt;/p&gt;

&lt;p&gt;Small feedback loops catch bugs much earlier.&lt;/p&gt;




&lt;h1&gt;
  
  
  Tech Stack
&lt;/h1&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Language:&lt;/strong&gt; TypeScript (Strict Mode)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Runtime:&lt;/strong&gt; Node.js&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Framework:&lt;/strong&gt; Express.js&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Database:&lt;/strong&gt; PostgreSQL + Prisma ORM&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Authentication:&lt;/strong&gt; JWT + bcrypt&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Queues:&lt;/strong&gt; BullMQ + Redis&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Real-Time:&lt;/strong&gt; Socket.IO&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Security:&lt;/strong&gt; Helmet, CORS, Express Rate Limit, Express Validator&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Documentation:&lt;/strong&gt; Swagger/OpenAPI&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Video Extraction:&lt;/strong&gt; yt-dlp&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Everything is container-ready and designed for production deployment.&lt;/p&gt;




&lt;h1&gt;
  
  
  What's Next?
&lt;/h1&gt;

&lt;h2&gt;
  
  
  Backend (V3)
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Unit tests&lt;/li&gt;
&lt;li&gt;Integration tests&lt;/li&gt;
&lt;li&gt;Email verification&lt;/li&gt;
&lt;li&gt;Password reset&lt;/li&gt;
&lt;li&gt;User profile endpoints&lt;/li&gt;
&lt;li&gt;More video providers&lt;/li&gt;
&lt;li&gt;Admin monitoring endpoints&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Frontend
&lt;/h2&gt;

&lt;p&gt;I'll be building a React frontend that includes:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Authentication&lt;/li&gt;
&lt;li&gt;Video downloads&lt;/li&gt;
&lt;li&gt;Download history&lt;/li&gt;
&lt;li&gt;Favorites&lt;/li&gt;
&lt;li&gt;Real-time progress tracking&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Future Plans
&lt;/h2&gt;

&lt;p&gt;After that:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Browser extension&lt;/li&gt;
&lt;li&gt;Mobile apps&lt;/li&gt;
&lt;li&gt;Analytics&lt;/li&gt;
&lt;li&gt;Premium features&lt;/li&gt;
&lt;/ul&gt;




&lt;h1&gt;
  
  
  Open Source
&lt;/h1&gt;

&lt;p&gt;The project is fully open source on GitHub.&lt;/p&gt;

&lt;p&gt;It includes:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;clean commit history&lt;/li&gt;
&lt;li&gt;tagged releases&lt;/li&gt;
&lt;li&gt;proper project structure&lt;/li&gt;
&lt;li&gt;production architecture&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If you're learning backend development, it can serve as a reference for:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Structuring a Node.js application&lt;/li&gt;
&lt;li&gt;Designing extensible systems&lt;/li&gt;
&lt;li&gt;Implementing authentication&lt;/li&gt;
&lt;li&gt;Building async workflows&lt;/li&gt;
&lt;li&gt;Writing clean service layers&lt;/li&gt;
&lt;li&gt;Handling errors properly&lt;/li&gt;
&lt;/ul&gt;




&lt;h1&gt;
  
  
  The Biggest Lesson
&lt;/h1&gt;

&lt;p&gt;Building production software isn't about writing more code.&lt;/p&gt;

&lt;p&gt;It's about making better decisions.&lt;/p&gt;

&lt;p&gt;Things like:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Feature-based organization&lt;/li&gt;
&lt;li&gt;Provider architecture&lt;/li&gt;
&lt;li&gt;Separation of concerns&lt;/li&gt;
&lt;li&gt;Typed errors&lt;/li&gt;
&lt;li&gt;Secure authentication&lt;/li&gt;
&lt;li&gt;Async processing&lt;/li&gt;
&lt;li&gt;Real-time updates&lt;/li&gt;
&lt;li&gt;Documentation&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;None of these ideas are particularly advanced.&lt;/p&gt;

&lt;p&gt;They simply require intentional design.&lt;/p&gt;

&lt;p&gt;That's what makes a project maintainable.&lt;/p&gt;




&lt;h1&gt;
  
  
  Advice for Anyone Building Something Similar
&lt;/h1&gt;

&lt;p&gt;If you're building your own backend, I'd recommend:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Start with architecture, not implementation.&lt;/li&gt;
&lt;li&gt;Write down &lt;em&gt;why&lt;/em&gt; you're choosing a pattern.&lt;/li&gt;
&lt;li&gt;Keep responsibilities separated.&lt;/li&gt;
&lt;li&gt;Test continuously.&lt;/li&gt;
&lt;li&gt;Document the reasoning, not just the API.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;That's what separates tutorial projects from production systems.&lt;/p&gt;




&lt;h1&gt;
  
  
  One More Thing
&lt;/h1&gt;

&lt;p&gt;If you're interested in backend architecture, video downloading, or building scalable TypeScript APIs, feel free to check out the project on GitHub.&lt;/p&gt;

&lt;p&gt;It's real code with real trade-offs—not tutorial code.&lt;/p&gt;

&lt;p&gt;If you find it useful, I'd really appreciate a ⭐ on the repository.&lt;/p&gt;

&lt;p&gt;Feedback, suggestions, and discussions are always welcome.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;GitHub Repository:&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;👉 &lt;a href="https://github.com/hamidukarimi/Vidiflow-backend" rel="noopener noreferrer"&gt;https://github.com/hamidukarimi/Vidiflow-backend&lt;/a&gt;&lt;/p&gt;




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

&lt;p&gt;The biggest win isn't that I built a video downloader.&lt;/p&gt;

&lt;p&gt;The biggest win is that &lt;strong&gt;I understand every architectural decision inside the codebase.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;For me, that's what building real software is all about.&lt;/p&gt;

</description>
      <category>node</category>
      <category>typescript</category>
      <category>opensource</category>
      <category>postgres</category>
    </item>
    <item>
      <title>I Built an Open-Source School Management System API — SchoolOS</title>
      <dc:creator>Hamid Karimi</dc:creator>
      <pubDate>Mon, 23 Mar 2026 10:28:11 +0000</pubDate>
      <link>https://dev.to/hkarimi/i-built-an-open-source-school-management-system-api-schoolos-3e8b</link>
      <guid>https://dev.to/hkarimi/i-built-an-open-source-school-management-system-api-schoolos-3e8b</guid>
      <description>&lt;h2&gt;
  
  
  What is SchoolOS?
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;SchoolOS&lt;/strong&gt; is a fully production-ready School Management Information System (MIS) REST API built with Node.js, Express, TypeScript, and MongoDB. It covers everything a school needs to manage its operations through a clean, well-structured API.&lt;/p&gt;

&lt;p&gt;The project is fully open-source under the MIT license, anyone can use it, extend it, or contribute to it.&lt;/p&gt;

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

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Runtime:&lt;/strong&gt; Node.js&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Framework:&lt;/strong&gt; Express.js&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Language:&lt;/strong&gt; TypeScript&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Database:&lt;/strong&gt; MongoDB + Mongoose&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Validation:&lt;/strong&gt; Zod&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Authentication:&lt;/strong&gt; JWT (Access + Refresh tokens with session management)&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  What's included?
&lt;/h2&gt;

&lt;p&gt;SchoolOS ships with 12 fully tested modules:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Students&lt;/strong&gt; — CRUD, filtering, search, pagination&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Teachers&lt;/strong&gt; — CRUD with subject and qualification tracking&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Classes&lt;/strong&gt; — Class creation, student enrollment, capacity management&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Timetable&lt;/strong&gt; — Weekly scheduling with teacher conflict detection&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Attendance&lt;/strong&gt; — Single and bulk recording, student summary reports&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Exams&lt;/strong&gt; — Exam scheduling with status tracking&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Grades&lt;/strong&gt; — Auto grade calculation (A+/A/B/C/D/F), bulk entry, summaries&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Fees&lt;/strong&gt; — Payment recording, overdue tracking, student summaries&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Announcements&lt;/strong&gt; — Audience-targeted (all, teachers, students, parents)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Messages&lt;/strong&gt; — Inbox, sent, unread count, read receipts&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Library&lt;/strong&gt; — Book catalog, borrow/return with availability control&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;HR&lt;/strong&gt; — Staff management, contracts, salary summaries&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Architecture
&lt;/h2&gt;

&lt;p&gt;The project follows a clean flat layered architecture. Every feature is spread across exactly 5 files:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;models/       → Mongoose schema and TypeScript interface
validators/   → Zod schemas for request validation
services/     → Business logic (database operations)
controllers/  → HTTP handlers (thin, no business logic)
routes/       → Express router with middleware applied
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The single rule: &lt;strong&gt;controllers stay thin&lt;/strong&gt;. All database queries and business logic belong in services.&lt;/p&gt;

&lt;h2&gt;
  
  
  Authentication
&lt;/h2&gt;

&lt;p&gt;Authentication is powered by a custom JWT system with:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Access token (short-lived, sent in Authorization header)&lt;/li&gt;
&lt;li&gt;Refresh token (long-lived, stored in httpOnly cookie)&lt;/li&gt;
&lt;li&gt;Session management with device tracking&lt;/li&gt;
&lt;li&gt;Role-based access control (admin, teacher, student, parent)&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Response Format
&lt;/h2&gt;

&lt;p&gt;All endpoints follow a consistent response structure:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"success"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"data"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{},&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"message"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Operation successful"&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Getting Started
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git clone https://github.com/hamidukarimi/SchoolOS-backend.git
&lt;span class="nb"&gt;cd &lt;/span&gt;SchoolOS-backend
npm &lt;span class="nb"&gt;install
cp&lt;/span&gt; .env.example .env
npm run dev
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  What's next?
&lt;/h2&gt;

&lt;p&gt;The frontend is currently in development — a React + TypeScript + Tailwind CSS web app that consumes this API. It will be open-source as well.&lt;/p&gt;

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

&lt;ul&gt;
&lt;li&gt;🔗 GitHub: &lt;a href="https://github.com/hamidukarimi/schoolos-backend" rel="noopener noreferrer"&gt;https://github.com/hamidukarimi/schoolos-backend&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;📄 License: MIT&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If you find it useful, a ⭐ on GitHub means a lot. Contributions are very welcome!&lt;/p&gt;

</description>
      <category>opensource</category>
      <category>node</category>
      <category>typescript</category>
      <category>webdev</category>
    </item>
    <item>
      <title>Building a Production-Grade React Auth Starter (JWT, Refresh Tokens, Zustand, TanStack Query)</title>
      <dc:creator>Hamid Karimi</dc:creator>
      <pubDate>Wed, 11 Mar 2026 18:01:14 +0000</pubDate>
      <link>https://dev.to/hkarimi/building-a-production-grade-react-auth-starter-jwt-refresh-tokens-zustand-tanstack-query-3pk3</link>
      <guid>https://dev.to/hkarimi/building-a-production-grade-react-auth-starter-jwt-refresh-tokens-zustand-tanstack-query-3pk3</guid>
      <description>&lt;h1&gt;
  
  
  Building a Production-Grade React Auth Starter
&lt;/h1&gt;

&lt;p&gt;Authentication is one of those things every frontend developer rebuilds from scratch on every new project. I got tired of that — so I built &lt;strong&gt;authforge-client&lt;/strong&gt;: a clean, scalable React + TypeScript auth starter that handles all the hard parts properly.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/hamidukarimi/authforge-client" rel="noopener noreferrer"&gt;GitHub →&lt;/a&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  What's in the stack
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;React + TypeScript&lt;/strong&gt; (Vite + SWC)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;TanStack Query&lt;/strong&gt; — server state and mutations&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Zustand&lt;/strong&gt; — global auth state&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Zod&lt;/strong&gt; — schema validation and TypeScript types&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;React Hook Form&lt;/strong&gt; — form management&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Framer Motion&lt;/strong&gt; — animations&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Axios&lt;/strong&gt; — HTTP client with interceptors&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Tailwind CSS v4&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  The architecture
&lt;/h2&gt;

&lt;p&gt;The folder structure is feature-based, not type-based:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;src/
├─ api/           # axios instance + endpoints
├─ features/
│  ├─ auth/       # login, register, schemas, hooks, services
│  └─ user/       # change password
├─ components/
│  ├─ ui/         # Button, Input, Alert, Spinner
│  └─ layout/     # Navbar, ProtectedRoute, PublicRoute
├─ store/         # Zustand auth store
├─ context/       # AuthContext (session restore)
├─ hooks/         # useAuth, useLogout
└─ utils/         # token helpers, error handler
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Each feature owns its own components, hooks, services, schemas, and types. Adding a new feature never touches existing ones.&lt;/p&gt;




&lt;h2&gt;
  
  
  The token refresh flow
&lt;/h2&gt;

&lt;p&gt;This is where most auth implementations cut corners. Here's the full flow:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;On login/register:&lt;/strong&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Backend returns &lt;code&gt;accessToken&lt;/code&gt; in the response body&lt;/li&gt;
&lt;li&gt;Backend sets &lt;code&gt;refreshToken&lt;/code&gt; as an &lt;code&gt;httpOnly&lt;/code&gt; cookie&lt;/li&gt;
&lt;li&gt;Frontend stores &lt;code&gt;accessToken&lt;/code&gt; in &lt;code&gt;sessionStorage&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;User object goes into Zustand&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;strong&gt;On every API request:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Axios request interceptor&lt;/span&gt;
&lt;span class="nx"&gt;config&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;headers&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Authorization&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;`Bearer &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nf"&gt;getToken&lt;/span&gt;&lt;span class="p"&gt;()}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;On 401 (expired access token):&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Axios response interceptor&lt;/span&gt;
&lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;?.&lt;/span&gt;&lt;span class="nx"&gt;status&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="mi"&gt;401&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="c1"&gt;// Queue all failed requests&lt;/span&gt;
  &lt;span class="c1"&gt;// Call POST /api/token once&lt;/span&gt;
  &lt;span class="c1"&gt;// Retry all queued requests with new token&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The failed queue pattern is important — without it, multiple simultaneous 401s would trigger multiple refresh calls, causing race conditions.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;On page refresh:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// ProtectedRoute calls restoreSession()&lt;/span&gt;
&lt;span class="c1"&gt;// restoreSession calls POST /api/token&lt;/span&gt;
&lt;span class="c1"&gt;// Gets fresh accessToken + user from refresh token cookie&lt;/span&gt;
&lt;span class="c1"&gt;// Repopulates Zustand&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  The session restore problem
&lt;/h2&gt;

&lt;p&gt;One subtle bug I ran into: &lt;code&gt;isInitializing&lt;/code&gt; starts as &lt;code&gt;false&lt;/code&gt;, so on page refresh at &lt;code&gt;/dashboard&lt;/code&gt;, the &lt;code&gt;ProtectedRoute&lt;/code&gt; would see &lt;code&gt;isAuthenticated: false&lt;/code&gt; and immediately redirect to &lt;code&gt;/login&lt;/code&gt; — before &lt;code&gt;restoreSession&lt;/code&gt; even ran.&lt;/p&gt;

&lt;p&gt;The fix was a module-level flag:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;sessionChecked&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;ProtectedRoute&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nf"&gt;useEffect&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;sessionChecked&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nx"&gt;sessionChecked&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;restoreSession&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;restoreSession&lt;/span&gt;&lt;span class="p"&gt;]);&lt;/span&gt;

  &lt;span class="c1"&gt;// Never redirect until we've actually checked&lt;/span&gt;
  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;sessionChecked&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="nx"&gt;isInitializing&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;Spinner&lt;/span&gt; &lt;span class="o"&gt;/&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;isAuthenticated&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;Navigate&lt;/span&gt; &lt;span class="nx"&gt;to&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;/login&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="o"&gt;/&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;Outlet&lt;/span&gt; &lt;span class="o"&gt;/&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Unlike &lt;code&gt;useRef&lt;/code&gt;, a module-level variable persists across route changes — so navigating between protected routes doesn't trigger &lt;code&gt;restoreSession&lt;/code&gt; again.&lt;/p&gt;




&lt;h2&gt;
  
  
  Error handling done right
&lt;/h2&gt;

&lt;p&gt;Most tutorials just catch errors and show "Something went wrong." This starter parses errors properly:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// mutationFn catches raw AxiosError and throws ParsedError&lt;/span&gt;
&lt;span class="nx"&gt;mutationFn&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;payload&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;registerService&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="nf"&gt;setToken&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;result&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;accessToken&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nf"&gt;setUser&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;result&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;user&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;catch &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;rawError&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="nf"&gt;parseApiError&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;rawError&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// "Email already registered"&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;TanStack Query stores the parsed error, so the UI always shows human-readable messages from the backend.&lt;/p&gt;




&lt;h2&gt;
  
  
  Multi-step registration form
&lt;/h2&gt;

&lt;p&gt;The register form is split into two steps with Framer Motion slide animations. The key challenge was cross-field validation (&lt;code&gt;password === confirmPassword&lt;/code&gt;) on Step 1 before allowing navigation to Step 2.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;trigger(["confirmPassword"])&lt;/code&gt; alone doesn't work for cross-field Zod refinements — the fix was using &lt;code&gt;getValues()&lt;/code&gt; + &lt;code&gt;setError()&lt;/code&gt; manually in the &lt;code&gt;goNext&lt;/code&gt; handler:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;goNext&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;valid&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;trigger&lt;/span&gt;&lt;span class="p"&gt;([...&lt;/span&gt;&lt;span class="nx"&gt;step1Fields&lt;/span&gt;&lt;span class="p"&gt;]);&lt;/span&gt;
  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;valid&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;values&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;getValues&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;values&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;password&lt;/span&gt; &lt;span class="o"&gt;!==&lt;/span&gt; &lt;span class="nx"&gt;values&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;confirmPassword&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nf"&gt;setError&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;confirmPassword&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;manual&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;message&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Passwords do not match&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;});&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="nf"&gt;setStep&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






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

&lt;p&gt;This starter is the auth foundation for &lt;strong&gt;Yummy&lt;/strong&gt; — a full-stack food ordering app I'm building. The backend is an ExpressJS + TypeScript server (also open source) with JWT, MongoDB sessions, role-based access control, and rate limiting.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;authforge-express&lt;/strong&gt; → &lt;a href="https://github.com/hamidukarimi/authforge-express" rel="noopener noreferrer"&gt;github.com/hamidukarimi/authforge-express&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;authforge-client&lt;/strong&gt; → &lt;a href="https://github.com/hamidukarimi/authforge-client" rel="noopener noreferrer"&gt;github.com/hamidukarimi/authforge-client&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If this saves you time, drop a ⭐ on GitHub!&lt;/p&gt;

</description>
      <category>react</category>
      <category>typescript</category>
      <category>webdev</category>
      <category>javascript</category>
    </item>
    <item>
      <title>I Built an Authentication System With Express.js, MongoDB, (Access/Refresh Tokens, Sessions, Rate Limiting &amp; More)</title>
      <dc:creator>Hamid Karimi</dc:creator>
      <pubDate>Sun, 22 Feb 2026 00:24:10 +0000</pubDate>
      <link>https://dev.to/hkarimi/i-built-an-authentication-system-with-expressjs-mongodb-accessrefresh-tokens-sessions-rate-hh0</link>
      <guid>https://dev.to/hkarimi/i-built-an-authentication-system-with-expressjs-mongodb-accessrefresh-tokens-sessions-rate-hh0</guid>
      <description>&lt;p&gt;I recently finished building one of my most solid backend projects — a complete authentication system written in Express.js, powered by MongoDB, JWT, and a clean architecture design.&lt;/p&gt;

&lt;p&gt;Here’s what I implemented step-by-step:&lt;/p&gt;

&lt;p&gt;**🔐 Access + Refresh tokens with secure storage&lt;/p&gt;

&lt;p&gt;🗂️ Session tracking in the database&lt;/p&gt;

&lt;p&gt;🧹 Automatic session invalidation&lt;/p&gt;

&lt;p&gt;🛡️ IP-based rate limiting (5 attempts / 10 mins)&lt;/p&gt;

&lt;p&gt;🧪 Validation middleware for all inputs&lt;/p&gt;

&lt;p&gt;⚠️ Global ApiError system for consistent error formatting&lt;/p&gt;

&lt;p&gt;🧱 Clean architecture with controllers, services, utils&lt;/p&gt;

&lt;p&gt;🛠️ Multiple bug fixes + edge case handling&lt;/p&gt;

&lt;p&gt;🚀 Focus on maintainability &amp;amp; production readiness**&lt;/p&gt;

&lt;p&gt;This project taught me a LOT about system design, real-world auth, and secure backend development.&lt;/p&gt;

&lt;p&gt;If you like it, Hit a ⭐ on GitHub or share it with other developers!&lt;/p&gt;

&lt;p&gt;👉 GitHub Repo: &lt;a href="https://github.com/hamidukarimi/authforge-express" rel="noopener noreferrer"&gt;https://github.com/hamidukarimi/authforge-express&lt;/a&gt;&lt;br&gt;
If you have ideas on improvements or want the front-end version too — let me know!&lt;/p&gt;

</description>
      <category>node</category>
      <category>express</category>
      <category>javascript</category>
      <category>backend</category>
    </item>
    <item>
      <title>👋 Hello Dev Community — I’m New Here!</title>
      <dc:creator>Hamid Karimi</dc:creator>
      <pubDate>Sat, 06 Dec 2025 18:50:18 +0000</pubDate>
      <link>https://dev.to/hkarimi/hello-dev-community-im-new-here-ln2</link>
      <guid>https://dev.to/hkarimi/hello-dev-community-im-new-here-ln2</guid>
      <description>&lt;p&gt;👋 Hello Dev Community — I'm New Here!&lt;/p&gt;

&lt;p&gt;Hi everyone! My name is Hamid, and this is my very first post on Dev.to 😊&lt;/p&gt;

&lt;p&gt;I'm a full-stack web and mobile app developer skilled in:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;React / Next.js&lt;/li&gt;
&lt;li&gt;React Native / Expo / Reanimated&lt;/li&gt;
&lt;li&gt;Node.js / Express&lt;/li&gt;
&lt;li&gt;MongoDB / MySQL&lt;/li&gt;
&lt;li&gt;UI/UX Design&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I joined Dev.to because I want to:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Share what I learn&lt;/li&gt;
&lt;li&gt;Connect with other developers&lt;/li&gt;
&lt;li&gt;Improve my skills&lt;/li&gt;
&lt;li&gt;Become part of the community&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I'm excited to start writing about the things I'm working on — especially React Native animations, mobile apps, and full-stack development.&lt;/p&gt;

&lt;p&gt;If you're reading this, feel free to say hi in the comments!&lt;br&gt;&lt;br&gt;
Looking forward to learning from all of you&lt;/p&gt;

&lt;p&gt;Thanks for having me here ❤️&lt;/p&gt;

</description>
    </item>
  </channel>
</rss>
