<?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>Forem: Pau Dang</title>
    <description>The latest articles on Forem by Pau Dang (@paudang).</description>
    <link>https://forem.com/paudang</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%2F3778555%2F0cf1e817-ad3e-4643-a257-b03aa03652f0.jpg</url>
      <title>Forem: Pau Dang</title>
      <link>https://forem.com/paudang</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://forem.com/feed/paudang"/>
    <language>en</language>
    <item>
      <title>Stop Wasting Time on Boilerplate: Real-world Kafka &amp; PostgreSQL Demo in 8 minutes</title>
      <dc:creator>Pau Dang</dc:creator>
      <pubDate>Tue, 07 Apr 2026 13:00:00 +0000</pubDate>
      <link>https://forem.com/paudang/stop-wasting-time-on-boilerplate-real-world-kafka-postgresql-demo-24bp</link>
      <guid>https://forem.com/paudang/stop-wasting-time-on-boilerplate-real-world-kafka-postgresql-demo-24bp</guid>
      <description>&lt;p&gt;After releasing the v2.0.0 Web UI for &lt;strong&gt;Node.js Quickstart Generator&lt;/strong&gt;, the most common question was: &lt;em&gt;"How does it handle real-world complexity?"&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;So, I decided to record a full, 8-minute implementation demo building a &lt;strong&gt;Payment Service&lt;/strong&gt; from scratch. &lt;/p&gt;

&lt;h3&gt;
  
  
  📺 Watch: UI to Production Code in 8 Minutes
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://youtu.be/PmmxJLloZ1Q" rel="noopener noreferrer"&gt;https://youtu.be/PmmxJLloZ1Q&lt;/a&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  🛠️ The Tech Stack (Zero-Prompt Setup)
&lt;/h2&gt;

&lt;p&gt;Instead of answering 20 CLI prompts, I used our new &lt;a href="https://paudang.github.io/nodejs-quickstart-structure/" rel="noopener noreferrer"&gt;Web Configurator&lt;/a&gt; to generate this exact stack:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Language&lt;/strong&gt;: TypeScript&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Architecture&lt;/strong&gt;: Clean Architecture (Domain, UseCase, Infra)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Database&lt;/strong&gt;: PostgreSQL&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Caching&lt;/strong&gt;: Redis&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Messaging&lt;/strong&gt;: Kafka&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Security&lt;/strong&gt;: Snyk Verified&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  🏗️ Clean Architecture in Action
&lt;/h2&gt;

&lt;p&gt;The video shows exactly how the folder structure reflects a production-grade system:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;src/domain&lt;/code&gt;&lt;/strong&gt;: Pure entities with no dependencies.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;src/usecases&lt;/code&gt;&lt;/strong&gt;: Where the transaction logic lives.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;src/infrastructure&lt;/code&gt;&lt;/strong&gt;: Concrete implementations for Postgres connections and Kafka producers.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  📡 Live Kafka Flow
&lt;/h2&gt;

&lt;p&gt;The "Wow" moment is at &lt;strong&gt;05:00&lt;/strong&gt; in the video. We trigger a REST API call that producers a Kafka event, which is then picked up by a separate consumer.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Zero configuration required&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Pre-mapped events&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Ready for microservices&lt;/strong&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  🛡️ Enterprise-Grade Security
&lt;/h2&gt;

&lt;p&gt;We don't skip security. I ran &lt;code&gt;npm run security:check&lt;/code&gt; in the video to show how &lt;strong&gt;Snyk&lt;/strong&gt; is integrated from the start. Clean code is nothing if it isn't secure.&lt;/p&gt;




&lt;h3&gt;
  
  
  💻 Try it yourself
&lt;/h3&gt;

&lt;p&gt;Want to generate the exact same project as in the video? Run this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npx nodejs-quickstart-structure@latest init &lt;span class="nt"&gt;-n&lt;/span&gt; &lt;span class="s2"&gt;"payment-service-nodejs"&lt;/span&gt; &lt;span class="nt"&gt;-l&lt;/span&gt; &lt;span class="s2"&gt;"TypeScript"&lt;/span&gt; &lt;span class="nt"&gt;-a&lt;/span&gt; &lt;span class="s2"&gt;"Clean Architecture"&lt;/span&gt; &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="s2"&gt;"PostgreSQL"&lt;/span&gt; &lt;span class="nt"&gt;--db-name&lt;/span&gt; &lt;span class="s2"&gt;"payment-db"&lt;/span&gt; &lt;span class="nt"&gt;-c&lt;/span&gt; &lt;span class="s2"&gt;"Kafka"&lt;/span&gt; &lt;span class="nt"&gt;--caching&lt;/span&gt; &lt;span class="s2"&gt;"Redis"&lt;/span&gt; &lt;span class="nt"&gt;--ci-provider&lt;/span&gt; &lt;span class="s2"&gt;"GitHub Actions"&lt;/span&gt; &lt;span class="nt"&gt;--include-security&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Check out our &lt;strong&gt;&lt;a href="https://github.com/paudang/nodejs-quickstart-structure" rel="noopener noreferrer"&gt;GitHub Repo&lt;/a&gt;&lt;/strong&gt; and let's end boilerplate fatigue together! 🌟&lt;/p&gt;

</description>
      <category>node</category>
      <category>javascript</category>
      <category>architecture</category>
      <category>webdev</category>
    </item>
    <item>
      <title>Introducing Node.js Quickstart Generator v2.0.0: Automated Clean Architecture with a Sleek New UI</title>
      <dc:creator>Pau Dang</dc:creator>
      <pubDate>Mon, 06 Apr 2026 12:24:35 +0000</pubDate>
      <link>https://forem.com/paudang/boilerplate-scaffold-pro-nodejs-apps-with-kafka-graphql-redis-in-30s-1ji9</link>
      <guid>https://forem.com/paudang/boilerplate-scaffold-pro-nodejs-apps-with-kafka-graphql-redis-in-30s-1ji9</guid>
      <description>&lt;p&gt;Hi everyone,&lt;/p&gt;

&lt;p&gt;How many times have you set up a new Node.js microservice by copying a folder from your last project? We all do it out of necessity. But after releasing v1.1.0, the scale of "boilerplate fatigue" became clear: &lt;strong&gt;over 4,000 developers&lt;/strong&gt; joined the journey in just one month.&lt;/p&gt;

&lt;p&gt;That growth sparked a realization: we needed more than just a template. Previously known as &lt;strong&gt;nodejs-quickstart-structure&lt;/strong&gt;, we have officially evolved into a full-scale Generator in v2.0.0.&lt;/p&gt;

&lt;p&gt;I built this tool to help ourselves: &lt;strong&gt;Why can't our starting points be enterprise-ready by default?&lt;/strong&gt;&lt;/p&gt;




&lt;h3&gt;
  
  
  🎥 The Evolution: From CLI (v1.1.0) to Web UI (v2.0.0)
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F91vv5s809rqmznkvny5r.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F91vv5s809rqmznkvny5r.gif" alt=" " width="400" height="294"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;How it started: A powerful CLI that 4,000 of you loved (v1.1.0).&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ff7l88uefp8knkqs3iljw.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ff7l88uefp8knkqs3iljw.png" alt=" " width="800" height="622"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://www.youtube.com/watch?v=Cxbb54T0uo8" rel="noopener noreferrer"&gt;https://www.youtube.com/watch?v=Cxbb54T0uo8&lt;/a&gt;&lt;br&gt;
&lt;em&gt;How it’s going: A full-blown Web UI with 1:1 structure parity (v2.0.0).&lt;/em&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  Introducing: Node.js Quickstart Generator 2.0.0
&lt;/h2&gt;

&lt;p&gt;I built this tool to solve my own frustration: &lt;strong&gt;Why can't I just have a professional-grade starting point in one command?&lt;/strong&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  ✨ What’s in the Box?
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Flexible Architecture&lt;/strong&gt;: Toggle between &lt;strong&gt;MVC&lt;/strong&gt; and &lt;strong&gt;Clean Architecture&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Modern Stack&lt;/strong&gt;: Pick &lt;strong&gt;JavaScript&lt;/strong&gt; or &lt;strong&gt;TypeScript&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Communication Protocols&lt;/strong&gt;: Built-in support for &lt;strong&gt;REST&lt;/strong&gt;, &lt;strong&gt;GraphQL (Apollo)&lt;/strong&gt;, and &lt;strong&gt;Kafka&lt;/strong&gt; (Event-driven).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Caching Layer&lt;/strong&gt;: Pre-configured &lt;strong&gt;Redis&lt;/strong&gt; or In-memory cache.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Enterprise Standards&lt;/strong&gt;:

&lt;ul&gt;
&lt;li&gt;Multi-stage Dockerfiles.&lt;/li&gt;
&lt;li&gt;Snyk &amp;amp; SonarCloud security hardening.&lt;/li&gt;
&lt;li&gt;Global error handling (ApiError, NotFoundError, etc.).&lt;/li&gt;
&lt;li&gt;Jest &amp;amp; Supertest with &lt;strong&gt;80% unit test coverage&lt;/strong&gt; out of the box.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;

&lt;h3&gt;
  
  
  🌐 Next-Gen Web UI Configurator (v2.0.0)
&lt;/h3&gt;

&lt;p&gt;Type no more! We have completely evolved the configuration experience in our new &lt;a href="https://paudang.github.io/nodejs-quickstart-structure/" rel="noopener noreferrer"&gt;Web UI Configurator&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;It includes a &lt;strong&gt;Real-time Folder Simulation&lt;/strong&gt;. As you toggle between MVC and Clean Architecture, you see exactly what the project will look like. Once you're happy, just copy the &lt;strong&gt;Zero-Prompt CLI command&lt;/strong&gt; and run it in your terminal. No more prompts!&lt;/p&gt;




&lt;h3&gt;
  
  
  🦾 AI-Native Foundation
&lt;/h3&gt;

&lt;p&gt;We designed v2.0.0 for the future. If you use &lt;strong&gt;Cursor&lt;/strong&gt; or other AI coding agents, our built-in &lt;code&gt;.cursorrules&lt;/code&gt; will make your AI assistant 10x smarter about your specific architecture. No more hallucinations about where controllers or repos belong!&lt;/p&gt;

&lt;h3&gt;
  
  
  🎯 Get Started NOW
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npx nodejs-quickstart-structure@latest init
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That’s it. No global install required.&lt;/p&gt;




&lt;h3&gt;
  
  
  🗺️ Road to 10k Downloads
&lt;/h3&gt;

&lt;p&gt;We just crossed &lt;strong&gt;4,000+ downloads&lt;/strong&gt; and want to reach 10k by the end of the year. If you love open-source tools that actually save you time, please:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Give us a ⭐ on &lt;strong&gt;&lt;a href="https://github.com/paudang/nodejs-quickstart-structure" rel="noopener noreferrer"&gt;GitHub&lt;/a&gt;&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;Share this with your team.&lt;/li&gt;
&lt;li&gt;Drop a comment below—what feature should we add next? (gRPC? NestJS-style?)&lt;/li&gt;
&lt;/ol&gt;

</description>
      <category>ai</category>
      <category>webdev</category>
      <category>node</category>
      <category>opensource</category>
    </item>
    <item>
      <title>24 Hours of Chaos: Saving My Open Source Project from a Supply Chain Attack (plain-crypto-js)</title>
      <dc:creator>Pau Dang</dc:creator>
      <pubDate>Wed, 01 Apr 2026 01:01:37 +0000</pubDate>
      <link>https://forem.com/paudang/24-hours-of-chaos-saving-my-open-source-project-from-a-supply-chain-attack-dm8</link>
      <guid>https://forem.com/paudang/24-hours-of-chaos-saving-my-open-source-project-from-a-supply-chain-attack-dm8</guid>
      <description>&lt;p&gt;Hello world, &lt;/p&gt;

&lt;p&gt;I'm a Senior SE. Today, I want to share a "battle-tested" experience that just happened to my open-source project: &lt;strong&gt;nodejs-quickstart-structure&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;This isn't just about code; it’s a lesson in &lt;strong&gt;Incident Response&lt;/strong&gt; when facing professional malware designed to hijack npm, GitHub, and sensitive developer credentials.&lt;/p&gt;

&lt;h2&gt;
  
  
  1. The Threat: Axios &amp;amp; plain-crypto-js
&lt;/h2&gt;

&lt;p&gt;While developing version &lt;strong&gt;v2.0.0&lt;/strong&gt;, I fell victim to a &lt;strong&gt;Typosquatting&lt;/strong&gt; attack. A malicious package or a "shell" dependency injected malware into my local environment.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;The Suspect:&lt;/strong&gt; Linked to the &lt;code&gt;plain-crypto-js&lt;/code&gt; incident (a malware variant targeting devs using Axios).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The Behavior:&lt;/strong&gt; It didn't just break my system; it silently exfiltrated:

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Browser Cookies:&lt;/strong&gt; Hijacking active sessions for Gmail, GitHub, and LinkedIn.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;SSH Keys:&lt;/strong&gt; Gaining unauthorized access to push code to repositories.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;npm Tokens:&lt;/strong&gt; Attempting to publish malicious releases under my name.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;

&lt;h2&gt;
  
  
  2. 0h00: Detection &amp;amp; Containment
&lt;/h2&gt;

&lt;p&gt;Immediately after noticing suspicious logs and file modifications, I followed the "Security Textbook" or you can check at &lt;a href="https://snyk.io/blog/axios-npm-package-compromised-supply-chain-attack-delivers-cross-platform" rel="noopener noreferrer"&gt;Axios npm Package Compromised: Supply Chain Attack Delivers Cross-Platform RAT&lt;/a&gt;:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt; &lt;strong&gt;Deleted Local Repos:&lt;/strong&gt; Wiped the execution environment of the malware.&lt;/li&gt;
&lt;li&gt; &lt;strong&gt;Revoked All Sessions:&lt;/strong&gt; Used a clean device (mobile) to remotely sign out of Google, GitHub, Microsoft, and LinkedIn.&lt;/li&gt;
&lt;li&gt; &lt;strong&gt;Untrusted Devices:&lt;/strong&gt; Removed my current machine from the "Trusted Devices" list of all critical accounts.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  3. The Battle for npm (The Support Battle)
&lt;/h2&gt;

&lt;p&gt;The worst-case scenario: The attacker hijacked the session and invalidated my 2FA (my stored Recovery Codes returned &lt;strong&gt;Invalid&lt;/strong&gt;).&lt;/p&gt;

&lt;p&gt;I immediately contacted &lt;strong&gt;npm Support&lt;/strong&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Ticket ID: 4223695&lt;/strong&gt; was created.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fg8zbkud61jjc6erurqg8.jpeg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fg8zbkud61jjc6erurqg8.jpeg" alt=" " width="750" height="1028"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;The Strategy:&lt;/strong&gt; Providing proof of ownership through my GitHub account (which I still control) and the project's long-standing commit history.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  4. The Decision: Eradication (Wipe &amp;amp; Rebuild)
&lt;/h2&gt;

&lt;p&gt;As an Architect, I know that if an OS is compromised by a Rootkit/Trojan, no antivirus can guarantee a 100% clean state. The only solution: &lt;strong&gt;Wipe &amp;amp; Rebuild&lt;/strong&gt;.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;The Method:&lt;/strong&gt; Reset PC &amp;gt; Remove everything &amp;gt; &lt;strong&gt;Cloud download Windows&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Why Cloud Download?&lt;/strong&gt; To ensure a fresh installation image directly from Microsoft, avoiding any malware lurking in the local Recovery partition.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  5. Lessons Learned for Developers
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt; &lt;strong&gt;Dependency Vigilance:&lt;/strong&gt; Always double-check new packages, especially those with names similar to popular libraries.&lt;/li&gt;
&lt;li&gt; &lt;strong&gt;2FA is Not Enough:&lt;/strong&gt; Attackers can bypass 2FA via Session Hijacking. Always be ready to &lt;strong&gt;Revoke Sessions&lt;/strong&gt; remotely.&lt;/li&gt;
&lt;li&gt; &lt;strong&gt;Offline Recovery Codes:&lt;/strong&gt; Don't just store them on your computer. Print them or use a decoupled password manager.&lt;/li&gt;
&lt;li&gt; &lt;strong&gt;Incident Response Mindset:&lt;/strong&gt; When hacked, stay calm and follow: &lt;strong&gt;Containment -&amp;gt; Asset Protection -&amp;gt; Eradication -&amp;gt; Recovery.&lt;/strong&gt;
&lt;/li&gt;
&lt;/ol&gt;




&lt;p&gt;Currently, I am in the process of restoring a "sterile" environment to finalize &lt;strong&gt;v2.0.0&lt;/strong&gt; for &lt;code&gt;nodejs-quickstart-structure&lt;/code&gt;. You can check out the &lt;strong&gt;v2.0.0&lt;/strong&gt; beta details here:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://paudang.github.io/nodejs-quickstart-structure/guide/browser-generator.html" rel="noopener noreferrer"&gt;Next gen Web UI - Browser Generator&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The project will return with a higher security standard. I hope this story helps fellow developers protect their "digital children"!&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>node</category>
      <category>security</category>
    </item>
    <item>
      <title>How I Built a Node.js Generator with 1,680+ Combinations, Clean Architecture or MVC (Kafka)</title>
      <dc:creator>Pau Dang</dc:creator>
      <pubDate>Thu, 26 Mar 2026 10:01:34 +0000</pubDate>
      <link>https://forem.com/paudang/how-i-built-a-nodejs-generator-with-1680-combinations-clean-architecture-or-mvc-kafka-38j9</link>
      <guid>https://forem.com/paudang/how-i-built-a-nodejs-generator-with-1680-combinations-clean-architecture-or-mvc-kafka-38j9</guid>
      <description>&lt;p&gt;Hi all,&lt;/p&gt;

&lt;p&gt;We’ve all been there: starting a new Node.js microservice from scratch. You spend 4 hours setting up Express, another 2 hours arguing over folder structure (MVC or Clean Architecture?), 3 hours configuring Prisma or Mongoose, and half a day trying to get a Kafka KRaft container to talk to your local app. &lt;/p&gt;

&lt;p&gt;By the time you're ready to write your first line of business logic, you're already exhausted.&lt;/p&gt;

&lt;p&gt;I decided to solve this once and for all. I built &lt;strong&gt;&lt;a href="https://github.com/paudang/nodejs-quickstart-structure" rel="noopener noreferrer"&gt;nodejs-quickstart-structure&lt;/a&gt;&lt;/strong&gt;—a CLI that doesn't just give you a "hello world," but scaffolds a production-ready engine tailored to your exact needs.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Problem: The "Boilerplate exhaustion"
&lt;/h2&gt;

&lt;p&gt;Most generators give you a fixed stack. But in the real world, requirements vary:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;"This needs to be a simple MVC app."&lt;/li&gt;
&lt;li&gt;"This needs to be a Clean Architecture domain-driven service."&lt;/li&gt;
&lt;li&gt;"We need GraphQL, not REST."&lt;/li&gt;
&lt;li&gt;"We need Kafka for event-driven messaging."&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  The Solution: 1,680+ Scenarios in One CLI
&lt;/h2&gt;

&lt;p&gt;The core of this tool is its flexibility. By combining different technologies, it supports &lt;strong&gt;over 1,680 unique project combinations&lt;/strong&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Languages&lt;/strong&gt;: TypeScript (Recommended) / JavaScript.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Architectures&lt;/strong&gt;: MVC or Clean Architecture.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Databases&lt;/strong&gt;: MySQL, PostgreSQL, MongoDB, or None.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Communications&lt;/strong&gt;: REST APIs, GraphQL, or Kafka (Event-Driven).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Caching&lt;/strong&gt;: Redis or Memory Cache.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Security&lt;/strong&gt;: Hardened with Helmet, HPP, and automated Snyk/SonarCloud configs.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;CI/CD&lt;/strong&gt;: Ready-to-use workflows for GitHub Actions, GitLab CI, or Jenkins.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Why "Clean Architecture"?
&lt;/h2&gt;

&lt;p&gt;For many projects, MVC starts fast but becomes a nightmare to test. I’ve implemented a robust Clean Architecture template where:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Domain&lt;/strong&gt;: Pure business logic (Entities/Use Cases).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Infrastructure&lt;/strong&gt;: Database, Messaging (Kafka Client), Caching.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Interfaces&lt;/strong&gt;: Controllers, Express Routes, GraphQL Resolvers.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;This separation ensures your business logic doesn't care if you're using MongoDB or PostgreSQL.&lt;/p&gt;

&lt;h2&gt;
  
  
  AI-Native: Coding in 2026
&lt;/h2&gt;

&lt;p&gt;I realized that if the folder structure is standard, AI tools (like &lt;strong&gt;Cursor&lt;/strong&gt;, &lt;strong&gt;ChatGPT&lt;/strong&gt;, or &lt;strong&gt;Gemini&lt;/strong&gt;) can understand the codebase 10x faster. &lt;br&gt;
Every project generated includes:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;.cursorrules&lt;/strong&gt;: Pre-configured rules so Cursor knows exactly how to add new features following your chosen architecture.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;prompts/&lt;/strong&gt;: A library of "Agent Skills" to help you generate Use Cases or Repositories without breaking patterns.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;
  
  
  Try it out
&lt;/h2&gt;

&lt;p&gt;You don't even need to install it globally. Just run:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npx nodejs-quickstart-structure init
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The CLI will walk you through the setup. Within seconds, you'll have a project with &lt;strong&gt;80%+ unit test coverage&lt;/strong&gt;, &lt;strong&gt;ESLint/Prettier&lt;/strong&gt; configured, and a &lt;strong&gt;Docker Compose&lt;/strong&gt; file that actually works.&lt;/p&gt;

&lt;p&gt;Checking the official docs:&lt;br&gt;
👉 &lt;a href="https://paudang.github.io/nodejs-quickstart-structure/" rel="noopener noreferrer"&gt;Official Documentation&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Check out the repo and give it a ⭐ if it helps your workflow:&lt;br&gt;
👉 &lt;a href="https://github.com/paudang/nodejs-quickstart-structure" rel="noopener noreferrer"&gt;GitHub: paudang/nodejs-quickstart-structure&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I'd love to hear your feedback! What's the one thing you always struggle with when starting a new Node project? &lt;/p&gt;

</description>
      <category>webdev</category>
      <category>microservices</category>
      <category>node</category>
      <category>opensource</category>
    </item>
    <item>
      <title>Stop Writing Flaky Tests: The Ultimate Node.js Testing Strategy (Unit + E2E)</title>
      <dc:creator>Pau Dang</dc:creator>
      <pubDate>Mon, 23 Mar 2026 13:00:00 +0000</pubDate>
      <link>https://forem.com/paudang/stop-writing-flaky-tests-the-ultimate-nodejs-testing-strategy-unit-e2e-4d25</link>
      <guid>https://forem.com/paudang/stop-writing-flaky-tests-the-ultimate-nodejs-testing-strategy-unit-e2e-4d25</guid>
      <description>&lt;p&gt;Hi DEV community,&lt;/p&gt;

&lt;p&gt;If you are a Backend Developer working with Node.js, you have likely experienced the dreaded scenario: &lt;strong&gt;"It passes on my machine, but randomly fails on the CI/CD pipeline."&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;This phenomenon is known as &lt;strong&gt;Flaky Tests&lt;/strong&gt;. It usually stems from writing End-to-End (E2E) tests that share database states across test files, or due to network and infrastructure services (Redis, Kafka) not being fully initialized when the test begins.&lt;/p&gt;

&lt;p&gt;Today, I’m going to share the complete testing architecture and lessons I learned while building the automation framework &lt;a href="https://github.com/paudang/nodejs-quickstart-structure" rel="noopener noreferrer"&gt;nodejs-quickstart-structure&lt;/a&gt;. We will solve the core problem: &lt;strong&gt;How to build blazing fast Unit Tests and completely deterministic E2E Tests.&lt;/strong&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  1. The Crisis in 90% of Projects
&lt;/h2&gt;

&lt;p&gt;Many teams implement testing "half-heartedly":&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Unit Tests:&lt;/strong&gt; Dependencies like the Database or Redis aren't mocked, causing the tests to drag on because they wait for network I/O.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;E2E Tests:&lt;/strong&gt; Developers use their local dev database to run E2E suites. Test A creates a User, Test B checks the total number of Users. If they run in a different order, the test suite explodes! Some even use conditionals in tests: &lt;em&gt;&lt;code&gt;if (statusCode == 404) expect(404) else expect(201)&lt;/code&gt;&lt;/em&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;This is a massive Anti-pattern!&lt;/strong&gt; A test must strictly return exactly one predictable result based on static inputs.&lt;/p&gt;




&lt;h2&gt;
  
  
  2. The "Big Tech" Strategy: Draw a Hard Line
&lt;/h2&gt;

&lt;p&gt;To fix this, you must strictly delineate your boundaries:&lt;/p&gt;

&lt;h3&gt;
  
  
  Unit Tests (Fast &amp;amp; Isolated)
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Goal:&lt;/strong&gt; Verify Business Logic (Use cases, Services, Domain).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Rule:&lt;/strong&gt; MOCK EVERYTHING. No real database connections, no external APIs, no touching Redis or Kafka.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Speed:&lt;/strong&gt; Thousands of test cases should execute in less than 2 seconds.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  E2E Tests (Black-box &amp;amp; Automated Infra)
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Goal:&lt;/strong&gt; Verify the entire request flow (Route -&amp;gt; Controller -&amp;gt; Usecase -&amp;gt; Repo -&amp;gt; DB -&amp;gt; Response).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Rule:&lt;/strong&gt; Use the &lt;strong&gt;REAL Database, Redis, and Kafka&lt;/strong&gt; (spinned up via isolated Docker Containers or Testcontainers).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Characteristics:&lt;/strong&gt; Data must be TRUNCATED/Teared down before or after each test suite to guarantee a "clean room" environment. It runs slower, but absolute correctness is guaranteed.&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  3. The Recipe for Perfect E2E Tests
&lt;/h2&gt;

&lt;p&gt;To prevent E2E tests from interfering with the developer's local development environment, follow these steps and source demo &lt;a href="https://github.com/paudang/nodejs-service-redis-kafka" rel="noopener noreferrer"&gt;nodejs-service-redis-kafka&lt;/a&gt;:&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 1: Fully isolate &lt;code&gt;jest.config.js&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;Do not share your Unit test configurations with E2E. Create a dedicated &lt;code&gt;jest.e2e.config.js&lt;/code&gt; with a higher &lt;code&gt;testTimeout&lt;/code&gt; (e.g., 30 seconds to allow databases to boot).&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="nx"&gt;module&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;exports&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="p"&gt;...&lt;/span&gt;&lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;./jest.config&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
  &lt;span class="na"&gt;testMatch&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;&amp;lt;rootDir&amp;gt;/tests/e2e/**/*.test.ts&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
  &lt;span class="na"&gt;testPathIgnorePatterns&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/node_modules/&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
  &lt;span class="na"&gt;testTimeout&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;30000&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; 
  &lt;span class="na"&gt;clearMocks&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Step 2: Use Node.js Scripts to Manage Docker Lifecycle
&lt;/h3&gt;

&lt;p&gt;Instead of forcing developers to manually type &lt;code&gt;docker-compose up&lt;/code&gt; before running tests, write an automated orchestration script:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Assign a dedicated port (&lt;code&gt;PORT=3001&lt;/code&gt; instead of &lt;code&gt;3000&lt;/code&gt;) to avoid Dev Server collisions.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;execSync('docker-compose up -d db redis kafka')&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Use the &lt;code&gt;wait-on&lt;/code&gt; npm package to &lt;strong&gt;poll the healthcheck&lt;/strong&gt; until dependencies are fully green.&lt;/li&gt;
&lt;li&gt;Run &lt;code&gt;npm run test:e2e:run&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Clean up gracefully: &lt;code&gt;execSync('docker-compose down')&lt;/code&gt;.
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Wait for dependencies to prevent "Flaky connections"&lt;/span&gt;
&lt;span class="nf"&gt;execute&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`npx wait-on http-get://127.0.0.1:&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;TEST_PORT&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;/health -t 120000`&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="nf"&gt;execute&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;jest --config ./jest.e2e.config.js&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;h3&gt;
  
  
  Step 3: Fix Kafka's "read ECONNRESET" Locally
&lt;/h3&gt;

&lt;p&gt;The most common issue when testing Kafka in a local E2E run is that the tests run on the host network while Kafka is stuck in the Docker bridged network.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The Fix:&lt;/strong&gt; Explicitly map the &lt;code&gt;PLAINTEXT_HOST&lt;/code&gt; listener to a dedicated port (e.g., &lt;code&gt;9093&lt;/code&gt;):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="c1"&gt;# docker-compose.yml&lt;/span&gt;
&lt;span class="na"&gt;kafka&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;ports&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;9093:9093"&lt;/span&gt; &lt;span class="c1"&gt;# Host mapping&lt;/span&gt;
  &lt;span class="na"&gt;environment&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;KAFKA_CFG_ADVERTISED_LISTENERS=PLAINTEXT://kafka:9092,PLAINTEXT_HOST://localhost:9093&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;KAFKA_CFG_LISTENERS=PLAINTEXT://:9092,PLAINTEXT_HOST://:9093,CONTROLLER://:9094&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then, in your &lt;code&gt;.env.test&lt;/code&gt;, simply point &lt;code&gt;KAFKA_BROKER=localhost:9093&lt;/code&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 4: Write "Iron-clad" Assertions
&lt;/h3&gt;

&lt;p&gt;Eliminate loose assertions. Because your database is wiped clean (or relies on random seeds like &lt;code&gt;Date.now()&lt;/code&gt;), the output status must be absolutely rigid!&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="nf"&gt;it&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;should create a user successfully via REST&lt;/span&gt;&lt;span class="dl"&gt;'&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="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;uniqueEmail&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;`test_&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nb"&gt;Date&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;now&lt;/span&gt;&lt;span class="p"&gt;()}&lt;/span&gt;&lt;span class="s2"&gt;@example.com`&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;response&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;request&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;SERVER_URL&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;post&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/api/users&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="nf"&gt;send&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Test User&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;email&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;uniqueEmail&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;

    &lt;span class="c1"&gt;// Strictly expect a 201 Created&lt;/span&gt;
    &lt;span class="nf"&gt;expect&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;statusCode&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;toBe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;201&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  4. Conclusion
&lt;/h2&gt;

&lt;p&gt;Shifting to an isolated, automated Docker test strategy will cost you 1-2 days of initial setup infrastructure work. But in return, it brings &lt;strong&gt;absolute peace of mind&lt;/strong&gt; to the team as the codebase scales.&lt;/p&gt;

&lt;p&gt;If you find this setup process too tedious, you can simply grab the exact folder structures, Docker automation scripts, and Jest configurations that I’ve already pre-configured out of the box in my open-source CLI generator:&lt;br&gt;
👉 &lt;a href="https://github.com/paudang/nodejs-quickstart-structure" rel="noopener noreferrer"&gt;nodejs-quickstart-structure on GitHub&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Drop a star (⭐) if you find it helpful! Happy coding, and here's to never seeing &lt;code&gt;Test Failed randomly&lt;/code&gt; in GitHub Actions again!&lt;/strong&gt;&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>node</category>
      <category>testing</category>
      <category>automation</category>
    </item>
    <item>
      <title>Master Caching Patterns: A Clean Architecture Guide with AI-Native Tooling</title>
      <dc:creator>Pau Dang</dc:creator>
      <pubDate>Thu, 19 Mar 2026 15:02:06 +0000</pubDate>
      <link>https://forem.com/paudang/master-caching-patterns-a-clean-architecture-guide-with-ai-native-tooling-p7j</link>
      <guid>https://forem.com/paudang/master-caching-patterns-a-clean-architecture-guide-with-ai-native-tooling-p7j</guid>
      <description>&lt;p&gt;In high-performance systems, caching is the ultimate "lifesaver" for reducing database load and optimizing response times. However, choosing the right pattern (Cache-Aside, Write-Through, etc.) depends heavily on your specific use case. In this article, I’ll walk you through a hands-on demo project that showcases 5 essential caching patterns, built with &lt;strong&gt;Clean Architecture&lt;/strong&gt; and the power of an AI-Native tool: &lt;strong&gt;nodejs-quickstart-structure&lt;/strong&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fe02fvwulm3sdwag3w0oy.png" alt=" " width="640" height="640"&gt;
&lt;/h2&gt;

&lt;h2&gt;
  
  
  🚀 Why Caching?
&lt;/h2&gt;

&lt;p&gt;When scaling applications, the database often becomes the bottleneck. Caching strategically places frequently accessed data in memory (like Redis) to bypass slow disk I/O. But not all caching is equal. To understand the nuances, I built a showcase service from scratch.&lt;/p&gt;

&lt;p&gt;To skip the boilerplate and focus on the patterns, I used &lt;a href="https://github.com/paudang/nodejs-quickstart-structure" rel="noopener noreferrer"&gt;nodejs-quickstart-structure&lt;/a&gt;, a CLI tool designed for AI-ready, structured development.&lt;/p&gt;




&lt;h2&gt;
  
  
  📂 5 Caching Patterns You Must Know
&lt;/h2&gt;

&lt;p&gt;Here is a breakdown of the 5 patterns implemented in this demo:&lt;/p&gt;

&lt;h3&gt;
  
  
  1. Cache-Aside (Lazy Loading)
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fc2v6nge0uosjc2ljij9r.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fc2v6nge0uosjc2ljij9r.png" alt=" " width="640" height="640"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Purpose&lt;/strong&gt;: The application manages the cache. Data is only loaded into the cache when specifically requested.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;When to use&lt;/strong&gt;: Best for systems with &lt;strong&gt;Read &amp;gt;&amp;gt; Write&lt;/strong&gt; ratios (e.g., User Profiles).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Pros&lt;/strong&gt;: Memory efficiency; cache only contains data that is actually needed.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  2. Read-Through
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fix6mhsj30nhr3wi96hf3.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fix6mhsj30nhr3wi96hf3.png" alt=" " width="640" height="640"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Purpose&lt;/strong&gt;: Offloads data-fetching logic to the Cache Provider. The app simply calls "Get" from the cache.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;When to use&lt;/strong&gt;: To keep the business logic (Use Case) clean and decouple from the database.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Pros&lt;/strong&gt;: Extremely clean business code.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  3. Write-Through
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F62niw25r5y4p49hscguz.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F62niw25r5y4p49hscguz.png" alt=" " width="640" height="640"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Purpose&lt;/strong&gt;: Data is written to both the Cache and the Database simultaneously.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;When to use&lt;/strong&gt;: When &lt;strong&gt;Strong Consistency&lt;/strong&gt; is required.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Pros&lt;/strong&gt;: Minimal data inconsistency risk.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  4. Write-Around
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Furat61ubxcq86ujstqzi.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Furat61ubxcq86ujstqzi.png" alt=" " width="640" height="640"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Purpose&lt;/strong&gt;: Data is written directly to the DB, and the corresponding cache entry is invalidated (deleted).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;When to use&lt;/strong&gt;: When the written data might not be read again immediately.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Pros&lt;/strong&gt;: Prevents "polluting" the cache with data that won't be reused soon.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  5. Write-Back (Write-Behind)
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F5po6m7z4ljybcv4xxnol.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F5po6m7z4ljybcv4xxnol.png" alt=" " width="640" height="640"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Purpose&lt;/strong&gt;: Data is written to the Cache first; the DB update happens asynchronously in the background.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;When to use&lt;/strong&gt;: For &lt;strong&gt;exceptionally high write performance&lt;/strong&gt; (e.g., Logging, Real-time Metrics).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Pros&lt;/strong&gt;: Near-instant write response.&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  🛠️ Technical Implementation (Step-by-Step)
&lt;/h2&gt;

&lt;p&gt;Follow this roadmap to see how I build a production-ready demo using Clean Architecture:&lt;/p&gt;

&lt;h3&gt;
  
  
  1. Project Initialization
&lt;/h3&gt;

&lt;p&gt;Bootstrap the project using the AI-Native CLI:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npx nodejs-quickstart-structure@latest init
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;em&gt;Selection Guide:&lt;/em&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Project name: &lt;code&gt;nodejs-service-caching-pattern&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Architecture: &lt;code&gt;Clean Architecture&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Database: &lt;code&gt;MySQL&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Caching: &lt;code&gt;Redis&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Communication: &lt;code&gt;REST APIs&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  2. Defining Domain Interfaces
&lt;/h3&gt;

&lt;p&gt;Abstract the caching and repository logic to ensure true decoupling.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://github.com/paudang/nodejs-service-caching-pattern/blob/main/src/domain/services/ICacheService.ts" rel="noopener noreferrer"&gt;ICacheService.ts&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kr"&gt;interface&lt;/span&gt; &lt;span class="nx"&gt;ICacheService&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;get&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;T&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;key&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;T&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="kc"&gt;null&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;set&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;key&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;value&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;unknown&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;ttl&lt;/span&gt;&lt;span class="p"&gt;?:&lt;/span&gt; &lt;span class="kr"&gt;number&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="k"&gt;void&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;del&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;key&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="k"&gt;void&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nx"&gt;getOrSet&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;T&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;key&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;fetcher&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;Promise&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;T&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;ttl&lt;/span&gt;&lt;span class="p"&gt;?:&lt;/span&gt; &lt;span class="kr"&gt;number&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;T&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;ul&gt;
&lt;li&gt;&lt;a href="https://github.com/paudang/nodejs-service-caching-pattern/blob/main/src/domain/repositories/IUserRepository.ts" rel="noopener noreferrer"&gt;IUserRepository.ts&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  3. Infrastructure Layer
&lt;/h3&gt;

&lt;p&gt;Implementation for Redis and Sequelize (MySQL).&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://github.com/paudang/nodejs-service-caching-pattern/blob/main/src/infrastructure/caching/redisClient.ts" rel="noopener noreferrer"&gt;redisClient.ts&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/paudang/nodejs-service-caching-pattern/blob/main/src/infrastructure/repositories/UserRepository.ts" rel="noopener noreferrer"&gt;UserRepository.ts&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  4. Database Seeding: Generate 1,000 dummy users for demo purposes.
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://github.com/paudang/nodejs-service-caching-pattern/blob/main/flyway/sql/V20260319__seed_1000_users.sql" rel="noopener noreferrer"&gt;V20260319__seed_1000_users.sql&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  5. Implementing the Use Cases
&lt;/h3&gt;

&lt;p&gt;This is where the pattern logic lives.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Read-Through Example&lt;/strong&gt;:
&lt;a href="https://github.com/paudang/nodejs-service-caching-pattern/blob/main/src/usecases/getUserInfoReadThrough.ts" rel="noopener noreferrer"&gt;getUserInfoReadThrough.ts&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// The cache provider handles DB fetching upon miss&lt;/span&gt;
&lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;cacheService&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;getOrSet&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;User&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;cacheKey&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="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="k"&gt;await&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;userRepository&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;findById&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="p"&gt;},&lt;/span&gt; &lt;span class="mi"&gt;3600&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Write-Back Example&lt;/strong&gt;:
&lt;a href="https://github.com/paudang/nodejs-service-caching-pattern/blob/main/src/usecases/updateUserWriteBack.ts" rel="noopener noreferrer"&gt;updateUserWriteBack.ts&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Update cache immediately, DB updates in background&lt;/span&gt;
&lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;cacheService&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;set&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;cacheKey&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;updatedUser&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;3600&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;asyncDatabaseUpdate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;updatedUser&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; 
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You can check sample code at here:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://github.com/paudang/nodejs-service-caching-pattern/blob/main/src/usecases/createUserWriteThrough.ts" rel="noopener noreferrer"&gt;createUserWriteThrough.ts&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/paudang/nodejs-service-caching-pattern/blob/main/src/usecases/createUserWriteAround.ts" rel="noopener noreferrer"&gt;createUserWriteAround.ts&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/paudang/nodejs-service-caching-pattern/blob/main/src/usecases/updateUserWriteBack.ts" rel="noopener noreferrer"&gt;updateUserWriteBack.ts&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  6. API &amp;amp; Documentation
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://github.com/paudang/nodejs-service-caching-pattern/blob/main/src/interfaces/controllers/cacheDemoController.ts" rel="noopener noreferrer"&gt;cacheDemoController.ts&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/paudang/nodejs-service-caching-pattern/blob/main/src/config/swagger.yml" rel="noopener noreferrer"&gt;swagger.yml&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  🔍 How to Test &amp;amp; Verify
&lt;/h2&gt;

&lt;p&gt;Once deployed, you can trigger these endpoints to verify the internal logic:&lt;/p&gt;

&lt;h3&gt;
  
  
  1. Test Cache-Aside / Read-Through (Read Path)
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;First Call (Cold Cache)&lt;/strong&gt;: &lt;code&gt;curl -X GET http://localhost:3000/api/demo/cache-aside/1&lt;/code&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;em&gt;Internal:&lt;/em&gt; App checks Redis (Miss) -&amp;gt; Queries MySQL -&amp;gt; Updates Redis -&amp;gt; Returns.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;

&lt;strong&gt;Subsequent Call (Warm Cache)&lt;/strong&gt;: Repeat the command.

&lt;ul&gt;
&lt;li&gt;
&lt;em&gt;Internal:&lt;/em&gt; App checks Redis (Hit) -&amp;gt; Returns immediately with zero DB overhead.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;

&lt;h3&gt;
  
  
  2. Test Write-Through (Sync Write)
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Trigger&lt;/strong&gt;: &lt;code&gt;curl -X POST http://localhost:3000/api/demo/write-through -H "Content-Type: application/json" -d '{"name": "Performance", "email": "perf@test.com"}'&lt;/code&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;em&gt;Internal:&lt;/em&gt; Service writes to MySQL AND Redis simultaneously.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;

&lt;h3&gt;
  
  
  3. Test Write-Around (Clean Write)
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Trigger&lt;/strong&gt;: &lt;code&gt;curl -X POST http://localhost:3000/api/demo/write-around -H "Content-Type: application/json" -d '{"name": "Guest", "email": "guest@test.com"}'&lt;/code&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;em&gt;Internal:&lt;/em&gt; Writes to MySQL, then calls &lt;code&gt;DEL&lt;/code&gt; to invalidate the Redis key. The next read will be a Miss to load the fresh data.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;

&lt;h3&gt;
  
  
  4. Test Write-Back (Async Write)
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Trigger&lt;/strong&gt;: &lt;code&gt;curl -X PUT http://localhost:3000/api/demo/write-back/1 -H "Content-Type: application/json" -d '{"name": "Fast Update"}'&lt;/code&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;em&gt;Internal:&lt;/em&gt; Updates Redis immediately (super fast response). The DB update happens after a short delay asynchronously.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;




&lt;h2&gt;
  
  
  💡 Final Thoughts on AI-Native Tooling
&lt;/h2&gt;

&lt;p&gt;What makes &lt;code&gt;nodejs-quickstart-structure&lt;/code&gt; special is not just the speed of initialization, but its &lt;strong&gt;AI-Native&lt;/strong&gt; nature. With &lt;code&gt;.cursorrules&lt;/code&gt; and pre-built prompts, it ensures that your project maintains Clean Architecture and consistent coding standards automatically from development to production.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Full Source Code&lt;/strong&gt;: &lt;a href="https://github.com/paudang/nodejs-service-caching-pattern" rel="noopener noreferrer"&gt;GitHub Repository&lt;/a&gt;&lt;/p&gt;




&lt;p&gt;&lt;em&gt;I hope this guide helps you choose the right caching pattern for your next project&lt;/em&gt;&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>node</category>
      <category>redis</category>
      <category>microservices</category>
    </item>
    <item>
      <title>Why Your Docker Container Works on Windows but Fails on Linux: The Case-Sensitive Naming Nightmare</title>
      <dc:creator>Pau Dang</dc:creator>
      <pubDate>Thu, 19 Mar 2026 00:00:00 +0000</pubDate>
      <link>https://forem.com/paudang/why-your-docker-container-works-on-windows-but-fails-on-linux-the-case-sensitive-naming-nightmare-422l</link>
      <guid>https://forem.com/paudang/why-your-docker-container-works-on-windows-but-fails-on-linux-the-case-sensitive-naming-nightmare-422l</guid>
      <description>&lt;p&gt;Hi Everyone,&lt;/p&gt;

&lt;p&gt;Have you ever faced that frustrating moment: Your code runs perfectly on your local machine (Windows/macOS), but the moment you containerize it with Docker or deploy it to a Linux server, everything breaks with a cryptic &lt;code&gt;Module not found&lt;/code&gt; error?&lt;/p&gt;

&lt;p&gt;The culprit usually isn't your logic—it’s a "sweet lie" told by your operating system.&lt;/p&gt;




&lt;h3&gt;
  
  
  1. The Kernel-Level Reality Check
&lt;/h3&gt;

&lt;p&gt;The trouble starts with how different Operating Systems handle their &lt;strong&gt;File Systems&lt;/strong&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Windows (NTFS):&lt;/strong&gt; Microsoft prioritizes user convenience. To Windows, &lt;code&gt;User.service.ts&lt;/code&gt; and &lt;code&gt;user.service.ts&lt;/code&gt; are semantically the same. Therefore, NTFS is designed to be &lt;strong&gt;Case-Insensitive&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Linux (Ext4/XFS):&lt;/strong&gt; The philosophy of Linux (and Unix) is absolute precision. In ASCII, 'U' (65) and 'u' (117) are two completely different entities. Linux treats &lt;code&gt;User.ts&lt;/code&gt; and &lt;code&gt;user.ts&lt;/code&gt; as two distinct files that can coexist in the same folder.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  2. The Docker "Trap" on Local Machines
&lt;/h3&gt;

&lt;p&gt;Docker on Windows or macOS doesn't actually run directly on those kernels. It runs inside a &lt;strong&gt;Lightweight Linux VM&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;When you &lt;code&gt;mount&lt;/code&gt; (bind mount) your code from a Windows host (Case-Insensitive) into that Linux VM (Case-Sensitive):&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt; &lt;strong&gt;The Mistake:&lt;/strong&gt; You name your file &lt;code&gt;UserMapper.ts&lt;/code&gt; but write &lt;code&gt;import { UserMapper } from './usermapper'&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt; &lt;strong&gt;The Local Pass:&lt;/strong&gt; Windows tells the engine: &lt;em&gt;"Sure, I know what you mean, here is the file."&lt;/em&gt;
&lt;/li&gt;
&lt;li&gt; &lt;strong&gt;The Docker Crash:&lt;/strong&gt; The Linux environment inside Docker yells: &lt;em&gt;"I don't see any file named usermapper (lowercase) here!"&lt;/em&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;strong&gt;The Result:&lt;/strong&gt; Your system crashes at runtime or fails the build process immediately.&lt;/p&gt;




&lt;h3&gt;
  
  
  3. Vaccinating Your Node.js Projects
&lt;/h3&gt;

&lt;p&gt;Instead of relying on human memory, we should use tools to enforce consistency. In my Open Source project, &lt;strong&gt;nodejs-quickstart-structure&lt;/strong&gt;, I’ve implemented a 3-layer defense system:&lt;/p&gt;

&lt;h4&gt;
  
  
  Layer 1: ESLint Enforcement (Stop it at the source)
&lt;/h4&gt;

&lt;p&gt;We use the &lt;code&gt;eslint-plugin-import&lt;/code&gt; plugin to flag errors directly in VS Code if your import path doesn't match the actual filename 100%.&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="nl"&gt;"rules"&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;"import/no-unresolved"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"error"&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;h4&gt;
  
  
  Layer 2: CI/CD Pipeline (The Ultimate Gatekeeper)
&lt;/h4&gt;

&lt;p&gt;Our GitHub Actions run on Ubuntu (Linux). If you accidentally push a naming mismatch, the pipeline will fail during the test suite, preventing broken code from ever reaching Production.&lt;/p&gt;

&lt;h4&gt;
  
  
  Layer 3: Standardized Naming Conventions
&lt;/h4&gt;

&lt;p&gt;Stick to a strict naming convention across the entire project. Whether it's kebab-case or PascalCase, consistency is your best friend.&lt;/p&gt;

&lt;h3&gt;
  
  
  Final Thoughts
&lt;/h3&gt;

&lt;p&gt;Linux environments don't create bugs; they simply expose the inconsistencies that Windows hides from you. Synchronizing your environment from Local to Production is a hallmark of a Senior Developer.&lt;/p&gt;

&lt;p&gt;If you’re looking for a Node.js boilerplate that is pre-configured to be "immune" to these environment issues (along with Kafka, Redis, and Clean Architecture), feel free to check out my project:&lt;/p&gt;

&lt;p&gt;👉 &lt;strong&gt;GitHub&lt;/strong&gt;: &lt;a href="https://github.com/paudang/nodejs-quickstart-structure" rel="noopener noreferrer"&gt;nodejs-quickstart-structure&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;👉 &lt;strong&gt;NPM&lt;/strong&gt;: &lt;code&gt;npx nodejs-quickstart-structure init&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;I hope this saves you a few hours of meaningless debugging! Feel free to drop a ⭐ if you find the project helpful!&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>docker</category>
      <category>linux</category>
      <category>node</category>
    </item>
    <item>
      <title>15 Minutes to "Ship It": (Clean Architecture + REST API + Kafka + Docker &amp; CI/CD) From Zero to Production with Node.js</title>
      <dc:creator>Pau Dang</dc:creator>
      <pubDate>Mon, 16 Mar 2026 02:02:12 +0000</pubDate>
      <link>https://forem.com/paudang/15-minutes-to-ship-it-from-zero-to-production-with-nodejs-clean-architecture-rest-api--3l7b</link>
      <guid>https://forem.com/paudang/15-minutes-to-ship-it-from-zero-to-production-with-nodejs-clean-architecture-rest-api--3l7b</guid>
      <description>&lt;p&gt;Starting a new Node.js project often involves tedious repetitive tasks: scaffolding directory structures, setting up Express, configuring database connections, managing migrations, and integrating messaging systems like Kafka. This "boilerplate phase" can eat up hours of your initial development time.&lt;/p&gt;

&lt;p&gt;Today, I’ll show you how to go &lt;strong&gt;from zero to a production-ready environment&lt;/strong&gt; in minutes. We will build a high-performance Node.js service using &lt;strong&gt;Clean Architecture&lt;/strong&gt;, &lt;strong&gt;TypeScript&lt;/strong&gt;, &lt;strong&gt;MySQL&lt;/strong&gt;, &lt;strong&gt;Flyway&lt;/strong&gt; for database migrations, &lt;strong&gt;Kafka&lt;/strong&gt; for real-time event-driven messaging, &lt;strong&gt;Docker Compose&lt;/strong&gt; for orchestration, and &lt;strong&gt;GitHub Actions&lt;/strong&gt; for CI/CD.&lt;/p&gt;

&lt;p&gt;Let’s dive in!&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;🎯 &lt;strong&gt;"Ready-to-Run" Source Code for You:&lt;/strong&gt;&lt;br&gt;
Instead of manually copying snippets, I’ve packaged the entire source code for this article into a production-grade template on GitHub. This project has already reached &lt;strong&gt;3,000+ downloads&lt;/strong&gt; and is being used by developers for real-world services.&lt;/p&gt;

&lt;p&gt;🔗 &lt;strong&gt;&lt;a href="https://github.com/paudang/nodejs-clean-rest-kafka" rel="noopener noreferrer"&gt;Repo: paudang/nodejs-clean-rest-kafka&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;(Just &lt;code&gt;git clone&lt;/code&gt;, run &lt;code&gt;docker-compose up -d&lt;/code&gt;, and you're live!)&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  Step 1: Initialize Project &amp;amp; Install Dependencies
&lt;/h2&gt;

&lt;p&gt;First, let's create the project directory:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;mkdir &lt;/span&gt;nodejs-clean-rest-kafka
&lt;span class="nb"&gt;cd &lt;/span&gt;nodejs-clean-rest-kafka
npm init &lt;span class="nt"&gt;-y&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Install the essential production libraries:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm &lt;span class="nb"&gt;install &lt;/span&gt;express cors helmet hpp express-rate-limit dotenv morgan kafkajs sequelize mysql2 winston
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Install development dependencies:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;-D&lt;/span&gt; typescript @types/node @types/express @types/cors @types/morgan ts-node tsconfig-paths tsc-alias jest ts-jest @types/jest
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Initialize &lt;code&gt;tsconfig.json&lt;/code&gt; with Path Aliases (&lt;code&gt;@/*&lt;/code&gt;):&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;"compilerOptions"&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;"target"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"es2020"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"module"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"commonjs"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"outDir"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"./dist"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"rootDir"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"./src"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"strict"&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;"esModuleInterop"&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;"skipLibCheck"&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;"baseUrl"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&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;"paths"&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;"@/*"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"src/*"&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="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"include"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"src/**/*"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  Step 2: Architecting for Scale (Clean Architecture) 🏗️
&lt;/h2&gt;

&lt;p&gt;Using &lt;strong&gt;Clean Architecture&lt;/strong&gt; ensures your codebase remains decoupled and highly testable. We divide the source code into distinct layers:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;mkdir&lt;/span&gt; &lt;span class="nt"&gt;-p&lt;/span&gt; src/domain src/usecases src/interfaces src/infrastructure src/utils
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;src/domain&lt;/strong&gt;: Core business entities and logic (framework-independent).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;src/usecases&lt;/strong&gt;: Application-specific business rules (The "Interactors").&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;src/interfaces&lt;/strong&gt;: Adapters such as Controllers and HTTP Routes.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;src/infrastructure&lt;/strong&gt;: Technical details like Database connections, Kafka clients, and Loggers.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;src/utils&lt;/strong&gt;: Shared utility functions.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This separation allows you to swap your database or framework without touching the core business logic.&lt;/p&gt;




&lt;h2&gt;
  
  
  Step 3: Event-Driven Messaging with Kafka 🚀
&lt;/h2&gt;

&lt;p&gt;In a microservices architecture, Kafka acts as the "heartbeat" for asynchronous communication. We’ll build a &lt;strong&gt;KafkaService&lt;/strong&gt; using the &lt;strong&gt;Connection Promise&lt;/strong&gt; pattern to ensure the Producer is fully connected before sending messages, preventing data loss during startup.&lt;/p&gt;

&lt;p&gt;Inside &lt;code&gt;src/infrastructure/messaging/kafkaClient.ts&lt;/code&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="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;Kafka&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;Producer&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;kafkajs&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;KafkaService&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="nx"&gt;producer&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Producer&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="nx"&gt;isConnected&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="k"&gt;private&lt;/span&gt; &lt;span class="nx"&gt;connectionPromise&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="k"&gt;void&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="nf"&gt;constructor&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;kafka&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Kafka&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;clientId&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;user-service&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;brokers&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;localhost:9092&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;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;producer&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;kafka&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;producer&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="c1"&gt;// "Connection Promise" ensures we only connect once efficiently&lt;/span&gt;
    &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="nf"&gt;connect&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;connectionPromise&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;connectionPromise&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

        &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;connectionPromise&lt;/span&gt; &lt;span class="o"&gt;=&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="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;producer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;connect&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
            &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;isConnected&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="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;[Kafka] Producer connected successfully&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="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;connectionPromise&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="nf"&gt;sendEvent&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;topic&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;action&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;payload&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;any&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;connect&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt; &lt;span class="c1"&gt;// Always wait for readiness&lt;/span&gt;
        &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;producer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;send&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
            &lt;span class="nx"&gt;topic&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="na"&gt;messages&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[{&lt;/span&gt; &lt;span class="na"&gt;value&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;stringify&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;action&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="na"&gt;ts&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Date&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt; &lt;span class="p"&gt;}],&lt;/span&gt;
        &lt;span class="p"&gt;});&lt;/span&gt;
        &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`[Kafka] Triggered &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;action&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt; to &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;topic&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;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;kafkaService&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;KafkaService&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  Step 3.5: Scaling Consumers with Clean Interfaces 🛠️
&lt;/h2&gt;

&lt;p&gt;Managing dozens of event types can quickly become messy. We use &lt;strong&gt;Abstract Base Classes&lt;/strong&gt; and &lt;strong&gt;Schema Validation&lt;/strong&gt; to keep consumers organized:&lt;/p&gt;

&lt;h3&gt;
  
  
  1. BaseConsumer (The Blueprint)
&lt;/h3&gt;

&lt;p&gt;At &lt;code&gt;src/interfaces/messaging/baseConsumer.ts&lt;/code&gt;, we define a template for all consumers. It handles JSON parsing and error logging, so subclasses can focus solely on business logic:&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;export&lt;/span&gt; &lt;span class="kd"&gt;abstract&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;BaseConsumer&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;abstract&lt;/span&gt; &lt;span class="nx"&gt;topic&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="kd"&gt;abstract&lt;/span&gt; &lt;span class="nf"&gt;handle&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;unknown&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="k"&gt;void&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;async&lt;/span&gt; &lt;span class="nf"&gt;onMessage&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;message&lt;/span&gt; &lt;span class="p"&gt;}:&lt;/span&gt; &lt;span class="nx"&gt;EachMessagePayload&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;rawValue&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;message&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;?.&lt;/span&gt;&lt;span class="nf"&gt;toString&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;rawValue&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;data&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;parse&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;rawValue&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;handle&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  2. Schema Validation (The Contract)
&lt;/h3&gt;

&lt;p&gt;Using &lt;strong&gt;Zod&lt;/strong&gt; at &lt;code&gt;src/interfaces/messaging/schemas/userEventSchema.ts&lt;/code&gt;, we define the contract between Producer and Consumer. This ensures type safety across the wire.&lt;/p&gt;

&lt;h3&gt;
  
  
  3. WelcomeEmailConsumer (The Implementation)
&lt;/h3&gt;

&lt;p&gt;Logic that triggers an email when a &lt;code&gt;USER_CREATED&lt;/code&gt; event is received:&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;export&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;WelcomeEmailConsumer&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="nc"&gt;BaseConsumer&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;topic&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;user-topic&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="nf"&gt;handle&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;unknown&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;UserEventSchema&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;safeParse&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;data&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;result&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;success&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&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;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;action&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;USER_CREATED&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="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`[Kafka] 📧 Sending welcome email to &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;data&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;email&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;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  Step 4: Database Version Control with Flyway
&lt;/h2&gt;

&lt;p&gt;Manual database changes are a nightmare in production. &lt;strong&gt;Flyway&lt;/strong&gt; manages your schema versioning through simple SQL files.&lt;/p&gt;

&lt;p&gt;Example &lt;code&gt;V1__Create_Users_Table.sql&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;CREATE&lt;/span&gt; &lt;span class="k"&gt;TABLE&lt;/span&gt; &lt;span class="n"&gt;users&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;id&lt;/span&gt; &lt;span class="nb"&gt;INT&lt;/span&gt; &lt;span class="n"&gt;AUTO_INCREMENT&lt;/span&gt; &lt;span class="k"&gt;PRIMARY&lt;/span&gt; &lt;span class="k"&gt;KEY&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;name&lt;/span&gt; &lt;span class="nb"&gt;VARCHAR&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;255&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;NOT&lt;/span&gt; &lt;span class="k"&gt;NULL&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;email&lt;/span&gt; &lt;span class="nb"&gt;VARCHAR&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;255&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;NOT&lt;/span&gt; &lt;span class="k"&gt;NULL&lt;/span&gt; &lt;span class="k"&gt;UNIQUE&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;created_at&lt;/span&gt; &lt;span class="nb"&gt;TIMESTAMP&lt;/span&gt; &lt;span class="k"&gt;DEFAULT&lt;/span&gt; &lt;span class="k"&gt;CURRENT_TIMESTAMP&lt;/span&gt;
&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  Step 5: Clean UseCases &amp;amp; Controllers
&lt;/h2&gt;

&lt;h3&gt;
  
  
  1. UseCase (The Interactor)
&lt;/h3&gt;

&lt;p&gt;Decoupled logic at &lt;code&gt;src/usecases/createUser.ts&lt;/code&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="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;createUserUseCase&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="nx"&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="nx"&gt;email&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="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;user&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;userRepository&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;save&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;email&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt; &lt;span class="c1"&gt;// Repository abstraction&lt;/span&gt;
    &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;kafkaService&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;sendEvent&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;user-events&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;USER_CREATED&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;id&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="na"&gt;email&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;email&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;user&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  2. Controller (Interface Layer)
&lt;/h3&gt;

&lt;p&gt;The Controller's sole task is to receive requests, call use cases, and return responses. Very clean!&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;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;Request&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="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;express&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;createUserUseCase&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@/usecases/createUser&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;createUser&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="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Request&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;res&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="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="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;email&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&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;body&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;user&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;createUserUseCase&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;email&lt;/span&gt;&lt;span class="p"&gt;);&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;201&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="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;err&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&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;500&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;error&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Internal Server Error&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="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  Step 6: Docker for Production Excellence
&lt;/h2&gt;

&lt;h3&gt;
  
  
  1. &lt;code&gt;Dockerfile&lt;/code&gt; (Multi-stage Build)
&lt;/h3&gt;

&lt;p&gt;We separate the build environment from the runtime to minimize image size and attack surface:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight docker"&gt;&lt;code&gt;&lt;span class="k"&gt;FROM&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;node:22-alpine&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;AS&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;builder&lt;/span&gt;
&lt;span class="k"&gt;WORKDIR&lt;/span&gt;&lt;span class="s"&gt; /app&lt;/span&gt;
&lt;span class="k"&gt;COPY&lt;/span&gt;&lt;span class="s"&gt; package*.json ./&lt;/span&gt;
&lt;span class="k"&gt;RUN &lt;/span&gt;npm ci
&lt;span class="k"&gt;COPY&lt;/span&gt;&lt;span class="s"&gt; . .&lt;/span&gt;
&lt;span class="k"&gt;RUN &lt;/span&gt;npm run build

&lt;span class="k"&gt;FROM&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;node:22-alpine&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;AS&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;production&lt;/span&gt;
&lt;span class="k"&gt;WORKDIR&lt;/span&gt;&lt;span class="s"&gt; /app&lt;/span&gt;
&lt;span class="k"&gt;ENV&lt;/span&gt;&lt;span class="s"&gt; NODE_ENV=production&lt;/span&gt;
&lt;span class="k"&gt;ENV&lt;/span&gt;&lt;span class="s"&gt; NPM_CONFIG_UPDATE_NOTIFIER=false&lt;/span&gt;
&lt;span class="k"&gt;COPY&lt;/span&gt;&lt;span class="s"&gt; package*.json ./&lt;/span&gt;
&lt;span class="k"&gt;RUN &lt;/span&gt;npm ci &lt;span class="nt"&gt;--only&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;production
&lt;span class="k"&gt;COPY&lt;/span&gt;&lt;span class="s"&gt; --from=builder /app/dist ./dist&lt;/span&gt;
&lt;span class="k"&gt;EXPOSE&lt;/span&gt;&lt;span class="s"&gt; 3000&lt;/span&gt;
&lt;span class="k"&gt;CMD&lt;/span&gt;&lt;span class="s"&gt; ["npm", "start"]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  2. &lt;code&gt;docker-compose.yml&lt;/code&gt; (The Full Stack)
&lt;/h3&gt;

&lt;p&gt;One command to rule them all:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;services&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;app&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;build&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;.&lt;/span&gt;
    &lt;span class="na"&gt;ports&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;3000:3000"&lt;/span&gt;&lt;span class="pi"&gt;]&lt;/span&gt;
    &lt;span class="na"&gt;depends_on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;db&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;kafka&lt;/span&gt;&lt;span class="pi"&gt;]&lt;/span&gt;
    &lt;span class="na"&gt;environment&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;KAFKA_BROKER=kafka:29092&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;DB_HOST=db&lt;/span&gt;
  &lt;span class="na"&gt;db&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;image&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;mysql:8.0&lt;/span&gt;
    &lt;span class="na"&gt;ports&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;3306:3306"&lt;/span&gt;&lt;span class="pi"&gt;]&lt;/span&gt;
    &lt;span class="na"&gt;volumes&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;./flyway/sql:/docker-entrypoint-initdb.d"&lt;/span&gt;&lt;span class="pi"&gt;]&lt;/span&gt;
  &lt;span class="na"&gt;kafka&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;image&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;confluentinc/cp-kafka:7.4.0&lt;/span&gt;
    &lt;span class="na"&gt;depends_on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;zookeeper&lt;/span&gt;&lt;span class="pi"&gt;]&lt;/span&gt;
    &lt;span class="na"&gt;ports&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;9092:9092"&lt;/span&gt;&lt;span class="pi"&gt;]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  One Last Surprise... 🤫
&lt;/h2&gt;

&lt;p&gt;Think this took hours to set up?&lt;/p&gt;

&lt;p&gt;👉 &lt;strong&gt;The Truth Is:&lt;/strong&gt; The entire project structure—Clean Architecture, Kafka Producers/Consumers, Flyway migrations, Docker configs, and CI/CD pipelines—was generated in &lt;strong&gt;under 60 seconds&lt;/strong&gt; using an automation tool I built.&lt;/p&gt;

&lt;p&gt;Time-to-market is everything. Stop reinventing the wheel and start shipping business value.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Want to generate a "perfect" Node.js repo like this yourself? Try my CLI tool:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npx nodejs-quickstart-structure init
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Read the full document here: &lt;a href="https://dev.to/paudang/stop-wasting-time-on-boilerplate-generate-production-ready-nodejs-apps-in-1-minute-n1p"&gt;Nodejs Quickstart Structure&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Building production-ready software shouldn't be a chore. I hope this helps you ship your next big idea faster than ever. If you found this useful, don't forget to give a Star ⭐ on GitHub! 🔥&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>node</category>
      <category>microservices</category>
      <category>kafka</category>
    </item>
    <item>
      <title>The Hybrid Era: Why Clean Architecture needs AI-Native Scaffolding</title>
      <dc:creator>Pau Dang</dc:creator>
      <pubDate>Thu, 12 Mar 2026 14:12:37 +0000</pubDate>
      <link>https://forem.com/paudang/the-hybrid-era-why-clean-architecture-needs-ai-native-scaffolding-m5n</link>
      <guid>https://forem.com/paudang/the-hybrid-era-why-clean-architecture-needs-ai-native-scaffolding-m5n</guid>
      <description>&lt;h2&gt;
  
  
  Beyond Boilerplates: Why "Classic Engineering" is the Secret Sauce for AI Success
&lt;/h2&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;“Engineering provides the roots; AI provides the wings. A project without roots will be blown away by the first storm of complexity.”&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  The 3,000 Downloads Milestone: A Reality Check
&lt;/h3&gt;

&lt;p&gt;When my project, &lt;code&gt;nodejs-quickstart-structure&lt;/code&gt;, approached the &lt;strong&gt;3,000 downloads&lt;/strong&gt; mark on NPM, I didn't just feel pride—I felt a sense of urgency. &lt;/p&gt;

&lt;p&gt;In the age of Cursor, Claude, and Gemini, I realized that providing a "clean" structure isn't enough anymore. We are all coding with AI now, but AI has a dark secret: &lt;strong&gt;It is an "instant-noodle" developer.&lt;/strong&gt; Without a strict framework, AI will generate spaghetti code at the speed of light, breaking the very architectures we spend years mastering.&lt;/p&gt;

&lt;p&gt;That is why I released &lt;strong&gt;v1.15.0&lt;/strong&gt;. It’s not just an update; it’s a manifesto for the &lt;strong&gt;Hybrid Era of Software Engineering.&lt;/strong&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  1. The Root-and-Wing Philosophy: Amplify the Standard, Don't Mask the Flaws
&lt;/h2&gt;

&lt;p&gt;90% of current AI-driven projects are built on "shifting sands." When you feed a hollow prompt into an AI, it "hallucinates" a structure from scratch. &lt;/p&gt;

&lt;p&gt;In this ecosystem, &lt;strong&gt;The Roots&lt;/strong&gt; are MVC, Clean Architecture, and a strict 70% Unit Test coverage mandate. AI is not the architect here; it is the &lt;strong&gt;Elite Builder&lt;/strong&gt; executing a perfect, pre-defined blueprint. &lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Key Takeaway:&lt;/strong&gt; AI can only help you soar (&lt;strong&gt;The Wings&lt;/strong&gt;) if you are anchored by a solid foundation (&lt;strong&gt;The Roots&lt;/strong&gt;). We use AI to amplify high standards, not to hide technical debt.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  2. Solving the Developer’s Dilemma: Velocity vs. Integrity
&lt;/h2&gt;

&lt;p&gt;Modern developers are caught in a crossfire:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Management demands Velocity:&lt;/strong&gt; "We need this feature by tonight."&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Senior Architects demand Integrity:&lt;/strong&gt; "The code must be clean, tested, and scalable."&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Usually, you have to sacrifice one for the other. Our tool introduces &lt;strong&gt;"Guardrailed Velocity."&lt;/strong&gt; By integrating AI-native configurations directly into the scaffolding, you get the speed of AI-assisted generation without sacrificing the soul of the project. The AI is "caged" within your standards.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Key Takeaway:&lt;/strong&gt; We don’t just sell a boilerplate; we provide an &lt;strong&gt;AI-Ready Infrastructure&lt;/strong&gt;. It is the only way to move fast without breaking things.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  3. The Great Shift: From Scaffolding to Knowledge Encoding
&lt;/h2&gt;

&lt;p&gt;Traditional scaffolders are just "photocopiers"—they spit out files and leave the developer to figure out the rest. We are moving from a &lt;strong&gt;Tool&lt;/strong&gt; to an &lt;strong&gt;Ecosystem.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;With the new &lt;strong&gt;&lt;code&gt;.cursorrules&lt;/code&gt;&lt;/strong&gt; and &lt;strong&gt;&lt;code&gt;prompts/&lt;/code&gt;&lt;/strong&gt; directory (Agent Skills), we are performing &lt;strong&gt;Knowledge Encoding.&lt;/strong&gt; You aren't just generating code; you are transferring your "Architectural DNA" into the AI’s brain. &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;.cursorrules&lt;/code&gt;&lt;/strong&gt;: Acts as a permanent guardrail. The moment you open the project, the AI knows it &lt;em&gt;must&lt;/em&gt; use &lt;code&gt;ApiError&lt;/code&gt; and &lt;em&gt;must&lt;/em&gt; write tests before services.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;prompts/&lt;/code&gt;&lt;/strong&gt;: Pre-defined "Agent Skills" that teach the AI exactly how to evolve the codebase without human hand-holding.&lt;/li&gt;
&lt;/ul&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Key Takeaway:&lt;/strong&gt; You are no longer just a coder; you are a &lt;strong&gt;Technical Orchestrator&lt;/strong&gt; managing an AI swarm that has been perfectly trained on your specific codebase.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  4. Building the Future Together
&lt;/h2&gt;

&lt;p&gt;This journey from 1 to 3,000 downloads has taught me that the community doesn't just want more code—they want &lt;strong&gt;better ways to build.&lt;/strong&gt; The Hybrid Era is here. It’s time to stop treating AI like a magic wand and start treating it like a Senior Partner that needs a clear, disciplined environment to succeed.&lt;/p&gt;

&lt;p&gt;🚀 &lt;strong&gt;Try v1.15.1 now:&lt;/strong&gt; &lt;code&gt;npx nodejs-quickstart-structure&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;⭐ &lt;strong&gt;Join the movement on GitHub:&lt;/strong&gt; [&lt;a href="https://github.com/paudang/nodejs-quickstart-structure" rel="noopener noreferrer"&gt;https://github.com/paudang/nodejs-quickstart-structure&lt;/a&gt;]&lt;/p&gt;

&lt;p&gt;&lt;em&gt;If you believe that the future of coding is the perfect marriage between Classic Engineering and AI-Native speed, I’d love to have your support.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>ai</category>
      <category>opensource</category>
      <category>node</category>
    </item>
    <item>
      <title>How I Used an AI Agent to "Enforce" 70% Unit Test Coverage for 3,000 Users</title>
      <dc:creator>Pau Dang</dc:creator>
      <pubDate>Wed, 11 Mar 2026 01:09:07 +0000</pubDate>
      <link>https://forem.com/paudang/how-i-used-an-ai-agent-to-enforce-70-unit-test-coverage-for-3000-users-2aeb</link>
      <guid>https://forem.com/paudang/how-i-used-an-ai-agent-to-enforce-70-unit-test-coverage-for-3000-users-2aeb</guid>
      <description>&lt;p&gt;Hello everyone, I'm back!&lt;/p&gt;

&lt;p&gt;Following my previous post on &lt;em&gt;Zero-Downtime&lt;/em&gt;, my CLI tool &lt;code&gt;nodejs-quickstart-structure&lt;/code&gt; has fortunately reached nearly &lt;strong&gt;3,000 downloads&lt;/strong&gt;. This milestone is both a joy and a pressure. The pressure lies in: &lt;strong&gt;How do I ensure that every project initialized from the tool is truly "Production-Ready"?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The answer doesn't lie in boilerplate code, but in &lt;strong&gt;Unit Testing&lt;/strong&gt;. However, instead of just manually writing tests, I tried a more "trendy" approach: Building an &lt;strong&gt;AI Testing Agent&lt;/strong&gt; to establish a Quality Gate for the entire project.&lt;/p&gt;




&lt;h2&gt;
  
  
  1. The Pain: "I'll Write Tests Later!" ... and the Reality
&lt;/h2&gt;

&lt;p&gt;We all know Unit Testing is important. But in reality, when starting a new Node.js project:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Setting up Jest and &lt;code&gt;ts-jest&lt;/code&gt; takes time.&lt;/li&gt;
&lt;li&gt;Mocking Mongoose, Redis, and Express Request/Response is incredibly cumbersome.&lt;/li&gt;
&lt;li&gt;Defining a &lt;code&gt;tests/&lt;/code&gt; folder structure that follows Clean Architecture is a hassle.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The result? We often tell ourselves: &lt;em&gt;"Let's finish the logic first, then write tests later."&lt;/em&gt; And that "later" usually never comes. With 3,000 users relying on my tool, I couldn't let that happen.&lt;/p&gt;




&lt;h2&gt;
  
  
  2. The Solution: From AI Chatbot to AI Agent (Thinking in Loops)
&lt;/h2&gt;

&lt;p&gt;Instead of just using AI to "ghostwrite" a few isolated test snippets, I built a &lt;strong&gt;Testing Agent Skill&lt;/strong&gt;. Unlike a simple Chatbot, this Agent operates based on a Standard Operating Procedure (SOP):&lt;/p&gt;

&lt;h3&gt;
  
  
  The "Skill" Framework I established for the Agent:
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Role:&lt;/strong&gt; Senior QA &amp;amp; Testing Architect.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Standards:&lt;/strong&gt; Utilize the &lt;strong&gt;AAA Pattern&lt;/strong&gt; (Arrange - Act - Assert), 100% Isolation (never touch the real database).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Goal:&lt;/strong&gt; Enforce a minimum coverage threshold of &lt;strong&gt;70% Lines&lt;/strong&gt; and &lt;strong&gt;70% Functions&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Protocol:&lt;/strong&gt; Must list Scenarios (Happy/Sad/Edge cases) before writing any code.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Git file: &lt;a href="https://github.com/paudang/nodejs-quickstart-structure/blob/main/docs/testing-agent-standards.md" rel="noopener noreferrer"&gt;https://github.com/paudang/nodejs-quickstart-structure/blob/main/docs/testing-agent-standards.md&lt;/a&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  3. Implementation: Turning "Senior Mindset" into Automation
&lt;/h2&gt;

&lt;p&gt;I integrated this logic directly into the project through a system of &lt;strong&gt;Templates (.ejs)&lt;/strong&gt; and smart path handling.&lt;/p&gt;

&lt;h3&gt;
  
  
  The Automatic "Quality Gate" Mechanism:
&lt;/h3&gt;

&lt;p&gt;When you use the command to initialize a project:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;The tool automatically scans the &lt;code&gt;src/&lt;/code&gt; structure (Controllers, Services, Resolvers).&lt;/li&gt;
&lt;li&gt;The AI Agent suggests standard Mocks for Mongoose (e.g., &lt;code&gt;admin.ping&lt;/code&gt; failure) or GraphQL Context.&lt;/li&gt;
&lt;li&gt;All &lt;code&gt;.spec.ts&lt;/code&gt; files are generated into the &lt;code&gt;tests/&lt;/code&gt; directory with the corresponding structure.&lt;/li&gt;
&lt;/ol&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Fun fact:&lt;/strong&gt; During development, I encountered several "missing file extension" bugs due to manual path handling. I eventually refactored everything to use &lt;code&gt;path.parse()&lt;/code&gt; so the tool can run flawlessly across Windows, MacOS, and Linux.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  4. Results: Numbers Don't Lie
&lt;/h2&gt;

&lt;p&gt;Here is the Coverage report after I verified all scenarios for version &lt;strong&gt;v1.14.0&lt;/strong&gt;:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Category&lt;/th&gt;
&lt;th&gt;Coverage&lt;/th&gt;
&lt;th&gt;Requirement&lt;/th&gt;
&lt;th&gt;Status&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Lines&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;78.5%&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&amp;gt; 70%&lt;/td&gt;
&lt;td&gt;✅ PASS&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Functions&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;82.1%&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&amp;gt; 70%&lt;/td&gt;
&lt;td&gt;✅ PASS&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Branches&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;70.2%&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&amp;gt; 65%&lt;/td&gt;
&lt;td&gt;✅ PASS&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Now, every user downloading &lt;code&gt;nodejs-quickstart-structure&lt;/code&gt; has no excuse to skip testing. The hardest "skeleton" has already been prepared by the AI.&lt;/p&gt;




&lt;h2&gt;
  
  
  5. Lessons Learned: AI Doesn't Replace Us, It Levels Us Up
&lt;/h2&gt;

&lt;p&gt;Using an AI Agent to build Testing standards taught me one thing: &lt;strong&gt;AI is most powerful when placed within a standard Workflow.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;If you're struggling to set up a professional Node.js project, give my tool a try:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npx nodejs-quickstart-structure init
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And remember: &lt;strong&gt;"Quality is not an act, it is a habit."&lt;/strong&gt;&lt;/p&gt;

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

&lt;p&gt;My next goal is the &lt;strong&gt;5,000&lt;/strong&gt; downloads milestone. If you find this approach useful, please leave a Star on GitHub or comment with your thoughts on how you handle Unit Testing!&lt;/p&gt;

&lt;p&gt;GitHub Repo: &lt;a href="https://github.com/paudang/nodejs-quickstart-structure" rel="noopener noreferrer"&gt;https://github.com/paudang/nodejs-quickstart-structure&lt;/a&gt;&lt;/p&gt;




&lt;p&gt;Thanks for reading! I hope this sharing gives you a little more motivation in your "struggle" with Unit Testing.&lt;/p&gt;

</description>
      <category>ai</category>
      <category>backend</category>
      <category>microservices</category>
      <category>automation</category>
    </item>
    <item>
      <title>Stop Holding Your Breath Every Time You Deploy: Zero-Downtime Secrets for Node.js Developers</title>
      <dc:creator>Pau Dang</dc:creator>
      <pubDate>Thu, 05 Mar 2026 07:37:11 +0000</pubDate>
      <link>https://forem.com/paudang/stop-holding-your-breath-every-time-you-deploy-zero-downtime-secrets-for-nodejs-developers-3294</link>
      <guid>https://forem.com/paudang/stop-holding-your-breath-every-time-you-deploy-zero-downtime-secrets-for-nodejs-developers-3294</guid>
      <description>&lt;p&gt;Hello dev community,&lt;/p&gt;

&lt;p&gt;Have you ever experienced that "heart-pounding" feeling when typing the deploy command to Production?&lt;/p&gt;

&lt;p&gt;The terminal screen scrolls, and you hold your breath, praying that no &lt;code&gt;502 Bad Gateway&lt;/code&gt; errors appear, or worse, that your first users start complaining about sudden connection drops. In the world of modern microservices, "turning it off and on again" (Cold Restart) is a luxury we should no longer accept.&lt;/p&gt;

&lt;p&gt;Today, I’ll share three heavy-hitting "weapons" to turn your deployment process into a smooth, &lt;strong&gt;Zero-Downtime&lt;/strong&gt; experience.&lt;/p&gt;




&lt;h2&gt;
  
  
  1. Stability: The "Fail-fast" Mindset with Env Validation
&lt;/h2&gt;

&lt;p&gt;&lt;code&gt;undefined&lt;/code&gt; errors due to missing environment variables are the #1 enemy of stability. Has your app ever run for 5 minutes only to crash because of a missing API Key in the &lt;code&gt;.env&lt;/code&gt; file?&lt;/p&gt;

&lt;p&gt;Apply &lt;strong&gt;Defensive Programming&lt;/strong&gt;: Validate all environment variables as soon as the application starts (Bootstrap phase). Use &lt;strong&gt;Zod&lt;/strong&gt; for schema validation and type safety. If the configuration isn't clean, the app should never even "start its engine."&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;// src/config/env.ts&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;z&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;zod&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;dotenv&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;dotenv&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="nx"&gt;dotenv&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;config&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;envSchema&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;z&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;object&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;NODE_ENV&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;z&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;enum&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;development&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;test&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;production&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]).&lt;/span&gt;&lt;span class="k"&gt;default&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;development&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
  &lt;span class="na"&gt;PORT&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;z&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;string&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;transform&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;Number&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="k"&gt;default&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;3000&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
  &lt;span class="na"&gt;DATABASE_URL&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;z&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;string&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;url&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
  &lt;span class="na"&gt;JWT_SECRET&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;z&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;string&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;min&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;32&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;parsed&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;envSchema&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;safeParse&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="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;parsed&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;success&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;error&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 environment variables:&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;parsed&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="nf"&gt;format&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="nf"&gt;exit&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// Stop the error at the source&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;env&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;parsed&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  2. Reliability: Beyond "Up and Running" — Ensuring Application Health
&lt;/h2&gt;

&lt;p&gt;Many developers stop at just checking if the process is running. In reality, a Node.js application can be "alive" but unable to serve requests (e.g., lost Database connection).&lt;/p&gt;

&lt;h4&gt;
  
  
  Advanced Health Checks (/health)
&lt;/h4&gt;

&lt;p&gt;Your monitoring system (like a Load Balancer) needs to know the exact status of dependent resources via Deep Database Pings:&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="nx"&gt;router&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/health&lt;/span&gt;&lt;span class="dl"&gt;'&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;req&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;res&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;dbStatus&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;database&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;ping&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;healthData&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;uptime&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="nf"&gt;uptime&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="nx"&gt;dbStatus&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;UP&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;DOWN&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;timestamp&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Date&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;toISOString&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
  &lt;span class="p"&gt;};&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="nx"&gt;dbStatus&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="mi"&gt;200&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;503&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="nx"&gt;healthData&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;h4&gt;
  
  
  Graceful Shutdown: The Art of a "Smooth Exit"
&lt;/h4&gt;

&lt;p&gt;When you update code, don't just "pull the plug." Implement a workflow to handle remaining requests:&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="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;on&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;SIGTERM&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="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;logger&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;info&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;SIGTERM received. Starting graceful shutdown...&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="nx"&gt;server&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;close&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="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;database&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;disconnect&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt; 
    &lt;span class="nx"&gt;logger&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;info&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Process terminated safely.&lt;/span&gt;&lt;span class="dl"&gt;'&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="nf"&gt;exit&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  3. Deployment: Mastering the Game with PM2 Ecosystem
&lt;/h2&gt;

&lt;p&gt;On EC2 or Virtual Machines, PM2 is your most powerful operational assistant. To achieve Zero-Downtime, we need to leverage Cluster Mode and the Reload mechanism:&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;// ecosystem.config.js&lt;/span&gt;
&lt;span class="nx"&gt;module&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;exports&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;apps&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[{&lt;/span&gt;
    &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;nodejs-app&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;script&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;./dist/server.js&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;instances&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;max&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;       &lt;span class="c1"&gt;// Utilize all CPU cores&lt;/span&gt;
    &lt;span class="na"&gt;exec_mode&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;cluster&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;   
    &lt;span class="na"&gt;wait_ready&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="c1"&gt;// Wait for the app to be ready before routing traffic&lt;/span&gt;
    &lt;span class="na"&gt;listen_timeout&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;3000&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;kill_timeout&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;5000&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;     &lt;span class="c1"&gt;// Wait time for Graceful Shutdown&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;
  
  
  4. The Optimal Solution: nodejs-quickstart-structure
&lt;/h2&gt;

&lt;p&gt;Setting up all these standards manually often takes at least 2 days for every new project. That’s why the &lt;strong&gt;nodejs-quickstart-structure&lt;/strong&gt; project was born.&lt;/p&gt;

&lt;p&gt;In less than a month since its launch, this tool has reached over &lt;strong&gt;2,000+&lt;/strong&gt; downloads on NPM, proving the massive demand for a standard Node.js structure.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Initialize a Production-ready project instantly:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npx nodejs-quickstart-structure init
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Why should you try it today?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;✅ &lt;strong&gt;Architecture&lt;/strong&gt;: Choose between MVC or Clean Architecture (TypeScript).&lt;/p&gt;

&lt;p&gt;✅ &lt;strong&gt;Zero-Downtime&lt;/strong&gt;: Built-in Graceful Shutdown &amp;amp; PM2 Ecosystem.&lt;/p&gt;

&lt;p&gt;✅ &lt;strong&gt;Stability&lt;/strong&gt;: Strict environment variable control with Zod.&lt;/p&gt;

&lt;p&gt;✅ &lt;strong&gt;Security&lt;/strong&gt;: Helmet, Rate Limiting, and CORS included out of the box.&lt;/p&gt;

&lt;p&gt;✅ &lt;strong&gt;Observability&lt;/strong&gt;: Professional logging system and automated Swagger Docs.&lt;/p&gt;

&lt;p&gt;👉 Explore the source code and support the project at: &lt;a href="https://github.com/paudang/nodejs-quickstart-structure" rel="noopener noreferrer"&gt;https://github.com/paudang/nodejs-quickstart-structure&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Thanks for reading! If you find this article and tool helpful, please consider leaving a Star for the project!&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>backend</category>
      <category>microservices</category>
      <category>automation</category>
    </item>
    <item>
      <title>Beyond Try-Catch: Architecting a Production-Ready Error Handling System in Node.js</title>
      <dc:creator>Pau Dang</dc:creator>
      <pubDate>Mon, 02 Mar 2026 13:03:50 +0000</pubDate>
      <link>https://forem.com/paudang/beyond-try-catch-architecting-a-production-ready-error-handling-system-in-nodejs-2ilb</link>
      <guid>https://forem.com/paudang/beyond-try-catch-architecting-a-production-ready-error-handling-system-in-nodejs-2ilb</guid>
      <description>&lt;p&gt;Hello DEV community,&lt;/p&gt;

&lt;p&gt;In Node.js application development, &lt;code&gt;try-catch&lt;/code&gt; is just the tip of the iceberg. Catching errors is easy, but managing errors so the system remains stable and highly traceable is the true challenge for a software architect.&lt;/p&gt;

&lt;p&gt;A standard system not only answers the question "What error just occurred?", but must also answer: &lt;strong&gt;"Where did this error occur, in what context, and how much should the Client know about it?"&lt;/strong&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  1. The Essence of a Centralized Error Handling System
&lt;/h2&gt;

&lt;p&gt;To achieve consistency, we need to gather all error handling into a single centralized point. A robust Centralized Error Handling system is built on 3 core pillars:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Categorization:&lt;/strong&gt; Clearly separate operational errors (invalid input data, expired sessions) from programming/system errors (database crashes, code logic errors).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Contextual Logging:&lt;/strong&gt; Ensure every thrown error carries the "trace" of the function or module where it originated, making debugging in Production multiple times faster.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Data Sanitization:&lt;/strong&gt; Stop the risk of leaking sensitive system information to the outside world through raw error messages from the Database or Server.&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  2. The 3-Layer Strategy: Building a Solid "Defense Line"
&lt;/h2&gt;

&lt;p&gt;Instead of letting errors "drift" freely, we force them through a strict control process across 3 layers:&lt;/p&gt;

&lt;h3&gt;
  
  
  Layer 1: Error Identification with Custom Error Classes
&lt;/h3&gt;

&lt;p&gt;Don't throw a lifeless String. Build Classes that inherit from the &lt;code&gt;Error&lt;/code&gt; object (e.g., &lt;code&gt;AppError&lt;/code&gt;). Here, we attach a &lt;code&gt;statusCode&lt;/code&gt; (for standard REST responses) and an &lt;code&gt;isOperational&lt;/code&gt; flag (to know if this error is anticipated).&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;AppError&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="nc"&gt;Error&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nf"&gt;constructor&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;message&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;statusCode&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;super&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;message&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;statusCode&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;statusCode&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;isOperational&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="c1"&gt;// Mark this as a predictable operational error&lt;/span&gt;
    &lt;span class="nb"&gt;Error&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;captureStackTrace&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="kd"&gt;constructor&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Layer 2: Error Mapping &amp;amp; Contextual Catching (The Checkpoint at the Controller)
&lt;/h3&gt;

&lt;p&gt;At the Controller or Repository level, using &lt;code&gt;try-catch&lt;/code&gt; is not merely to catch errors, but to enrich the error data.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Contextual Logging: Actively log exactly which function is experiencing the issue.&lt;/li&gt;
&lt;li&gt;Error Mapping: Transform raw technical errors from the Database into user-friendly operational errors for the end user.
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Practical example down at the Controller&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;user&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;userService&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;createUser&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;body&lt;/span&gt;&lt;span class="p"&gt;);&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;201&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="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;error&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="c1"&gt;// Actively log specific context for the failing function&lt;/span&gt;
  &lt;span class="nx"&gt;logger&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`Error in [createUser] controller: &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;message&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;span class="nf"&gt;next&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="c1"&gt;// Push the error to the centralized Error Middleware&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Layer 3: Global Error Middleware - The Final Control Station
&lt;/h3&gt;

&lt;p&gt;This is the single gathering point authorized to send the Response back to the Client. Here, we perform:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Environment Filtering: In the Development environment, return full error details and stack traces. In the Production environment, return only safe messages.&lt;/li&gt;
&lt;li&gt;Professional Logging: Deeply integrate with professional Logger libraries like Winston to categorize errors by priority level (Error, Warn, Info).
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;errorMiddleware&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;err&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;res&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;next&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;let&lt;/span&gt; &lt;span class="nx"&gt;error&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;err&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="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt; &lt;span class="k"&gt;instanceof&lt;/span&gt; &lt;span class="nx"&gt;ApiError&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;statusCode&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;err&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;statusCode&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="mi"&gt;500&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;message&lt;/span&gt; &lt;span class="o"&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;message&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Internal Server Error&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nx"&gt;error&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;ApiError&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;statusCode&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;message&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;err&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;stack&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;statusCode&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;message&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;error&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;statusCode&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="mi"&gt;500&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;logger&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;error&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;span class="nx"&gt;statusCode&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;span class="nx"&gt;message&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;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;originalUrl&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;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;method&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;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;ip&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;span class="nx"&gt;logger&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;error&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;stack&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;No stack trace&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="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="nx"&gt;statusCode&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="nx"&gt;statusCode&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nx"&gt;message&lt;/span&gt;&lt;span class="p"&gt;,&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;NODE_ENV&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;development&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;stack&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;stack&lt;/span&gt; &lt;span class="p"&gt;}),&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  3. Automating Standards with nodejs-quickstart-structure
&lt;/h2&gt;

&lt;p&gt;Manually setting up the entire process above for every new project is a waste of time and prone to errors. The project &lt;code&gt;nodejs-quickstart-structure&lt;/code&gt; was born to help you enforce these architectural standards automatically and consistently.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Why should you use nodejs-quickstart-structure?&lt;/strong&gt;&lt;br&gt;
The project provides absolute flexibility by letting you choose the architecture you want, not a mandatory Framework architecture:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Built-in Centralized Error Handling System: All Middlewares, Error Classes, and Winston configurations are pre-set according to practical operational standards.&lt;/li&gt;
&lt;li&gt;Architecture Customization: You can choose MVC for lean projects or Clean Architecture to protect the integrity of Business Logic.&lt;/li&gt;
&lt;li&gt;Production-Ready: Built-in Winston Logging, basic security configurations (Helmet, CORS), and optimized Docker Multi-stage image size.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Initialize a standard project with just one command:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npx nodejs-quickstart-structure init
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



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

&lt;p&gt;A good architecture not only helps the system run precisely, but also allows the team to maintain absolute control when problems arise. Don't let those lines of try-catch just act as error catchers; turn them into tools to understand your system deeply.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;👉 Discover and contribute to the project at: &lt;a href="https://github.com/paudang/nodejs-quickstart-structure" rel="noopener noreferrer"&gt;https://github.com/paudang/nodejs-quickstart-structure&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;👉 Link document tool: &lt;a href="https://dev.to/paudang/stop-wasting-time-on-boilerplate-generate-production-ready-nodejs-apps-in-1-minute-n1p"&gt;https://dev.to/paudang/stop-wasting-time-on-boilerplate-generate-production-ready-nodejs-apps-in-1-minute-n1p&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;👉Source of the article: &lt;a href="https://medium.com/@paudang/architecting-a-production-ready-error-handling-system-in-node-js-3415e768cb70" rel="noopener noreferrer"&gt;https://medium.com/@paudang/architecting-a-production-ready-error-handling-system-in-node-js-3415e768cb70&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Thank you for reading. If you find the article and tool useful, please consider leaving a Star for the project!&lt;/p&gt;

</description>
      <category>backend</category>
      <category>node</category>
      <category>microservices</category>
      <category>webdev</category>
    </item>
  </channel>
</rss>
