<?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: Ch1ef - The Dev</title>
    <description>The latest articles on Forem by Ch1ef - The Dev (@ch1efthedev).</description>
    <link>https://forem.com/ch1efthedev</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%2F3792379%2Fc9f777e5-62a6-4a73-bd38-8652fd17d66f.jpg</url>
      <title>Forem: Ch1ef - The Dev</title>
      <link>https://forem.com/ch1efthedev</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://forem.com/feed/ch1efthedev"/>
    <language>en</language>
    <item>
      <title>How I stopped wasting 60 hours on NestJS setup (Architecture + Boilerplate)</title>
      <dc:creator>Ch1ef - The Dev</dc:creator>
      <pubDate>Wed, 25 Feb 2026 19:05:25 +0000</pubDate>
      <link>https://forem.com/ch1efthedev/how-i-stopped-wasting-60-hours-on-nestjs-setup-architecture-boilerplate-1ojp</link>
      <guid>https://forem.com/ch1efthedev/how-i-stopped-wasting-60-hours-on-nestjs-setup-architecture-boilerplate-1ojp</guid>
      <description>&lt;p&gt;Every time I started a new NestJS project, I spent the first 2-3 days doing the same thing: setting up authentication, configuring the database, implementing caching, writing guards, and organizing the project structure. By the third project, I was tired of reinventing the wheel.&lt;/p&gt;

&lt;p&gt;So I decided to build a boilerplate that would handle all the repetitive stuff — but this time, I wanted to do it &lt;em&gt;right&lt;/em&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Problem: NestJS is powerful, but setup is painful
&lt;/h2&gt;

&lt;p&gt;Don't get me wrong — NestJS is an incredible framework. But when you're building production apps, there's a huge gap between the basic CLI template and what you actually need:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Authentication &amp;amp; Authorization&lt;/strong&gt;: Not just "login works" but sessions, OTP, role-based permissions&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Database layer&lt;/strong&gt;: Migrations, transactions, proper error handling&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Caching strategy&lt;/strong&gt;: Redis integration that actually makes sense&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Testing setup&lt;/strong&gt;: Unit, integration, and E2E tests that don't fight with your DI container&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Architecture&lt;/strong&gt;: A scalable structure that doesn't turn into spaghetti after 3 months&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I tried several approaches over my last few projects:&lt;/p&gt;

&lt;h3&gt;
  
  
  Approach 1: Copy-paste from previous projects
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Problem&lt;/strong&gt;: Every project had slightly different requirements. Copy-pasting meant bringing old bugs and outdated dependencies forward.&lt;/p&gt;

&lt;h3&gt;
  
  
  Approach 2: Use existing boilerplates from GitHub
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Problem&lt;/strong&gt;: Most were either too minimal (just auth + db) or too opinionated (locked into specific libraries I didn't need). Many were abandoned after 1-2 years.&lt;/p&gt;

&lt;h3&gt;
  
  
  Approach 3: "This time I'll document everything properly"
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Problem&lt;/strong&gt;: Documentation gets outdated fast. Without automation, manual setup still took forever.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Solution: CQRS + Event-Driven Architecture
&lt;/h2&gt;

&lt;p&gt;After building my 4th project, I finally sat down and thought: &lt;em&gt;What if I automated the entire setup AND used an architecture that scales?&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;I chose &lt;strong&gt;CQRS (Command Query Responsibility Segregation)&lt;/strong&gt; because:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Separation of concerns&lt;/strong&gt;: Write operations (Commands) stay separate from read operations (Queries)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Easier testing&lt;/strong&gt;: You can test business logic without touching the database&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Event-driven side effects&lt;/strong&gt;: Send emails, clear cache, update analytics — all decoupled from your main business logic&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Scales naturally&lt;/strong&gt;: When you need to optimize reads vs writes, the separation is already there&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Here's what the structure looks like in practice:&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;// CreateProductHandler — handles the write operation&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;Logger&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;@nestjs/common&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;CommandHandler&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;EventBus&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kd"&gt;type&lt;/span&gt; &lt;span class="nx"&gt;ICommandHandler&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;@nestjs/cqrs&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;DatabaseService&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;../../../database/database.service&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;ProductResponseDto&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;../dto/product-response.dto&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;ProductCreatedEvent&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;../events/product-created.event&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;CreateProductCommand&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;./create-product.command&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="nd"&gt;CommandHandler&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;CreateProductCommand&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;CreateProductHandler&lt;/span&gt; &lt;span class="k"&gt;implements&lt;/span&gt; &lt;span class="nx"&gt;ICommandHandler&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;CreateProductCommand&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;private&lt;/span&gt; &lt;span class="k"&gt;readonly&lt;/span&gt; &lt;span class="nx"&gt;logger&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;Logger&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;CreateProductHandler&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="nf"&gt;constructor&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="k"&gt;readonly&lt;/span&gt; &lt;span class="na"&gt;database&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;DatabaseService&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="k"&gt;readonly&lt;/span&gt; &lt;span class="na"&gt;eventBus&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;EventBus&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;async&lt;/span&gt; &lt;span class="nf"&gt;execute&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="na"&gt;command&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;CreateProductCommand&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;ProductResponseDto&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;this&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;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`Creating product: &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;command&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;name&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="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;product&lt;/span&gt; &lt;span class="o"&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;database&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;product&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;create&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
      &lt;span class="na"&gt;data&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="nx"&gt;command&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;name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;description&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;command&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;description&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;price&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;command&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;price&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;stock&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;command&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;stock&lt;/span&gt; &lt;span class="o"&gt;??&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;isActive&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;command&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;isActive&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="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;logger&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;`Product created with ID: &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;product&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="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="c1"&gt;// Emit event — other parts of the system react independently&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;eventBus&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;publish&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;ProductCreatedEvent&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;product&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;product&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="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;ProductResponseDto&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
      &lt;span class="p"&gt;...&lt;/span&gt;&lt;span class="nx"&gt;product&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;price&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;Number&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;product&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;price&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;p&gt;With CQRS:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Your controllers are thin (just dispatch commands/queries)&lt;/li&gt;
&lt;li&gt;Business logic is isolated and testable&lt;/li&gt;
&lt;li&gt;Side effects don't block your main flow&lt;/li&gt;
&lt;li&gt;You can add new features without touching existing code&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  The Result: From 3 days to 5 minutes
&lt;/h2&gt;

&lt;p&gt;After implementing everything, I packaged it into a boilerplate with a single bootstrap command:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;pnpm bootstrap
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&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%2Fi0j6h2ft7a45icty4m7y.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%2Fi0j6h2ft7a45icty4m7y.gif" alt="pnpm bootstrap running" width="760" height="418"&gt;&lt;/a&gt;&lt;/p&gt;

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

&lt;ul&gt;
&lt;li&gt;✅ Installs dependencies and git hooks&lt;/li&gt;
&lt;li&gt;✅ Spins up PostgreSQL and Redis via Docker&lt;/li&gt;
&lt;li&gt;✅ Runs database migrations&lt;/li&gt;
&lt;li&gt;✅ Seeds initial data&lt;/li&gt;
&lt;li&gt;✅ Leaves everything ready — just run &lt;code&gt;pnpm dev&lt;/code&gt; to start&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Before&lt;/strong&gt;: 2-3 days of setup per project&lt;br&gt;&lt;br&gt;
&lt;strong&gt;After&lt;/strong&gt;: 5 minutes&lt;/p&gt;

&lt;h2&gt;
  
  
  What I learned building this
&lt;/h2&gt;

&lt;h3&gt;
  
  
  1. CQRS isn't overkill — it's protection
&lt;/h3&gt;

&lt;p&gt;At first, CQRS felt like over-engineering. But after the project grew past 10 modules, I realized: the separation between commands and queries kept the codebase organized. New developers onboarded faster because the patterns were consistent.&lt;/p&gt;

&lt;h3&gt;
  
  
  2. Event-driven architecture saves you from "just one more line"
&lt;/h3&gt;

&lt;p&gt;Every time you think "I'll just add this one side effect here," you're creating coupling. Event handlers keep your code clean and make refactoring painless.&lt;/p&gt;

&lt;h3&gt;
  
  
  3. Automation compounds
&lt;/h3&gt;

&lt;p&gt;Every minute spent automating setup saves hours across multiple projects. The bootstrap script alone has saved me 20+ hours this year.&lt;/p&gt;

&lt;h3&gt;
  
  
  4. Better Auth &amp;gt; Passport.js
&lt;/h3&gt;

&lt;p&gt;I switched from Passport to Better Auth and never looked back. Type-safe sessions, OTP, and RBAC in a modern API. If you're still using Passport in 2026, give Better Auth a try.&lt;/p&gt;

&lt;h3&gt;
  
  
  5. Tests need real dependencies sometimes
&lt;/h3&gt;

&lt;p&gt;I used to mock everything in tests. Now I spin up Postgres via Docker for integration tests. Slower? Yes. But the confidence level is 10x higher.&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%2F229nw1jud01si1fg362a.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%2F229nw1jud01si1fg362a.gif" alt="Test coverage" width="720" height="553"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The boilerplate ships with 327 tests across unit, integration, and E2E — 99%+ coverage across statements, branches, and lines.&lt;/p&gt;

&lt;h2&gt;
  
  
  Try it yourself
&lt;/h2&gt;

&lt;p&gt;If you're tired of rewriting the same NestJS setup code, a demo version of the boilerplate is available:&lt;/p&gt;

&lt;p&gt;🔗 &lt;strong&gt;&lt;a href="https://github.com/vinirossa/nest-api-boilerplate-demo" rel="noopener noreferrer"&gt;Demo Repository&lt;/a&gt;&lt;/strong&gt; — See the full CQRS architecture in practice&lt;/p&gt;

&lt;p&gt;The demo is a &lt;strong&gt;read-only structural preview&lt;/strong&gt; — not a runnable project. Use it to inspect the folder structure, read the full README, and see real examples of CQRS handlers, event handlers, and tests. Running &lt;code&gt;pnpm lint&lt;/code&gt; or &lt;code&gt;pnpm typecheck&lt;/code&gt; will throw errors because several modules are intentionally omitted.&lt;/p&gt;

&lt;p&gt;The demo includes:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Complete folder structure with all modules visible&lt;/li&gt;
&lt;li&gt;Real CQRS examples (commands, queries, event handlers)&lt;/li&gt;
&lt;li&gt;Example tests (unit + integration)&lt;/li&gt;
&lt;li&gt;Prisma schema with full data model&lt;/li&gt;
&lt;li&gt;Full README with architecture documentation&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%2Frzhvvi4kztg6gyd3nlrc.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%2Frzhvvi4kztg6gyd3nlrc.gif" alt="Swagger preview" width="600" height="322"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;📦 &lt;strong&gt;&lt;a href="https://ch1efthedev.gumroad.com/l/nestjs-ultimate-boilerplate" rel="noopener noreferrer"&gt;Full Version&lt;/a&gt;&lt;/strong&gt; — The complete, production-ready codebase&lt;/p&gt;

&lt;p&gt;The full version adds:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Complete authentication system (Better Auth — sessions, OTP, password reset)&lt;/li&gt;
&lt;li&gt;Role-based access control (RBAC) with full permissions&lt;/li&gt;
&lt;li&gt;Redis caching with event-driven invalidation and &lt;code&gt;@Cache()&lt;/code&gt; decorator&lt;/li&gt;
&lt;li&gt;Rate limiting pre-configured&lt;/li&gt;
&lt;li&gt;Type-safe i18n translation system&lt;/li&gt;
&lt;li&gt;Full test suite — 327 tests (unit + integration + E2E), 99%+ coverage&lt;/li&gt;
&lt;li&gt;API documentation with Swagger&lt;/li&gt;
&lt;li&gt;Git hooks with Lefthook and Commitlint&lt;/li&gt;
&lt;li&gt;Biome for linting and formatting (100x faster than ESLint)&lt;/li&gt;
&lt;li&gt;Fail-fast config validation (12-Factor App)&lt;/li&gt;
&lt;li&gt;3,600+ lines of technical documentation&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;em&gt;One-time payment. Every time I improve the stack, you get the latest version at no extra cost.&lt;/em&gt;&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;What's your experience with NestJS project setup?&lt;/strong&gt; Have you found a better way to handle the initial boilerplate? Let me know in the comments!&lt;/p&gt;

&lt;p&gt;&lt;em&gt;A backend dev building to save other devs' time. More on &lt;a href="https://github.com/vinirossa" rel="noopener noreferrer"&gt;GitHub&lt;/a&gt; and &lt;a href="https://ch1efthedev.gumroad.com" rel="noopener noreferrer"&gt;ch1efthedev.gumroad.com&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>node</category>
      <category>typescript</category>
      <category>webdev</category>
      <category>architecture</category>
    </item>
  </channel>
</rss>
