<?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: Boyinbode Ebenezer Ayomide</title>
    <description>The latest articles on Forem by Boyinbode Ebenezer Ayomide (@drbenzene).</description>
    <link>https://forem.com/drbenzene</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%2F916071%2F5cb8078e-0ae6-43f5-859b-97e6ad8dbda5.jpeg</url>
      <title>Forem: Boyinbode Ebenezer Ayomide</title>
      <link>https://forem.com/drbenzene</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://forem.com/feed/drbenzene"/>
    <language>en</language>
    <item>
      <title>The Worst Morning of My Developer Life — A Patient Hacker, a Fake AI Tool, and 150 Deleted Repos — My Story</title>
      <dc:creator>Boyinbode Ebenezer Ayomide</dc:creator>
      <pubDate>Sat, 04 Apr 2026 08:47:09 +0000</pubDate>
      <link>https://forem.com/drbenzene/the-worst-morning-of-my-developer-life-a-patient-hacker-a-fake-ai-tool-and-150-deleted-repos--iin</link>
      <guid>https://forem.com/drbenzene/the-worst-morning-of-my-developer-life-a-patient-hacker-a-fake-ai-tool-and-150-deleted-repos--iin</guid>
      <description>&lt;h1&gt;
  
  
  I Woke Up to 150+ Deleted GitHub Repos and a Ransom Note — This Is What Happened
&lt;/h1&gt;

&lt;p&gt;&lt;em&gt;A real developer. A real attack. A real recovery.&lt;/em&gt;&lt;/p&gt;




&lt;p&gt;I almost didn't write this.&lt;/p&gt;

&lt;p&gt;Partly because it's embarrassing. Partly because I'm still angry about it. But mostly because I know there are developers out there running the exact same tools I was running, making the exact same mistakes I was making — and I don't want them to wake up to what I woke up to on the morning of March 24, 2026.&lt;/p&gt;

&lt;p&gt;So here it is. The full story. No sugar coating.&lt;/p&gt;




&lt;h2&gt;
  
  
  The Morning Everything Was Gone
&lt;/h2&gt;

&lt;p&gt;I opened my laptop like any other morning. Grabbed my coffee, opened GitHub, and just… froze.&lt;/p&gt;

&lt;p&gt;My repositories were gone.&lt;/p&gt;

&lt;p&gt;Not some of them. Not a few. &lt;strong&gt;Over 150 repositories&lt;/strong&gt; across multiple organizations — personal projects, client work, production backends — all gone. Replaced by repositories with random UUID names, each containing a single file with this message:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;All production database backups have been deleted from your systems 
and securely stored in our databases. 
Contact for Backup: https://t.me/cyberluffy
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;My stomach dropped.&lt;/p&gt;

&lt;p&gt;I checked my GitHub audit log. The damage was methodical and deliberate. Someone had:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Removed all my repos from every integration — SonarCloud, Vercel, AWS Amplify, AWS Connector for GitHub, Cursor — all of it&lt;/li&gt;
&lt;li&gt;Then deleted the repositories themselves&lt;/li&gt;
&lt;li&gt;The activity was traced to IP &lt;strong&gt;188.241.177.181&lt;/strong&gt; — São Paulo, Brazil&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I had never been to Brazil.&lt;/p&gt;




&lt;h2&gt;
  
  
  My First Reaction (And What You Should Do First)
&lt;/h2&gt;

&lt;p&gt;I panicked. I won't lie. But I forced myself to slow down and think.&lt;/p&gt;

&lt;p&gt;The first thing I did was change my GitHub password and start revoking tokens. But here's what I didn't fully understand yet — &lt;strong&gt;changing your password means nothing if the attacker is using a token.&lt;/strong&gt; Tokens are independent of your password. I had to revoke everything.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;If this is happening to you right now, do this immediately:&lt;/strong&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Go to GitHub → Settings → Developer Settings → Personal Access Tokens → &lt;strong&gt;Revoke ALL&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;Settings → SSH and GPG Keys → &lt;strong&gt;Delete ALL&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;Settings → Applications → &lt;strong&gt;Remove anything suspicious&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;Settings → Security → Sessions → &lt;strong&gt;Sign out all other sessions&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;Enable 2FA if you haven't already — use an &lt;strong&gt;authenticator app, not SMS&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;Go to &lt;strong&gt;support.github.com&lt;/strong&gt; and open an emergency ticket — time matters for repo restoration&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;I opened a GitHub support ticket within the first hour. That was one of the best decisions I made that day.&lt;/p&gt;




&lt;h2&gt;
  
  
  How Did They Even Get In?
&lt;/h2&gt;

&lt;p&gt;This is the part that took me a while to figure out. And honestly, the answer surprised me.&lt;/p&gt;

&lt;p&gt;They didn't brute force my password. They didn't phish me. They didn't exploit some obscure vulnerability.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;I let them in myself. Without knowing it.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;A few weeks earlier I had come across an AI agent tool called &lt;strong&gt;OpenClaw&lt;/strong&gt;. It was marketed as a developer productivity tool — an AI gateway that could help automate workflows. I installed it, tried it out briefly, and kind of forgot about it.&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;-g&lt;/span&gt; openclaw
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That one command changed everything.&lt;/p&gt;

&lt;p&gt;What I didn't know was that OpenClaw installed a &lt;strong&gt;persistent background service&lt;/strong&gt; on my Mac — a gateway that ran 24/7, survived reboots, and had full access to my filesystem. I found it hiding in my LaunchAgents:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plist"&gt;&lt;code&gt;&lt;span class="err"&gt;~&lt;/span&gt;&lt;span class="l"&gt;/Library/LaunchAgents/ai.openclaw.gateway.plist&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Opening that file made my blood run cold:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight xml"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;key&amp;gt;&lt;/span&gt;RunAtLoad&lt;span class="nt"&gt;&amp;lt;/key&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;true/&amp;gt;&lt;/span&gt;

&lt;span class="nt"&gt;&amp;lt;key&amp;gt;&lt;/span&gt;KeepAlive&lt;span class="nt"&gt;&amp;lt;/key&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;true/&amp;gt;&lt;/span&gt;

&lt;span class="nt"&gt;&amp;lt;key&amp;gt;&lt;/span&gt;ProgramArguments&lt;span class="nt"&gt;&amp;lt;/key&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;array&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;string&amp;gt;&lt;/span&gt;/opt/homebrew/bin/node&lt;span class="nt"&gt;&amp;lt;/string&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;string&amp;gt;&lt;/span&gt;/opt/homebrew/lib/node_modules/openclaw/dist/index.js&lt;span class="nt"&gt;&amp;lt;/string&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;string&amp;gt;&lt;/span&gt;gateway&lt;span class="nt"&gt;&amp;lt;/string&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;string&amp;gt;&lt;/span&gt;--port&lt;span class="nt"&gt;&amp;lt;/string&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;string&amp;gt;&lt;/span&gt;18789&lt;span class="nt"&gt;&amp;lt;/string&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/array&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;RunAtLoad: true&lt;/code&gt;. &lt;code&gt;KeepAlive: true&lt;/code&gt;. It was designed to always be running. Always listening. On port 18789.&lt;/p&gt;

&lt;p&gt;The gateway logs told the rest of the story. This thing had been running since &lt;strong&gt;March 17&lt;/strong&gt; — a full &lt;strong&gt;7 days&lt;/strong&gt; before I noticed anything. And it wasn't just sitting there. It was connected to my &lt;strong&gt;WhatsApp&lt;/strong&gt;. It was advertising itself on my local network. It had access to everything on my machine.&lt;/p&gt;

&lt;p&gt;But the real damage came from something embarrassingly simple.&lt;/p&gt;




&lt;h2&gt;
  
  
  The $0 Mistake That Actually Caused This
&lt;/h2&gt;

&lt;p&gt;While digging through my shell history, I found this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git remote set-url origin https://drbenzene:ghp_WT&lt;span class="k"&gt;********&lt;/span&gt;oIYa57uNoVz3kkDKWXMGgOAW5N0xhm5u@github.com/company/backend_code.git
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;There it was. My GitHub Personal Access Token. Sitting in plain text in my bash history.&lt;/p&gt;

&lt;p&gt;I had done what so many developers do — embedded a token directly in a git remote URL to avoid being prompted for credentials. It's quick. It's convenient. And it is &lt;strong&gt;catastrophically dangerous.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;That token had full repository access. OpenClaw read my shell history, extracted the token, and handed it to whoever was on the other end.&lt;/p&gt;

&lt;p&gt;The full attack chain was:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;I install OpenClaw to try it out
        ↓
OpenClaw runs silently for 7 days with full filesystem access
        ↓
Reads ~/.bash_history and ~/.zsh_history
        ↓
Finds GitHub PAT stored in plain text in remote URL
        ↓
Attacker uses token to access GitHub API
        ↓
Removes repos from all integrations
        ↓
Deletes all 150+ repositories
        ↓
Creates ransom note repos
        ↓
I wake up to a nightmare
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Seven days. One npm install. One bad habit with git tokens.&lt;/p&gt;




&lt;h2&gt;
  
  
  The Investigation — What I Checked and How
&lt;/h2&gt;

&lt;p&gt;I want to be thorough here because this is the part most blog posts skip. Here's exactly what I checked to understand the full scope of the breach.&lt;/p&gt;

&lt;h3&gt;
  
  
  Checking My Mac
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;LaunchAgents and LaunchDaemons&lt;/strong&gt; — where malware hides on Mac:&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;ls&lt;/span&gt; ~/Library/LaunchAgents/
&lt;span class="nb"&gt;ls&lt;/span&gt; /Library/LaunchAgents/
&lt;span class="nb"&gt;ls&lt;/span&gt; /Library/LaunchDaemons/
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is where I found OpenClaw.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Active network connections:&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;lsof &lt;span class="nt"&gt;-i&lt;/span&gt; | &lt;span class="nb"&gt;grep &lt;/span&gt;ESTABLISHED
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Running processes:&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;ps aux
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Cron jobs:&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;crontab &lt;span class="nt"&gt;-l&lt;/span&gt;
&lt;span class="nb"&gt;sudo &lt;/span&gt;crontab &lt;span class="nt"&gt;-l&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Shell history for exposed secrets:&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;&lt;span class="nb"&gt;cat&lt;/span&gt; ~/.zsh_history | &lt;span class="nb"&gt;grep&lt;/span&gt; &lt;span class="nt"&gt;-E&lt;/span&gt; &lt;span class="s2"&gt;"ghp_|token|password|secret|key"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is where I found a &lt;strong&gt;second exposed token&lt;/strong&gt; — for another GitHub organization. I revoked it immediately.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Login history:&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;last
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Good news here — no remote logins to my Mac. The attacker never directly SSH'd into my machine. They operated entirely through the OpenClaw gateway.&lt;/p&gt;

&lt;h3&gt;
  
  
  Checking My Production Servers
&lt;/h3&gt;

&lt;p&gt;I SSH'd into my live backend server and ran:&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="c"&gt;# Who has logged in?&lt;/span&gt;
last &lt;span class="nt"&gt;-n&lt;/span&gt; 50

&lt;span class="c"&gt;# Any unauthorized users?&lt;/span&gt;
&lt;span class="nb"&gt;cat&lt;/span&gt; /etc/passwd | &lt;span class="nb"&gt;grep&lt;/span&gt; &lt;span class="nt"&gt;-v&lt;/span&gt; nologin | &lt;span class="nb"&gt;grep&lt;/span&gt; &lt;span class="nt"&gt;-v&lt;/span&gt; &lt;span class="nb"&gt;false&lt;/span&gt;

&lt;span class="c"&gt;# Recently modified files?&lt;/span&gt;
find /etc &lt;span class="nt"&gt;-mtime&lt;/span&gt; &lt;span class="nt"&gt;-7&lt;/span&gt; &lt;span class="nt"&gt;-type&lt;/span&gt; f 2&amp;gt;/dev/null

&lt;span class="c"&gt;# What's in authorized_keys?&lt;/span&gt;
&lt;span class="nb"&gt;cat&lt;/span&gt; ~/.ssh/authorized_keys
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The server was clean — no unauthorized logins, no backdoor users, no modified files. But my server's bash history also had an exposed token in it. I revoked that too.&lt;/p&gt;




&lt;h2&gt;
  
  
  The Cleanup — Exactly What I Did
&lt;/h2&gt;

&lt;h3&gt;
  
  
  1. Killed the Backdoor
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Stop OpenClaw immediately&lt;/span&gt;
launchctl unload ~/Library/LaunchAgents/ai.openclaw.gateway.plist

&lt;span class="c"&gt;# Delete the LaunchAgent&lt;/span&gt;
&lt;span class="nb"&gt;rm&lt;/span&gt; ~/Library/LaunchAgents/ai.openclaw.gateway.plist

&lt;span class="c"&gt;# Uninstall the package&lt;/span&gt;
npm uninstall &lt;span class="nt"&gt;-g&lt;/span&gt; openclaw

&lt;span class="c"&gt;# Remove all its data&lt;/span&gt;
&lt;span class="nb"&gt;rm&lt;/span&gt; &lt;span class="nt"&gt;-rf&lt;/span&gt; ~/.openclaw
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  2. Deleted All Old SSH Keys
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Remove all old keys from my Mac&lt;/span&gt;
&lt;span class="nb"&gt;rm&lt;/span&gt; ~/.ssh/id_rsa ~/.ssh/id_rsa.pub
&lt;span class="nb"&gt;rm&lt;/span&gt; ~/.ssh/id_ed25519 ~/.ssh/id_ed25519.pub
&lt;span class="nb"&gt;rm&lt;/span&gt; ~/.ssh/company_key
&lt;span class="nb"&gt;rm&lt;/span&gt; ~/.ssh/prodevs_key ~/.ssh/prodevs_key.pub
&lt;span class="nb"&gt;rm&lt;/span&gt; ~/.ssh/azure_new_key ~/.ssh/azure_new_key.pub
&lt;span class="nb"&gt;rm&lt;/span&gt; ~/.ssh/your_key_name ~/.ssh/your_key_name.pub
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Also removed the old key from my production server:&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="c"&gt;# On the server - remove the old compromised key&lt;/span&gt;
&lt;span class="nb"&gt;sed&lt;/span&gt; &lt;span class="nt"&gt;-i&lt;/span&gt; &lt;span class="s1"&gt;'/generated-by-azure/d'&lt;/span&gt; ~/.ssh/authorized_keys
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  3. Generated Fresh SSH Keys
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Dedicated key for GitHub&lt;/span&gt;
ssh-keygen &lt;span class="nt"&gt;-t&lt;/span&gt; ed25519 &lt;span class="nt"&gt;-C&lt;/span&gt; &lt;span class="s2"&gt;"my@email.com"&lt;/span&gt; &lt;span class="nt"&gt;-f&lt;/span&gt; ~/.ssh/github_key

&lt;span class="c"&gt;# One key for all servers&lt;/span&gt;
ssh-keygen &lt;span class="nt"&gt;-t&lt;/span&gt; ed25519 &lt;span class="nt"&gt;-C&lt;/span&gt; &lt;span class="s2"&gt;"my@email.com"&lt;/span&gt; &lt;span class="nt"&gt;-f&lt;/span&gt; ~/.ssh/servers_key
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Why two separate keys?&lt;/strong&gt; If one gets compromised, the other is still safe. At minimum, keep your GitHub key separate from your server keys.&lt;/p&gt;

&lt;h3&gt;
  
  
  4. Set Up SSH Config Properly
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;nano ~/.ssh/config
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ssh"&gt;&lt;code&gt;&lt;span class="k"&gt;Host&lt;/span&gt; github.com
  &lt;span class="k"&gt;HostName&lt;/span&gt; github.com
  &lt;span class="k"&gt;User&lt;/span&gt; git
  &lt;span class="k"&gt;IdentityFile&lt;/span&gt; ~/.ssh/github_key

&lt;span class="k"&gt;Host&lt;/span&gt; Company-Live
  &lt;span class="k"&gt;HostName&lt;/span&gt; YOUR_SERVER_IP
  &lt;span class="k"&gt;User&lt;/span&gt; azureuser
  &lt;span class="k"&gt;IdentityFile&lt;/span&gt; ~/.ssh/servers_key
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  5. Switched All Repos From HTTPS to SSH
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Never again will I use this format&lt;/span&gt;
&lt;span class="c"&gt;# git remote set-url origin https://user:TOKEN@github.com/org/repo.git&lt;/span&gt;

&lt;span class="c"&gt;# This is the right way&lt;/span&gt;
git remote set-url origin git@github.com:username/repo.git
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  6. Cleared My Shell History
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;cat&lt;/span&gt; /dev/null &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; ~/.bash_history
&lt;span class="nb"&gt;cat&lt;/span&gt; /dev/null &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; ~/.zsh_history
&lt;span class="nb"&gt;history&lt;/span&gt; &lt;span class="nt"&gt;-c&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  7. Ran Security Scans
&lt;/h3&gt;

&lt;p&gt;I downloaded &lt;strong&gt;KnockKnock&lt;/strong&gt; from Objective-See (objective-see.org) — it scans every persistence mechanism on your Mac. All 17 items it found were legitimate. Clean.&lt;/p&gt;

&lt;p&gt;I also ran &lt;strong&gt;Malwarebytes for Mac&lt;/strong&gt;. Clean.&lt;/p&gt;




&lt;h2&gt;
  
  
  What the Security Scans Showed
&lt;/h2&gt;

&lt;p&gt;KnockKnock scanned every category:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Launch Items&lt;/strong&gt;: 17 found — all legitimate (Docker, MySQL, Nginx, Redis, etc.)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Login Items&lt;/strong&gt;: 3 found — all legitimate (Electron app, PDFgear, Docker)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Kernel Extensions&lt;/strong&gt;: 0&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Library Inserts&lt;/strong&gt;: 0&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Library Proxies&lt;/strong&gt;: 0&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Login/Logout Hooks&lt;/strong&gt;: 0&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;No traces of OpenClaw remained. The cleanup was complete.&lt;/p&gt;




&lt;h2&gt;
  
  
  About the Ransom — Don't Pay It
&lt;/h2&gt;

&lt;p&gt;The ransom note claimed they had "securely stored backups" of all my deleted repositories. Let me be very direct: &lt;strong&gt;this is almost certainly a lie.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;GitHub repository deletion is immediate and irreversible from the attacker's side. They deleted my repos and left a note — they almost certainly do not have my code.&lt;/p&gt;

&lt;p&gt;Do not contact them. Do not pay. Instead:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Contact &lt;strong&gt;GitHub support immediately&lt;/strong&gt; — they may be able to restore recently deleted repos&lt;/li&gt;
&lt;li&gt;Check if any collaborators have local clones&lt;/li&gt;
&lt;li&gt;Check Vercel, AWS Amplify, Netlify — they often cache your code&lt;/li&gt;
&lt;li&gt;Check your own local machine for cloned copies&lt;/li&gt;
&lt;/ol&gt;




&lt;h2&gt;
  
  
  The Lessons I'm Taking Forward
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Never Embed Tokens in Git URLs
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# ❌ This stores your token in shell history forever&lt;/span&gt;
git remote set-url origin https://username:ghp_TOKEN@github.com/org/repo.git

&lt;span class="c"&gt;# ✅ Use SSH — clean, secure, no tokens exposed&lt;/span&gt;
git remote set-url origin git@github.com:org/repo.git
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Vet Every Tool You Install — Especially AI Agents
&lt;/h3&gt;

&lt;p&gt;I installed OpenClaw because it looked interesting and useful. I didn't check:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Who published the npm package&lt;/li&gt;
&lt;li&gt;What permissions it was requesting&lt;/li&gt;
&lt;li&gt;Why it needed to run as a persistent background service&lt;/li&gt;
&lt;li&gt;Whether it was open source and auditable&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;AI agent tools are a new category of developer tool that often requires deep system access — filesystem, network, environment variables. That power can be abused.&lt;/p&gt;

&lt;p&gt;Before installing any tool like this:&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="c"&gt;# Check who published it and when&lt;/span&gt;
npm info PACKAGE_NAME

&lt;span class="c"&gt;# Look at the package contents before installing&lt;/span&gt;
npx &lt;span class="nt"&gt;--yes&lt;/span&gt; npm-package-inspect PACKAGE_NAME
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Audit Your LaunchAgents Regularly
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Run this monthly&lt;/span&gt;
&lt;span class="nb"&gt;ls&lt;/span&gt; ~/Library/LaunchAgents/
&lt;span class="nb"&gt;ls&lt;/span&gt; /Library/LaunchAgents/
&lt;span class="nb"&gt;ls&lt;/span&gt; /Library/LaunchDaemons/
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Download &lt;strong&gt;KnockKnock&lt;/strong&gt; and run it monthly. It takes 2 minutes and could save you everything.&lt;/p&gt;

&lt;h3&gt;
  
  
  Use Separate SSH Keys for Separate Purposes
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;~/.ssh/github_key        → GitHub only
~/.ssh/servers_key       → All production servers
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If you manage multiple GitHub accounts:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ssh"&gt;&lt;code&gt;&lt;span class="k"&gt;Host&lt;/span&gt; github-personal
  &lt;span class="k"&gt;HostName&lt;/span&gt; github.com
  &lt;span class="k"&gt;User&lt;/span&gt; git
  &lt;span class="k"&gt;IdentityFile&lt;/span&gt; ~/.ssh/github_personal_key

&lt;span class="k"&gt;Host&lt;/span&gt; github-work
  &lt;span class="k"&gt;HostName&lt;/span&gt; github.com
  &lt;span class="k"&gt;User&lt;/span&gt; git
  &lt;span class="k"&gt;IdentityFile&lt;/span&gt; ~/.ssh/github_work_key
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Enable 2FA — But Not SMS
&lt;/h3&gt;

&lt;p&gt;SMS 2FA can be SIM-swapped. Use an authenticator app:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Google Authenticator&lt;/li&gt;
&lt;li&gt;Authy&lt;/li&gt;
&lt;li&gt;1Password (built-in TOTP)&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Never Store Secrets in Shell History
&lt;/h3&gt;

&lt;p&gt;Add this to your &lt;code&gt;.zshrc&lt;/code&gt; or &lt;code&gt;.bashrc&lt;/code&gt;:&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="c"&gt;# Prevent specific commands from being saved to history&lt;/span&gt;
&lt;span class="nb"&gt;export &lt;/span&gt;&lt;span class="nv"&gt;HISTIGNORE&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"*TOKEN*:*token*:*password*:*secret*:*ghp_*"&lt;/span&gt;

&lt;span class="c"&gt;# Or increase history size but add timestamps for auditing&lt;/span&gt;
&lt;span class="nb"&gt;export &lt;/span&gt;&lt;span class="nv"&gt;HISTTIMEFORMAT&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"%F %T "&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Better yet — use a secrets manager like &lt;strong&gt;1Password&lt;/strong&gt; or &lt;strong&gt;Bitwarden&lt;/strong&gt; and never type tokens directly into the terminal.&lt;/p&gt;




&lt;h2&gt;
  
  
  The Developer Security Checklist I Now Follow
&lt;/h2&gt;

&lt;p&gt;I built this after the incident. Review it monthly:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;GitHub:
[ ] 2FA enabled with authenticator app (not SMS)
[ ] No tokens embedded in git remote URLs
[ ] SSH used instead of HTTPS for all repos
[ ] Audit log checked for suspicious activity
[ ] Old/unused tokens revoked
[ ] SSH keys rotated every 6 months

Mac:
[ ] LaunchAgents audited monthly
[ ] KnockKnock scan run monthly  
[ ] Malwarebytes scan run monthly
[ ] All new tools vetted before installation

Servers:
[ ] authorized_keys reviewed regularly
[ ] Login history checked
[ ] No unknown user accounts
[ ] .env secrets rotated after any compromise

General:
[ ] Secrets manager in use (1Password, Bitwarden)
[ ] No plaintext tokens anywhere in shell history
[ ] .env in global .gitignore
[ ] 2FA on every critical service
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  Where Things Stand Now
&lt;/h2&gt;

&lt;p&gt;GitHub support is working on repo restoration. My Mac is confirmed clean by both KnockKnock and Malwarebytes. All credentials have been rotated. SSH is set up properly. My production server is secure.&lt;/p&gt;

&lt;p&gt;It was a brutal day. Honestly one of the worst days I've had as a developer. But looking back, every single thing that went wrong was preventable. And that's exactly why I wrote this.&lt;/p&gt;

&lt;p&gt;The attack wasn't sophisticated in some magical, unstoppable way. It worked because:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;I installed a tool without fully vetting it&lt;/li&gt;
&lt;li&gt;I had a bad habit of embedding tokens in git URLs&lt;/li&gt;
&lt;li&gt;I didn't regularly audit what was running on my machine&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;All fixable. All preventable. Starting today.&lt;/p&gt;




&lt;h2&gt;
  
  
  One Last Thing
&lt;/h2&gt;

&lt;p&gt;If you're reading this and you have tokens embedded in git remote URLs right now — please go fix them. Right now. Before you finish reading this.&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="c"&gt;# Find all repos using HTTPS with tokens&lt;/span&gt;
&lt;span class="nb"&gt;grep&lt;/span&gt; &lt;span class="nt"&gt;-r&lt;/span&gt; &lt;span class="s2"&gt;"https://.*:.*@github.com"&lt;/span&gt; ~/.gitconfig
&lt;span class="nb"&gt;grep&lt;/span&gt; &lt;span class="nt"&gt;-r&lt;/span&gt; &lt;span class="s2"&gt;"https://.*:.*@github.com"&lt;/span&gt; ~/&lt;span class="k"&gt;*&lt;/span&gt;/. git/config 2&amp;gt;/dev/null

&lt;span class="c"&gt;# Fix them&lt;/span&gt;
git remote set-url origin git@github.com:username/repo.git
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It takes 30 seconds. It could save you everything.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;If this helped you or if you've had a similar experience, I'd love to hear about it in the comments. Let's help each other stay safe out there.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;And if you found this useful — share it. Another developer somewhere needs to read this today.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Let's Connect &lt;/p&gt;

&lt;p&gt;&lt;a href="https://www.linkedin.com/in/boyinbode-ebenezer/" rel="noopener noreferrer"&gt;Linkedln&lt;/a&gt; | &lt;a href="https://github.com/drbenzene" rel="noopener noreferrer"&gt;Github&lt;/a&gt; | &lt;a href="https://twitter.com/boyinbodeee" rel="noopener noreferrer"&gt;Twitter&lt;/a&gt; | &lt;/p&gt;




&lt;p&gt;&lt;strong&gt;Tags:&lt;/strong&gt; &lt;code&gt;security&lt;/code&gt; &lt;code&gt;github&lt;/code&gt; &lt;code&gt;developer&lt;/code&gt; &lt;code&gt;ssh&lt;/code&gt; &lt;code&gt;hacking&lt;/code&gt; &lt;code&gt;incidentresponse&lt;/code&gt; &lt;code&gt;nodejs&lt;/code&gt; &lt;code&gt;devops&lt;/code&gt; &lt;code&gt;macsecurity&lt;/code&gt; &lt;code&gt;career&lt;/code&gt;&lt;/p&gt;

</description>
      <category>openclaw</category>
      <category>ai</category>
      <category>security</category>
      <category>programming</category>
    </item>
    <item>
      <title>How I Built an AI-Driven Job Automation Engine: My Hardest Engineering Lessons</title>
      <dc:creator>Boyinbode Ebenezer Ayomide</dc:creator>
      <pubDate>Sat, 14 Feb 2026 10:07:54 +0000</pubDate>
      <link>https://forem.com/drbenzene/how-i-built-an-ai-driven-job-automation-engine-my-hardest-engineering-lessons-gdb</link>
      <guid>https://forem.com/drbenzene/how-i-built-an-ai-driven-job-automation-engine-my-hardest-engineering-lessons-gdb</guid>
      <description>&lt;p&gt;Building a job automation engine sounds like a fun project until you hit the first React-based form that ignores your scripts.&lt;/p&gt;

&lt;p&gt;After months of building for one a client on a Job automation core, I've realized that modern web automation isn't about "botting"—it's about teaching code to behave with human-level nuance. If you're looking to build something similar or just want to level up your browser automation game, here is the blueprint I followed and the "gotchas" I learned along the way.&lt;/p&gt;

&lt;h2&gt;
  
  
  1. Don't Build a Scraper. Build a System.
&lt;/h2&gt;

&lt;p&gt;The biggest mistake I made early on was trying to run automation directly from my API routes. Browser instances are heavy; they spike memory and can kill your main service.&lt;/p&gt;

&lt;p&gt;My Solution: I moved everything to a distributed microservice architecture using NestJS and Bull (Redis).&lt;/p&gt;

&lt;p&gt;When you're building this, treat every automation as a background job. This gives you:&lt;/p&gt;

&lt;p&gt;Retries: If a page fails to load, the system retries with exponential backoff.&lt;br&gt;
Concurrency Control: You can limit how many browsers run at once so you don't melt your server.&lt;/p&gt;
&lt;h2&gt;
  
  
  2. Dealing with Brittle Selectors (The AI Bridge)
&lt;/h2&gt;

&lt;p&gt;If you rely on id="first_name", your code will break next week. Modern UIs change too fast.&lt;/p&gt;

&lt;p&gt;What I learned: Use LLMs as a "semantic translator." Instead of hardcoding selectors, I wrote a service that crawls the page, extracts all visible inputs/labels, and sends that "schema" to an AI.&lt;/p&gt;

&lt;p&gt;Here’s how you can implement this "discovery" phase:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// How I extract form metadata for the AI to "understand"
const fields = await page.evaluate(() =&amp;gt; {
  return Array.from(document.querySelectorAll('input, select, textarea')).map(el =&amp;gt; ({
    id: el.id,
    label: el.closest('div')?.querySelector('label')?.innerText || '',
    type: el.tagName
  }));
});

// Then, I ask the AI to map the user's resume data to these IDs
const answers = await aiService.generateAnswers(fields, resumeData);

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

&lt;/div&gt;



&lt;p&gt;By doing this, I stopped caring if the developer changed the CSS or renamed a field. The AI just "gets it."&lt;/p&gt;

&lt;h2&gt;
  
  
  3. Outsmarting the "React Void"
&lt;/h2&gt;

&lt;p&gt;This was my biggest "aha!" moment. I noticed that high-level commands like page.fill() often failed to trigger React state updates. The value would appear on the screen, but when I clicked "Submit," the form acted like it was empty.&lt;/p&gt;

&lt;p&gt;The Fix: You have to simulate the actual hardware events. I learned that focusing, typing manually, and blurring the input is the only way to satisfy modern framework listeners (like Redux or React Hook Form).&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// The "Human" way to fill a field
async function humanFill(page: Page, selector: string, value: string) {
    const input = await page.$(selector);
    await input.focus();
    await input.click();

    // Clear the field first
    await page.keyboard.press('Control+A');
    await page.keyboard.press('Backspace');

    // Type with a slight delay to mimic a human
    await page.keyboard.type(value, { delay: 40 });

    // Explicitly trigger the 'change' and 'input' events
    await input.evaluate(el =&amp;gt; {
        el.dispatchEvent(new Event('input', { bubbles: true }));
        el.dispatchEvent(new Event('change', { bubbles: true }));
    });
}

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

&lt;/div&gt;



&lt;h2&gt;
  
  
  4. The Stealth Layer (Staying Under the Radar)
&lt;/h2&gt;

&lt;p&gt;Job boards don't like bots. If you use a default headless browser, you’ll get blocked.&lt;/p&gt;

&lt;p&gt;My Tip: Always remove the navigator.webdriver flag and use a real User-Agent. But the real "pro tip" I discovered? CSP Bypassing. Sometimes site security blocks reCAPTCHA assets on certain network configurations. I used Playwright's route interceptor to fix this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// A little trick to ensure reCAPTCHA always loads correctly
await page.route('**/*.recaptcha.net/**', route =&amp;gt; {
    const url = route.request().url().replace('www.recaptcha.net', 'www.google.com');
    route.continue({ url });
});
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  5. Trust but Verify (Success Detection)
&lt;/h2&gt;

&lt;p&gt;A common mistake is assuming that if the URL changed, the submission worked. In my experience, job boards are full of "noise"—like diversity surveys that show a "Thank You" message even if your main application crashed.&lt;/p&gt;

&lt;p&gt;I learned to clone the DOM, strip out the noise (footers, survey sections), and perform a strict text search.&lt;/p&gt;

&lt;p&gt;Final Thoughts&lt;br&gt;
The web is messy. Automation is the art of handling that mess. My biggest takeaway is that human-centric automation (keystrokes, AI-mapping, event-dispatching) beats brute-force scraping every single time.&lt;/p&gt;

&lt;p&gt;If you’re building your own engine, start small, capture screenshots of every failure, and treat the browser like a living person interacting with your code.&lt;/p&gt;

&lt;p&gt;Have questions about how I handled specific anti-bot blocks or AI prompts? Drop them in the comments!&lt;/p&gt;

&lt;p&gt;Let's Connect &lt;/p&gt;

&lt;p&gt;&lt;a href="https://www.linkedin.com/in/boyinbode-ebenezer/" rel="noopener noreferrer"&gt;Linkedln&lt;/a&gt; | &lt;a href="https://github.com/drbenzene" rel="noopener noreferrer"&gt;Github&lt;/a&gt; | &lt;a href="https://twitter.com/boyinbodeee" rel="noopener noreferrer"&gt;Twitter&lt;/a&gt; | &lt;/p&gt;

</description>
      <category>ai</category>
      <category>architecture</category>
      <category>automation</category>
      <category>webscraping</category>
    </item>
    <item>
      <title>Scaling Cron Job Performance from a Few Thousands to Millions Of Process</title>
      <dc:creator>Boyinbode Ebenezer Ayomide</dc:creator>
      <pubDate>Tue, 07 Oct 2025 10:39:02 +0000</pubDate>
      <link>https://forem.com/drbenzene/scaling-cron-job-performance-from-a-few-thousands-to-millions-of-process-5165</link>
      <guid>https://forem.com/drbenzene/scaling-cron-job-performance-from-a-few-thousands-to-millions-of-process-5165</guid>
      <description>&lt;p&gt;As business applications grow and more user retention, one of the most challenging aspects is maintaining performance while serving an ever-increasing user base. In this article, we'll explore how I transformed our portfolio performance processing system from a simple linear approach to a sophisticated batching system capable of handling over 1,000,000 users efficiently.&lt;/p&gt;

&lt;h2&gt;
  
  
  WHERE DOES THE PROBLEM BEGINS
&lt;/h2&gt;

&lt;p&gt;Initially, during the early days of this startup, our system processed portfolio performance reports cron jobs, and other transactional or async processes in a straightforward manner - iterate through all users and send their monthly summaries. This worked fine when we had thousands of users, but as we scaled to over 1,000,000 active users, several critical issues emerged&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Memory Exhaustion - Loading all users into memory simultaneously&lt;/li&gt;
&lt;li&gt;Database Connection Overload - Too many concurrent database queries&lt;/li&gt;
&lt;li&gt;Email Service Rate Limiting - Overwhelming our email provider&lt;/li&gt;
&lt;li&gt;Processing Timeouts - Jobs taking hours to complete&lt;/li&gt;
&lt;li&gt;Poor Error Recovery - One failed user could break the entire process.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  THE WAY FORWARD
&lt;/h2&gt;

&lt;p&gt;My approach was to implements a two-tier batching strategy that processes users in manageable chunks while distributing the load across time intervals.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import { Injectable, Logger } from '@nestjs/common';

interface User {
  id: number;
  email: string;
  first_name: string;
  last_name: string;
  created_at: Date;
}

interface JobLog {
  id: number;
  job_name: string;
  job_status: JobLogStatus;
  job_response?: string;
  created_at: Date;
}

enum JobLogStatus {
  PENDING = 'PENDING',
  IN_PROGRESS = 'IN_PROGRESS',
  COMPLETED = 'COMPLETED',
  FAILED = 'FAILED',
}

interface TimeInterval {
  from: Date;
  to: Date;
}

interface BatchProcessingConfig {
  batchSize: number;
  delayBetweenBatches: number;
  maxRetries: number;
  concurrencyLimit: number;
}

@Injectable()
export class PortfolioPerformanceProcessor {
  private readonly logger = new Logger(PortfolioPerformanceProcessor.name);

  // Configuration for batch processing
  private readonly config: BatchProcessingConfig = {
    batchSize: 100,
    delayBetweenBatches: 2000, // 2 seconds
    maxRetries: 3,
    concurrencyLimit: 5,
  };

  constructor(
    private readonly userRepository: UserRepository,
    private readonly statisticService: StatisticService,
    private readonly mailService: MailService,
    private readonly jobLogRepository: JobLogRepository,
    private readonly loggerRepository: LoggerRepository,
  ) {}

  /**
   * Main entry point for processing monthly portfolio performance
   * Uses temporal segmentation and batching for optimal performance
   */
  async processMonthlyPortfolioPerformanceInBatches(): Promise&amp;lt;string&amp;gt; {
    const newJobLog = await this.initializeJobLog();

    try {
      // Generate monthly intervals from start date to now
      const intervals = this.generateProcessingIntervals();
      let totalProcessed = 0;
      let totalErrors = 0;

      this.logger.log(`Starting portfolio processing for ${intervals.length} time intervals`);

      // Process each time interval sequentially to manage load
      for (const [index, interval] of intervals.entries()) {
        this.logger.log(`Processing interval ${index + 1}/${intervals.length}: ${interval.from.toISOString()} to ${interval.to.toISOString()}`);

        const intervalResult = await this.processTimeInterval(interval);
        totalProcessed += intervalResult.processed;
        totalErrors += intervalResult.errors;

        // Add delay between intervals to prevent overwhelming the system
        if (index &amp;lt; intervals.length - 1) {
          await this.delay(1000);
        }
      }

      await this.completeJobLog(newJobLog.id, totalProcessed, totalErrors);
      return `Portfolio processing completed. Processed: ${totalProcessed}, Errors: ${totalErrors}`;

    } catch (error) {
      await this.failJobLog(newJobLog.id, error);
      throw error;
    }
  }

  /**
   * Process all users within a specific time interval using cursor-based pagination
   */
  private async processTimeInterval(interval: TimeInterval): Promise&amp;lt;{ processed: number; errors: number }&amp;gt; {
    let lastId: number | undefined = undefined;
    let totalProcessed = 0;
    let totalErrors = 0;
    let batch: User[];

    do {
      // Fetch users in batches using cursor-based pagination
      batch = await this.userRepository.getUsersByDateRangeInBatches(
        interval.from,
        interval.to,
        this.config.batchSize,
        lastId,
      );

      if (batch.length === 0) break;

      this.logger.debug(`Processing batch of ${batch.length} users`);

      // Process current batch with error handling
      const batchResult = await this.processBatch(batch);
      totalProcessed += batchResult.processed;
      totalErrors += batchResult.errors;

      // Update cursor for next iteration
      lastId = batch[batch.length - 1].id;

      // Add delay between batches to prevent rate limiting
      if (batch.length === this.config.batchSize) {
        await this.delay(this.config.delayBetweenBatches);
      }

    } while (batch.length === this.config.batchSize);

    return { processed: totalProcessed, errors: totalErrors };
  }
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Instead of processing all over 1 Million users at once, I segment users by their registration date into monthly intervals. This approach ensures each interval contains a manageable number of users. If one interval fails, others continue processing.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;/**
   * Process a single batch of users with concurrent processing and error handling
   */
  private async processBatch(users: User[]): Promise&amp;lt;{ processed: number; errors: number }&amp;gt; {
    const promises = users.map(user =&amp;gt; this.processUserWithRetry(user));
    const results = await Promise.allSettled(promises);

    const processed = results.filter(r =&amp;gt; r.status === 'fulfilled').length;
    const errors = results.filter(r =&amp;gt; r.status === 'rejected').length;

    // Log any rejections for monitoring
    results.forEach((result, index) =&amp;gt; {
      if (result.status === 'rejected') {
        this.logError(`Failed to process user ${users[index].id}`, result.reason);
      }
    });

    return { processed, errors };
  }

  /**
   * Process individual user with retry logic
   */
  private async processUserWithRetry(user: User, attempt: number = 1): Promise&amp;lt;void&amp;gt; {
    try {
      await this.processUser(user);
    } catch (error) {
      if (attempt &amp;lt; this.config.maxRetries) {
        this.logger.warn(`Retry ${attempt}/${this.config.maxRetries} for user ${user.id}`);
        await this.delay(1000 * attempt); // Exponential backoff
        return this.processUserWithRetry(user, attempt + 1);
      }
      throw error;
    }
  }

  /**
   * Core user processing logic
   */
  private async processUser(user: User): Promise&amp;lt;void&amp;gt; {
    try {
      // Fetch portfolio statistics
      const portfolio = await this.statisticService.getWeeklyStats(user.id);

      // Send email if user has valid email
      if (this.isValidEmail(user.email)) {
        const displayName = this.formatUserDisplayName(user);
        await this.mailService.sendWeeklyPortfolioSummary(
          user.email,
          displayName,
          portfolio,
        );
      }
    } catch (error) {
      this.logError(`Error processing portfolio for user ${user.id}`, error);
      throw error;
    }
  }

  /**
   * Generate monthly intervals for processing
   */
  private generateProcessingIntervals(): TimeInterval[] {
    const start = new Date('2022-01-01');
    const now = new Date();
    return UTILITIES.generateMonthlyIntervals(start, now);
  }

  /**
   * Initialize job logging
   */
  private async initializeJobLog(): Promise&amp;lt;JobLog&amp;gt; {
    return this.jobLogRepository.saveJobLog({
      job_name: 'processMonthlyPortfolioPerformanceInBatches',
      job_status: JobLogStatus.IN_PROGRESS,
    });
  }

  /**
   * Complete job logging with success status
   */
  private async completeJobLog(jobId: number, processed: number, errors: number): Promise&amp;lt;void&amp;gt; {
    await this.jobLogRepository.updateJobLog(jobId, {
      job_status: JobLogStatus.COMPLETED,
      job_response: `Portfolio processing completed. Processed: ${processed} users, Errors: ${errors}`,
    });
  }

  /**
   * Mark job as failed
   */
  private async failJobLog(jobId: number, error: any): Promise&amp;lt;void&amp;gt; {
    await this.jobLogRepository.updateJobLog(jobId, {
      job_status: JobLogStatus.FAILED,
      job_response: `Job failed: ${error.message}`,
    });
  }

  /**
   * Enhanced error logging with structured data
   */
  private async logError(message: string, error: any): Promise&amp;lt;void&amp;gt; {
    this.logger.error(message, error.stack);

    await this.loggerRepository.saveLogger({
      log_message: `${message}: ${error.message} - ${JSON.stringify({
        stack: error.stack,
        timestamp: new Date().toISOString(),
      })}`,
      log_level: 'ERROR',
      log_path: 'src/crons/portfolio-processor.service.ts',
    });
  }

  /**
   * Utility methods
   */
  private async delay(ms: number): Promise&amp;lt;void&amp;gt; {
    return new Promise(resolve =&amp;gt; setTimeout(resolve, ms));
  }

  private isValidEmail(email: string): boolean {
    return email &amp;amp;&amp;amp; email.includes('@') &amp;amp;&amp;amp; email.length &amp;gt; 5;
  }

  private formatUserDisplayName(user: User): string {
    return user.first_name &amp;amp;&amp;amp; user.last_name 
      ? `${user.first_name} ${user.last_name}` 
      : 'There';
  }
}

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

&lt;/div&gt;



&lt;p&gt;We process users in batches of 100, which provides the optimal balance between:&lt;/p&gt;

&lt;p&gt;Memory usage (keeping it low)&lt;br&gt;
Database connection efficiency&lt;br&gt;
Processing speed&lt;br&gt;
Error isolation&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// Enhanced Repository with optimized queries
@Injectable()
export class UserRepository {
  constructor(private readonly db: DatabaseConnection) {}

  /**
   * Optimized cursor-based pagination for large datasets
   * Uses indexed columns for efficient querying
   */
  async getUsersByDateRangeInBatches(
    fromDate: Date,
    toDate: Date,
    batchSize: number,
    lastId?: number,
  ): Promise&amp;lt;User[]&amp;gt; {
    const query = `
      SELECT id, email, first_name, last_name, created_at
      FROM users 
      WHERE created_at &amp;gt;= $1 
        AND created_at &amp;lt; $2 
        ${lastId ? 'AND id &amp;gt; $4' : ''}
        AND email IS NOT NULL 
        AND email != ''
      ORDER BY id ASC 
      LIMIT $3
    `;

    const params = lastId 
      ? [fromDate, toDate, batchSize, lastId]
      : [fromDate, toDate, batchSize];

    return this.db.query(query, params);
  }
}

// Utility class for date and batch management
export class UTILITIES {
  /**
   * Generate monthly intervals between two dates
   */
  static generateMonthlyIntervals(start: Date, end: Date): TimeInterval[] {
    const intervals: TimeInterval[] = [];
    const current = new Date(start);

    while (current &amp;lt; end) {
      const from = new Date(current);
      const to = new Date(current.getFullYear(), current.getMonth() + 1, 1);

      intervals.push({
        from,
        to: to &amp;gt; end ? end : to,
      });

      current.setMonth(current.getMonth() + 1);
    }

    return intervals;
  }

  /**
   * Async delay utility
   */
  static delay(ms: number): Promise&amp;lt;void&amp;gt; {
    return new Promise(resolve =&amp;gt; setTimeout(resolve, ms));
  }
}

// Enhanced mail service with rate limiting and retry logic
@Injectable()
export class MailService {
  private readonly logger = new Logger(MailService.name);
  private emailQueue: Array&amp;lt;{ email: string; name: string; portfolio: any }&amp;gt; = [];
  private isProcessingQueue = false;

  /**
   * Queue-based email sending to handle rate limits
   */
  async sendWeeklyPortfolioSummary(
    email: string, 
    name: string, 
    portfolio: any
  ): Promise&amp;lt;void&amp;gt; {
    // Add to queue instead of immediate sending
    this.emailQueue.push({ email, name, portfolio });

    // Start processing if not already running
    if (!this.isProcessingQueue) {
      this.processEmailQueue();
    }
  }

  /**
   * Process email queue with rate limiting
   */
  private async processEmailQueue(): Promise&amp;lt;void&amp;gt; {
    this.isProcessingQueue = true;

    while (this.emailQueue.length &amp;gt; 0) {
      const batch = this.emailQueue.splice(0, 10); // Process 10 emails at once

      const promises = batch.map(({ email, name, portfolio }) =&amp;gt; 
        this.sendEmailWithRetry(email, name, portfolio)
      );

      await Promise.allSettled(promises);

      // Rate limiting delay
      if (this.emailQueue.length &amp;gt; 0) {
        await UTILITIES.delay(1000); // 1 second between batches
      }
    }

    this.isProcessingQueue = false;
  }

  /**
   * Send individual email with retry logic
   */
  private async sendEmailWithRetry(
    email: string, 
    name: string, 
    portfolio: any,
    attempt: number = 1
  ): Promise&amp;lt;void&amp;gt; {
    try {
      // Your actual email sending logic here
      await this.actualEmailSend(email, name, portfolio);
    } catch (error) {
      if (attempt &amp;lt; 3) {
        this.logger.warn(`Email retry ${attempt}/3 for ${email}`);
        await UTILITIES.delay(2000 * attempt);
        return this.sendEmailWithRetry(email, name, portfolio, attempt + 1);
      }
      this.logger.error(`Failed to send email to ${email} after 3 attempts`, error.stack);
      throw error;
    }
  }

  private async actualEmailSend(email: string, name: string, portfolio: any): Promise&amp;lt;void&amp;gt; {
    // Implementation depends on your email provider
    // This could be SendGrid, AWS SES, etc.
  }
}

// Advanced monitoring and metrics
@Injectable()
export class ProcessingMetrics {
  private processingStats = {
    totalUsers: 0,
    processedUsers: 0,
    failedUsers: 0,
    averageProcessingTime: 0,
    emailsSent: 0,
    startTime: null as Date | null,
  };

  startProcessing(): void {
    this.processingStats.startTime = new Date();
    this.resetCounters();
  }

  recordUserProcessed(): void {
    this.processingStats.processedUsers++;
  }

  recordUserFailed(): void {
    this.processingStats.failedUsers++;
  }

  recordEmailSent(): void {
    this.processingStats.emailsSent++;
  }

  getProcessingReport(): string {
    const duration = this.processingStats.startTime 
      ? Date.now() - this.processingStats.startTime.getTime()
      : 0;

    return `
Processing Report:
- Duration: ${(duration / 1000 / 60).toFixed(2)} minutes
- Users Processed: ${this.processingStats.processedUsers}
- Users Failed: ${this.processingStats.failedUsers}
- Emails Sent: ${this.processingStats.emailsSent}
- Success Rate: ${((this.processingStats.processedUsers / (this.processingStats.processedUsers + this.processingStats.failedUsers)) * 100).toFixed(2)}%
- Processing Rate: ${(this.processingStats.processedUsers / (duration / 1000 / 60)).toFixed(2)} users/minute
    `;
  }

  private resetCounters(): void {
    this.processingStats.processedUsers = 0;
    this.processingStats.failedUsers = 0;
    this.processingStats.emailsSent = 0;
  }
}

// Database optimization utilities
export class DatabaseOptimizations {
  /**
   * Example of an optimized query with proper indexing
   */
  static getOptimizedUserQuery(): string {
    return `
      -- Ensure these indexes exist for optimal performance:
      -- CREATE INDEX CONCURRENTLY idx_users_created_at_id ON users(created_at, id);
      -- CREATE INDEX CONCURRENTLY idx_users_email_not_null ON users(email) WHERE email IS NOT NULL;

      SELECT id, email, first_name, last_name, created_at
      FROM users 
      WHERE created_at &amp;gt;= $1 
        AND created_at &amp;lt; $2 
        AND id &amp;gt; COALESCE($4, 0)
        AND email IS NOT NULL 
        AND email != ''
      ORDER BY id ASC 
      LIMIT $3
    `;
  }
}

// Configuration management
export interface SystemConfig {
  database: {
    maxConnections: number;
    queryTimeout: number;
  };
  email: {
    rateLimit: number;
    batchSize: number;
  };
  processing: {
    userBatchSize: number;
    intervalDelayMs: number;
    maxConcurrentJobs: number;
  };
}

export const PRODUCTION_CONFIG: SystemConfig = {
  database: {
    maxConnections: 20,
    queryTimeout: 30000,
  },
  email: {
    rateLimit: 100, // emails per minute
    batchSize: 10,
  },
  processing: {
    userBatchSize: 100,
    intervalDelayMs: 2000,
    maxConcurrentJobs: 3,
  },
};
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Scaling from thousands to millions of users requires fundamental changes in how we approach batch processing. The key is breaking down large problems into manageable chunks while maintaining system reliability and performance.&lt;/p&gt;

&lt;p&gt;The batching strategy reduced our processing time from over 6 hours to under 45 minutes while improving reliability and monitoring capabilities. Most importantly, it's designed to scale further as our user base continues to grow.&lt;/p&gt;

&lt;p&gt;Always remember, premature optimization is the root of all evil, but once you hit scale, proper optimization becomes essential for business continuity.&lt;/p&gt;

&lt;p&gt;Let's Connect &lt;/p&gt;

&lt;p&gt;&lt;a href="https://www.linkedin.com/in/boyinbode-ebenezer/" rel="noopener noreferrer"&gt;Linkedln&lt;/a&gt; | &lt;a href="https://github.com/drbenzene" rel="noopener noreferrer"&gt;Github&lt;/a&gt; | &lt;a href="https://twitter.com/boyinbodeee" rel="noopener noreferrer"&gt;Twitter&lt;/a&gt; | &lt;a href="https://www.youtube.com/channel/UCLv47WaIjDmnROud6FRmPJg" rel="noopener noreferrer"&gt;Youtube&lt;/a&gt;&lt;/p&gt;

</description>
      <category>architecture</category>
      <category>backend</category>
      <category>performance</category>
    </item>
    <item>
      <title>Advanced NestJS: Hidden Gems for Senior Backend Engineers</title>
      <dc:creator>Boyinbode Ebenezer Ayomide</dc:creator>
      <pubDate>Mon, 30 Jun 2025 08:53:15 +0000</pubDate>
      <link>https://forem.com/drbenzene/advanced-nestjs-hidden-gems-for-senior-backend-engineers-207o</link>
      <guid>https://forem.com/drbenzene/advanced-nestjs-hidden-gems-for-senior-backend-engineers-207o</guid>
      <description>&lt;div class="ltag__link--embedded"&gt;
  &lt;div class="crayons-story "&gt;
  &lt;a href="https://dev.to/drbenzene/advanced-nestjs-hidden-gems-for-senior-backend-engineers-47bd" class="crayons-story__hidden-navigation-link"&gt;Advanced NestJS: Hidden Gems for Senior Backend Engineers&lt;/a&gt;


  &lt;div class="crayons-story__body crayons-story__body-full_post"&gt;
    &lt;div class="crayons-story__top"&gt;
      &lt;div class="crayons-story__meta"&gt;
        &lt;div class="crayons-story__author-pic"&gt;

          &lt;a href="/drbenzene" class="crayons-avatar  crayons-avatar--l  "&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%2Fuser%2Fprofile_image%2F916071%2F5cb8078e-0ae6-43f5-859b-97e6ad8dbda5.jpeg" alt="drbenzene profile" class="crayons-avatar__image"&gt;
          &lt;/a&gt;
        &lt;/div&gt;
        &lt;div&gt;
          &lt;div&gt;
            &lt;a href="/drbenzene" class="crayons-story__secondary fw-medium m:hidden"&gt;
              Boyinbode Ebenezer Ayomide
            &lt;/a&gt;
            &lt;div class="profile-preview-card relative mb-4 s:mb-0 fw-medium hidden m:inline-block"&gt;
              
                Boyinbode Ebenezer Ayomide
                
              
              &lt;div id="story-author-preview-content-2638534" class="profile-preview-card__content crayons-dropdown branded-7 p-4 pt-0"&gt;
                &lt;div class="gap-4 grid"&gt;
                  &lt;div class="-mt-4"&gt;
                    &lt;a href="/drbenzene" class="flex"&gt;
                      &lt;span class="crayons-avatar crayons-avatar--xl mr-2 shrink-0"&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%2Fuser%2Fprofile_image%2F916071%2F5cb8078e-0ae6-43f5-859b-97e6ad8dbda5.jpeg" class="crayons-avatar__image" alt=""&gt;
                      &lt;/span&gt;
                      &lt;span class="crayons-link crayons-subtitle-2 mt-5"&gt;Boyinbode Ebenezer Ayomide&lt;/span&gt;
                    &lt;/a&gt;
                  &lt;/div&gt;
                  &lt;div class="print-hidden"&gt;
                    
                      Follow
                    
                  &lt;/div&gt;
                  &lt;div class="author-preview-metadata-container"&gt;&lt;/div&gt;
                &lt;/div&gt;
              &lt;/div&gt;
            &lt;/div&gt;

          &lt;/div&gt;
          &lt;a href="https://dev.to/drbenzene/advanced-nestjs-hidden-gems-for-senior-backend-engineers-47bd" class="crayons-story__tertiary fs-xs"&gt;&lt;time&gt;Jun 30 '25&lt;/time&gt;&lt;span class="time-ago-indicator-initial-placeholder"&gt;&lt;/span&gt;&lt;/a&gt;
        &lt;/div&gt;
      &lt;/div&gt;

    &lt;/div&gt;

    &lt;div class="crayons-story__indention"&gt;
      &lt;h2 class="crayons-story__title crayons-story__title-full_post"&gt;
        &lt;a href="https://dev.to/drbenzene/advanced-nestjs-hidden-gems-for-senior-backend-engineers-47bd" id="article-link-2638534"&gt;
          Advanced NestJS: Hidden Gems for Senior Backend Engineers
        &lt;/a&gt;
      &lt;/h2&gt;
        &lt;div class="crayons-story__tags"&gt;
        &lt;/div&gt;
      &lt;div class="crayons-story__bottom"&gt;
        &lt;div class="crayons-story__details"&gt;
          &lt;a href="https://dev.to/drbenzene/advanced-nestjs-hidden-gems-for-senior-backend-engineers-47bd" class="crayons-btn crayons-btn--s crayons-btn--ghost crayons-btn--icon-left"&gt;
            &lt;div class="multiple_reactions_aggregate"&gt;
              &lt;span class="multiple_reactions_icons_container"&gt;
                  &lt;span class="crayons_icon_container"&gt;
                    &lt;img src="https://assets.dev.to/assets/exploding-head-daceb38d627e6ae9b730f36a1e390fca556a4289d5a41abb2c35068ad3e2c4b5.svg" width="18" height="18"&gt;
                  &lt;/span&gt;
                  &lt;span class="crayons_icon_container"&gt;
                    &lt;img src="https://assets.dev.to/assets/multi-unicorn-b44d6f8c23cdd00964192bedc38af3e82463978aa611b4365bd33a0f1f4f3e97.svg" width="18" height="18"&gt;
                  &lt;/span&gt;
                  &lt;span class="crayons_icon_container"&gt;
                    &lt;img src="https://assets.dev.to/assets/sparkle-heart-5f9bee3767e18deb1bb725290cb151c25234768a0e9a2bd39370c382d02920cf.svg" width="18" height="18"&gt;
                  &lt;/span&gt;
              &lt;/span&gt;
              &lt;span class="aggregate_reactions_counter"&gt;5&lt;span class="hidden s:inline"&gt; reactions&lt;/span&gt;&lt;/span&gt;
            &lt;/div&gt;
          &lt;/a&gt;
            &lt;a href="https://dev.to/drbenzene/advanced-nestjs-hidden-gems-for-senior-backend-engineers-47bd#comments" class="crayons-btn crayons-btn--s crayons-btn--ghost crayons-btn--icon-left flex items-center"&gt;
              Comments


              &lt;span class="hidden s:inline"&gt;Add Comment&lt;/span&gt;
            &lt;/a&gt;
        &lt;/div&gt;
        &lt;div class="crayons-story__save"&gt;
          &lt;small class="crayons-story__tertiary fs-xs mr-2"&gt;
            9 min read
          &lt;/small&gt;
            
              &lt;span class="bm-initial"&gt;
                

              &lt;/span&gt;
              &lt;span class="bm-success"&gt;
                

              &lt;/span&gt;
            
        &lt;/div&gt;
      &lt;/div&gt;
    &lt;/div&gt;
  &lt;/div&gt;
&lt;/div&gt;

&lt;/div&gt;


</description>
    </item>
    <item>
      <title>Advanced NestJS: Hidden Gems for Senior Backend Engineers</title>
      <dc:creator>Boyinbode Ebenezer Ayomide</dc:creator>
      <pubDate>Mon, 30 Jun 2025 08:52:38 +0000</pubDate>
      <link>https://forem.com/drbenzene/advanced-nestjs-hidden-gems-for-senior-backend-engineers-47bd</link>
      <guid>https://forem.com/drbenzene/advanced-nestjs-hidden-gems-for-senior-backend-engineers-47bd</guid>
      <description>&lt;p&gt;Most times when we think of NestJS, we picture decorators, dependency injection, and clean architecture. But beneath the surface lies a treasure trove of advanced features that can transform how you build scalable backend applications. After years of building enterprise-grade NestJS applications, I've discovered patterns and techniques that rarely make it into tutorials but are essential for senior-level development.&lt;/p&gt;




&lt;h2&gt;
  
  
  Custom Metadata Reflection: Building Intelligent Decorators
&lt;/h2&gt;

&lt;p&gt;Standard decorators are limited in their ability to store and retrieve complex configuration data. You need a way to attach sophisticated metadata to classes and methods that can be accessed at runtime.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// metadata.constants.ts
export const CACHE_CONFIG_METADATA = Symbol('cache-config');
export const RATE_LIMIT_METADATA = Symbol('rate-limit');

// cache-config.decorator.ts
import { SetMetadata } from '@nestjs/common';

export interface CacheConfig {
  ttl: number;
  key?: string;
  condition?: (args: any[]) =&amp;gt; boolean;
  tags?: string[];
}

export const CacheConfig = (config: CacheConfig) =&amp;gt; 
  SetMetadata(CACHE_CONFIG_METADATA, config);

// Advanced usage with conditional caching
@CacheConfig({
  ttl: 300,
  key: 'user-profile-{{userId}}',
  condition: (args) =&amp;gt; args[0]?.cacheEnabled !== false,
  tags: ['user', 'profile']
})
async getUserProfile(userId: string, options?: GetUserOptions) {
  // method implementation
}

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

&lt;/div&gt;



&lt;h2&gt;
  
  
  Smart Interceptor Using Metadata
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// cache.interceptor.ts
import { Injectable, NestInterceptor, ExecutionContext, CallHandler } from '@nestjs/common';
import { Reflector } from '@nestjs/core';
import { Observable, of } from 'rxjs';
import { tap } from 'rxjs/operators';

@Injectable()
export class SmartCacheInterceptor implements NestInterceptor {
  constructor(
    private reflector: Reflector,
    private cacheService: CacheService
  ) {}

  intercept(context: ExecutionContext, next: CallHandler): Observable&amp;lt;any&amp;gt; {
    const cacheConfig = this.reflector.get&amp;lt;CacheConfig&amp;gt;(
      CACHE_CONFIG_METADATA,
      context.getHandler()
    );

    if (!cacheConfig) {
      return next.handle();
    }

    const request = context.switchToHttp().getRequest();
    const args = context.getArgs();

    // Check condition
    if (cacheConfig.condition &amp;amp;&amp;amp; !cacheConfig.condition(args)) {
      return next.handle();
    }

    // Generate cache key with template replacement
    const cacheKey = this.generateCacheKey(cacheConfig.key, request, args);

    // Try to get from cache
    const cachedResult = this.cacheService.get(cacheKey);
    if (cachedResult) {
      return of(cachedResult);
    }

    // Execute and cache result
    return next.handle().pipe(
      tap(result =&amp;gt; {
        this.cacheService.set(cacheKey, result, {
          ttl: cacheConfig.ttl,
          tags: cacheConfig.tags
        });
      })
    );
  }

  private generateCacheKey(template: string, request: any, args: any[]): string {
    let key = template;

    // Replace template variables
    key = key.replace(/\{\{(\w+)\}\}/g, (match, prop) =&amp;gt; {
      return request.params?.[prop] || 
             request.query?.[prop] || 
             args[0]?.[prop] || 
             match;
    });

    return key;
  }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Advanced Execution Context Manipulation
&lt;/h2&gt;

&lt;p&gt;The &lt;code&gt;ExecutionContext&lt;/code&gt; is more powerful than you realize. Here's how to leverage it for sophisticated request handling.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// advanced-auth.guard.ts
import { Injectable, CanActivate, ExecutionContext, UnauthorizedException } from '@nestjs/common';
import { Reflector } from '@nestjs/core';

export interface AuthContext {
  user?: any;
  permissions?: string[];
  rateLimit?: {
    remaining: number;
    resetTime: number;
  };
}

@Injectable()
export class AdvancedAuthGuard implements CanActivate {
  constructor(
    private reflector: Reflector,
    private authService: AuthService,
    private rateLimitService: RateLimitService
  ) {}

  async canActivate(context: ExecutionContext): Promise&amp;lt;boolean&amp;gt; {
    const request = context.switchToHttp().getRequest();
    const response = context.switchToHttp().getResponse();

    // Get handler and class metadata
    const handler = context.getHandler();
    const controllerClass = context.getClass();

    // Check if route is public
    const isPublic = this.reflector.getAllAndOverride&amp;lt;boolean&amp;gt;('isPublic', [
      handler,
      controllerClass,
    ]);

    if (isPublic) {
      return true;
    }

    // Extract and validate token
    const token = this.extractTokenFromHeader(request);
    if (!token) {
      throw new UnauthorizedException('Token not found');
    }

    try {
      // Validate token and get user
      const user = await this.authService.validateToken(token);

      // Check rate limiting
      const rateLimitKey = `rate_limit:${user.id}:${handler.name}`;
      const rateLimit = await this.rateLimitService.checkLimit(rateLimitKey);

      if (!rateLimit.allowed) {
        response.set('X-RateLimit-Remaining', '0');
        response.set('X-RateLimit-Reset', rateLimit.resetTime.toString());
        throw new UnauthorizedException('Rate limit exceeded');
      }

      // Create enhanced auth context
      const authContext: AuthContext = {
        user,
        permissions: user.permissions,
        rateLimit: {
          remaining: rateLimit.remaining,
          resetTime: rateLimit.resetTime
        }
      };

      // Attach to request for use in downstream handlers
      request.authContext = authContext;

      // Set rate limit headers
      response.set('X-RateLimit-Remaining', rateLimit.remaining.toString());
      response.set('X-RateLimit-Reset', rateLimit.resetTime.toString());

      return true;
    } catch (error) {
      throw new UnauthorizedException('Invalid token');
    }
  }

  private extractTokenFromHeader(request: any): string | undefined {
    const [type, token] = request.headers.authorization?.split(' ') ?? [];
    return type === 'Bearer' ? token : undefined;
  }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Dynamic Module Factory Patterns
&lt;/h2&gt;

&lt;p&gt;Creating modules that adapt their behavior based on runtime conditions is a powerful pattern for building flexible applications.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// feature-flag.interface.ts
export interface FeatureFlagConfig {
  provider: 'redis' | 'database' | 'memory';
  refreshInterval?: number;
  defaultFlags?: Record&amp;lt;string, boolean&amp;gt;;
  remoteConfig?: {
    url: string;
    apiKey: string;
  };
}

// feature-flag.module.ts
import { DynamicModule, Module, Provider } from '@nestjs/common';
import { FeatureFlagService } from './feature-flag.service';

@Module({})
export class FeatureFlagModule {
  static forRoot(config: FeatureFlagConfig): DynamicModule {
    const providers: Provider[] = [
      {
        provide: 'FEATURE_FLAG_CONFIG',
        useValue: config,
      },
    ];

    // Conditionally add providers based on configuration
    switch (config.provider) {
      case 'redis':
        providers.push({
          provide: 'FEATURE_FLAG_STORAGE',
          useClass: RedisFeatureFlagStorage,
        });
        break;
      case 'database':
        providers.push({
          provide: 'FEATURE_FLAG_STORAGE',
          useClass: DatabaseFeatureFlagStorage,
        });
        break;
      default:
        providers.push({
          provide: 'FEATURE_FLAG_STORAGE',
          useClass: MemoryFeatureFlagStorage,
        });
    }

    // Add remote config provider if configured
    if (config.remoteConfig) {
      providers.push({
        provide: 'REMOTE_CONFIG_CLIENT',
        useFactory: () =&amp;gt; new RemoteConfigClient(config.remoteConfig),
      });
    }

    providers.push(FeatureFlagService);

    return {
      module: FeatureFlagModule,
      providers,
      exports: [FeatureFlagService],
      global: true,
    };
  }

  static forRootAsync(options: {
    useFactory: (...args: any[]) =&amp;gt; Promise&amp;lt;FeatureFlagConfig&amp;gt; | FeatureFlagConfig;
    inject?: any[];
  }): DynamicModule {
    return {
      module: FeatureFlagModule,
      providers: [
        {
          provide: 'FEATURE_FLAG_CONFIG',
          useFactory: options.useFactory,
          inject: options.inject || [],
        },
        {
          provide: 'FEATURE_FLAG_STORAGE',
          useFactory: async (config: FeatureFlagConfig) =&amp;gt; {
            switch (config.provider) {
              case 'redis':
                return new RedisFeatureFlagStorage();
              case 'database':
                return new DatabaseFeatureFlagStorage();
              default:
                return new MemoryFeatureFlagStorage();
            }
          },
          inject: ['FEATURE_FLAG_CONFIG'],
        },
        FeatureFlagService,
      ],
      exports: [FeatureFlagService],
      global: true,
    };
  }
}

// Usage in AppModule
@Module({
  imports: [
    FeatureFlagModule.forRootAsync({
      useFactory: (configService: ConfigService) =&amp;gt; ({
        provider: configService.get('FEATURE_FLAG_PROVIDER') as 'redis' | 'database',
        refreshInterval: configService.get('FEATURE_FLAG_REFRESH_INTERVAL', 30000),
        remoteConfig: configService.get('FEATURE_FLAG_REMOTE_URL') ? {
          url: configService.get('FEATURE_FLAG_REMOTE_URL'),
          apiKey: configService.get('FEATURE_FLAG_API_KEY'),
        } : undefined,
      }),
      inject: [ConfigService],
    }),
  ],
})
export class AppModule {}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Request Scope Memory Management
&lt;/h2&gt;

&lt;p&gt;Understanding REQUEST scope deeply is crucial for preventing memory leaks in high-traffic applications.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// user-context.service.ts
import { Injectable, Scope, OnModuleDestroy } from '@nestjs/common';
import { EventEmitter } from 'events';

@Injectable({ scope: Scope.REQUEST })
export class UserContextService implements OnModuleDestroy {
  private readonly eventEmitter = new EventEmitter();
  private readonly subscriptions: (() =&amp;gt; void)[] = [];
  private userData: Map&amp;lt;string, any&amp;gt; = new Map();

  constructor() {
    // Set max listeners to prevent memory leak warnings
    this.eventEmitter.setMaxListeners(100);
  }

  setUserData(key: string, value: any): void {
    this.userData.set(key, value);
    this.eventEmitter.emit('userDataChanged', { key, value });
  }

  getUserData(key: string): any {
    return this.userData.get(key);
  }

  onUserDataChange(callback: (data: { key: string; value: any }) =&amp;gt; void): void {
    this.eventEmitter.on('userDataChanged', callback);

    // Store cleanup function
    const cleanup = () =&amp;gt; this.eventEmitter.off('userDataChanged', callback);
    this.subscriptions.push(cleanup);
  }

  // Critical: Clean up resources when request ends
  onModuleDestroy(): void {
    // Remove all event listeners
    this.subscriptions.forEach(cleanup =&amp;gt; cleanup());
    this.eventEmitter.removeAllListeners();

    // Clear data
    this.userData.clear();

    console.log('UserContextService destroyed for request');
  }
}

// Usage with proper cleanup
@Injectable()
export class UserService {
  constructor(private userContext: UserContextService) {}

  async processUser(userId: string): Promise&amp;lt;void&amp;gt; {
    // This will be automatically cleaned up when request ends
    this.userContext.onUserDataChange((data) =&amp;gt; {
      console.log(`User data changed: ${data.key} = ${data.value}`);
    });

    this.userContext.setUserData('userId', userId);
    this.userContext.setUserData('lastActivity', new Date());
  }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Advanced Exception Filter Chaining
&lt;/h2&gt;

&lt;p&gt;Create sophisticated error handling with hierarchical exception filters.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// base-exception.filter.ts
import { ExceptionFilter, Catch, ArgumentsHost, HttpException, Logger } from '@nestjs/common';
import { Request, Response } from 'express';

export interface ErrorContext {
  correlationId: string;
  userId?: string;
  endpoint: string;
  userAgent?: string;
  ip: string;
}

@Catch()
export abstract class BaseExceptionFilter implements ExceptionFilter {
  protected readonly logger = new Logger(this.constructor.name);

  abstract canHandle(exception: any): boolean;
  abstract handleException(exception: any, host: ArgumentsHost): void;

  catch(exception: any, host: ArgumentsHost): void {
    if (this.canHandle(exception)) {
      this.handleException(exception, host);
    } else {
      // Pass to next filter in chain
      this.delegateToNext(exception, host);
    }
  }

  protected delegateToNext(exception: any, host: ArgumentsHost): void {
    // This would be handled by the next filter in the chain
    // or the default NestJS exception handler
    throw exception;
  }

  protected createErrorContext(host: ArgumentsHost): ErrorContext {
    const ctx = host.switchToHttp();
    const request = ctx.getRequest&amp;lt;Request&amp;gt;();

    return {
      correlationId: request.headers['x-correlation-id'] as string || 
                    Math.random().toString(36).substring(7),
      userId: (request as any).authContext?.user?.id,
      endpoint: `${request.method} ${request.url}`,
      userAgent: request.headers['user-agent'],
      ip: request.ip,
    };
  }
}

// validation-exception.filter.ts
@Catch(ValidationException)
export class ValidationExceptionFilter extends BaseExceptionFilter {
  canHandle(exception: any): boolean {
    return exception instanceof ValidationException;
  }

  handleException(exception: ValidationException, host: ArgumentsHost): void {
    const ctx = host.switchToHttp();
    const response = ctx.getResponse&amp;lt;Response&amp;gt;();
    const errorContext = this.createErrorContext(host);

    this.logger.warn('Validation error', {
      ...errorContext,
      errors: exception.getErrors(),
    });

    response.status(400).json({
      statusCode: 400,
      message: 'Validation failed',
      errors: exception.getErrors(),
      correlationId: errorContext.correlationId,
      timestamp: new Date().toISOString(),
    });
  }
}

// business-exception.filter.ts
@Catch(BusinessException)
export class BusinessExceptionFilter extends BaseExceptionFilter {
  canHandle(exception: any): boolean {
    return exception instanceof BusinessException;
  }

  handleException(exception: BusinessException, host: ArgumentsHost): void {
    const ctx = host.switchToHttp();
    const response = ctx.getResponse&amp;lt;Response&amp;gt;();
    const errorContext = this.createErrorContext(host);

    this.logger.error('Business logic error', {
      ...errorContext,
      errorCode: exception.getErrorCode(),
      message: exception.message,
    });

    response.status(422).json({
      statusCode: 422,
      message: exception.message,
      errorCode: exception.getErrorCode(),
      correlationId: errorContext.correlationId,
      timestamp: new Date().toISOString(),
    });
  }
}

// global-exception.filter.ts
@Catch()
export class GlobalExceptionFilter extends BaseExceptionFilter {
  canHandle(exception: any): boolean {
    return true; // Global filter handles everything
  }

  handleException(exception: any, host: ArgumentsHost): void {
    const ctx = host.switchToHttp();
    const response = ctx.getResponse&amp;lt;Response&amp;gt;();
    const errorContext = this.createErrorContext(host);

    // Handle different types of exceptions
    if (exception instanceof HttpException) {
      this.handleHttpException(exception, response, errorContext);
    } else {
      this.handleUnknownException(exception, response, errorContext);
    }
  }

  private handleHttpException(
    exception: HttpException, 
    response: Response, 
    context: ErrorContext
  ): void {
    const status = exception.getStatus();
    const exceptionResponse = exception.getResponse();

    this.logger.error('HTTP Exception', {
      ...context,
      status,
      response: exceptionResponse,
    });

    response.status(status).json({
      statusCode: status,
      message: exception.message,
      correlationId: context.correlationId,
      timestamp: new Date().toISOString(),
    });
  }

  private handleUnknownException(
    exception: any, 
    response: Response, 
    context: ErrorContext
  ): void {
    this.logger.error('Unhandled Exception', {
      ...context,
      error: exception.message,
      stack: exception.stack,
    });

    response.status(500).json({
      statusCode: 500,
      message: 'Internal server error',
      correlationId: context.correlationId,
      timestamp: new Date().toISOString(),
    });
  }
}

// Register filters in correct order
// main.ts
app.useGlobalFilters(
  new ValidationExceptionFilter(),
  new BusinessExceptionFilter(),
  new GlobalExceptionFilter(), // This should be last
);
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Advanced Health Check Orchestration
&lt;/h2&gt;

&lt;p&gt;Build sophisticated health monitoring that goes beyond simple HTTP checks.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// health-check.service.ts
import { Injectable } from '@nestjs/common';
import { HealthIndicator, HealthIndicatorResult, HealthCheckError } from '@nestjs/terminus';

@Injectable()
export class AdvancedHealthIndicator extends HealthIndicator {
  constructor(
    private readonly databaseService: DatabaseService,
    private readonly redisService: RedisService,
    private readonly externalApiService: ExternalApiService,
  ) {
    super();
  }

  async checkDatabase(key: string): Promise&amp;lt;HealthIndicatorResult&amp;gt; {
    try {
      const startTime = Date.now();
      await this.databaseService.executeQuery('SELECT 1');
      const responseTime = Date.now() - startTime;

      const isHealthy = responseTime &amp;lt; 1000; // 1 second threshold

      const result = this.getStatus(key, isHealthy, {
        responseTime: `${responseTime}ms`,
        threshold: '1000ms',
        timestamp: new Date().toISOString(),
      });

      if (!isHealthy) {
        throw new HealthCheckError('Database response time too high', result);
      }

      return result;
    } catch (error) {
      throw new HealthCheckError('Database connection failed', {
        [key]: {
          status: 'down',
          error: error.message,
          timestamp: new Date().toISOString(),
        },
      });
    }
  }

  async checkRedis(key: string): Promise&amp;lt;HealthIndicatorResult&amp;gt; {
    try {
      const startTime = Date.now();
      await this.redisService.ping();
      const responseTime = Date.now() - startTime;

      const result = this.getStatus(key, true, {
        responseTime: `${responseTime}ms`,
        timestamp: new Date().toISOString(),
      });

      return result;
    } catch (error) {
      throw new HealthCheckError('Redis connection failed', {
        [key]: {
          status: 'down',
          error: error.message,
          timestamp: new Date().toISOString(),
        },
      });
    }
  }

  async checkExternalDependencies(key: string): Promise&amp;lt;HealthIndicatorResult&amp;gt; {
    const checks = await Promise.allSettled([
      this.checkExternalApi('payment-gateway', 'https://api.payment.com/health'),
      this.checkExternalApi('notification-service', 'https://api.notifications.com/health'),
      this.checkExternalApi('analytics-service', 'https://api.analytics.com/health'),
    ]);

    const results = checks.map((check, index) =&amp;gt; ({
      name: ['payment-gateway', 'notification-service', 'analytics-service'][index],
      status: check.status === 'fulfilled' ? 'up' : 'down',
      details: check.status === 'fulfilled' ? check.value : check.reason,
    }));

    const failedServices = results.filter(r =&amp;gt; r.status === 'down');
    const isHealthy = failedServices.length === 0;

    const result = this.getStatus(key, isHealthy, {
      services: results,
      failedCount: failedServices.length,
      totalCount: results.length,
      timestamp: new Date().toISOString(),
    });

    if (!isHealthy) {
      throw new HealthCheckError('External dependencies failing', result);
    }

    return result;
  }

  private async checkExternalApi(name: string, url: string): Promise&amp;lt;any&amp;gt; {
    const startTime = Date.now();
    const response = await fetch(url, { 
      method: 'GET',
      timeout: 5000 // 5 second timeout
    });
    const responseTime = Date.now() - startTime;

    return {
      name,
      status: response.ok ? 'up' : 'down',
      responseTime: `${responseTime}ms`,
      statusCode: response.status,
    };
  }
}

// health.controller.ts
@Controller('health')
export class HealthController {
  constructor(
    private health: HealthCheckService,
    private advancedHealth: AdvancedHealthIndicator,
  ) {}

  @Get()
  @HealthCheck()
  check() {
    return this.health.check([
      () =&amp;gt; this.advancedHealth.checkDatabase('database'),
      () =&amp;gt; this.advancedHealth.checkRedis('redis'),
    ]);
  }

  @Get('detailed')
  @HealthCheck()
  detailedCheck() {
    return this.health.check([
      () =&amp;gt; this.advancedHealth.checkDatabase('database'),
      () =&amp;gt; this.advancedHealth.checkRedis('redis'),
      () =&amp;gt; this.advancedHealth.checkExternalDependencies('external-services'),
    ]);
  }

  @Get('readiness')
  @HealthCheck()
  readinessCheck() {
    // More strict checks for readiness
    return this.health.check([
      () =&amp;gt; this.advancedHealth.checkDatabase('database'),
      () =&amp;gt; this.advancedHealth.checkRedis('redis'),
      () =&amp;gt; this.advancedHealth.checkExternalDependencies('external-services'),
    ]);
  }

  @Get('liveness')
  @HealthCheck()
  livenessCheck() {
    // Basic checks for liveness (pod restart criteria)
    return this.health.check([
      () =&amp;gt; this.advancedHealth.checkDatabase('database'),
    ]);
  }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Provider Overriding in Tests: Surgical Test Isolation
&lt;/h2&gt;

&lt;p&gt;Advanced testing patterns that provide precise control over dependencies.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// user.service.spec.ts
describe('UserService', () =&amp;gt; {
  let service: UserService;
  let app: TestingModule;
  let mockUserRepository: jest.Mocked&amp;lt;UserRepository&amp;gt;;
  let mockEventEmitter: jest.Mocked&amp;lt;EventEmitter2&amp;gt;;
  let mockCacheService: jest.Mocked&amp;lt;CacheService&amp;gt;;

  beforeEach(async () =&amp;gt; {
    // Create sophisticated mocks
    mockUserRepository = {
      save: jest.fn(),
      findOne: jest.fn(),
      update: jest.fn(),
      delete: jest.fn(),
      find: jest.fn(),
    } as any;

    mockEventEmitter = {
      emit: jest.fn(),
      on: jest.fn(),
      off: jest.fn(),
    } as any;

    mockCacheService = {
      get: jest.fn(),
      set: jest.fn(),
      del: jest.fn(),
      reset: jest.fn(),
    } as any;

    app = await Test.createTestingModule({
      imports: [
        // Import actual modules but override specific providers
        DatabaseModule,
        CacheModule,
        EventEmitterModule.forRoot(),
      ],
      providers: [
        UserService,
        NotificationService,
      ],
    })
    // Override specific providers surgically
    .overrideProvider(getRepositoryToken(User))
    .useValue(mockUserRepository)

    .overrideProvider(EventEmitter2)
    .useValue(mockEventEmitter)

    .overrideProvider(CACHE_TOKENS.SESSION_CACHE)
    .useValue(mockCacheService)

    // Override guards for testing without authentication
    .overrideGuard(JwtAuthGuard)
    .useValue({ canActivate: () =&amp;gt; true })

    // Override interceptors to disable caching during tests
    .overrideInterceptor(CacheInterceptor)
    .useValue({ intercept: (context, next) =&amp;gt; next.handle() })

    .compile();

    service = app.get&amp;lt;UserService&amp;gt;(UserService);
  });

  describe('createUser', () =&amp;gt; {
    it('should create user and emit event', async () =&amp;gt; {
      // Arrange
      const userData = { email: 'test@example.com', name: 'Test User' };
      const createdUser = { id: '1', ...userData };
      mockUserRepository.save.mockResolvedValue(createdUser);

      // Act
      const result = await service.createUser(userData);

      // Assert
      expect(mockUserRepository.save).toHaveBeenCalledWith(userData);
      expect(mockEventEmitter.emit).toHaveBeenCalledWith('user.created', {
        userId: '1',
        email: 'test@example.com',
        preferences: undefined
      });
      expect(result).toEqual(createdUser);
    });

    it('should handle cache failure gracefully', async () =&amp;gt; {
      // Arrange
      const userData = { email: 'test@example.com', name: 'Test User' };
      const createdUser = { id: '1', ...userData };
      mockUserRepository.save.mockResolvedValue(createdUser);
      mockCacheService.set.mockRejectedValue(new Error('Cache unavailable'));

      // Act &amp;amp; Assert - should not throw
      const result = await service.createUser(userData);
      expect(result).toEqual(createdUser);
    });
  });

  // Test with different provider overrides per test
  describe('with different cache configuration', () =&amp;gt; {
    beforeEach(async () =&amp;gt; {
      // Override with different cache implementation
      await app.close();

      app = await Test.createTestingModule({
        imports: [UserModule],
      })
      .overrideProvider(CACHE_TOKENS.SESSION_CACHE)
      .useFactory({
        factory: () =&amp;gt; new MemoryCacheService({ maxSize: 10 }),
      })
      .compile();

      service = app.get&amp;lt;UserService&amp;gt;(UserService);
    });

    it('should work with memory cache', async () =&amp;gt; {
      // Test implementation with actual memory cache
    });
  });

  afterEach(async () =&amp;gt; {
    await app.close();
  });
});
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;p&gt;The key insight is that NestJS provides the primitives, but senior engineers know how to compose them into powerful, maintainable systems. These patterns have been battle-tested in production environments handling millions of requests.&lt;br&gt;
Master these techniques, and you'll find yourself building more robust, scalable, and maintainable backend applications that can handle enterprise-level complexity with ease.&lt;/p&gt;

&lt;p&gt;Let's Connect &lt;/p&gt;

&lt;p&gt;&lt;a href="https://www.linkedin.com/in/boyinbode-ebenezer/" rel="noopener noreferrer"&gt;Linkedln&lt;/a&gt; | &lt;a href="https://github.com/drbenzene" rel="noopener noreferrer"&gt;Github&lt;/a&gt; | &lt;a href="https://twitter.com/boyinbodeee" rel="noopener noreferrer"&gt;Twitter&lt;/a&gt; | &lt;/p&gt;

</description>
    </item>
    <item>
      <title>ADVANCED NESTJS BRUTAL FORCE ATTACK TECHNIQUES AND PREVENTIONS</title>
      <dc:creator>Boyinbode Ebenezer Ayomide</dc:creator>
      <pubDate>Fri, 14 Mar 2025 12:42:09 +0000</pubDate>
      <link>https://forem.com/drbenzene/advanced-nestjs-brutal-force-attack-techniques-and-preventions-4lch</link>
      <guid>https://forem.com/drbenzene/advanced-nestjs-brutal-force-attack-techniques-and-preventions-4lch</guid>
      <description>&lt;h2&gt;
  
  
  Table of Contents
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Understanding Brute Force Attacks&lt;/li&gt;
&lt;li&gt;Advanced Techniques to Prevent Brute Force Attacks&lt;/li&gt;
&lt;li&gt;Setting Up Rate Limiting&lt;/li&gt;
&lt;li&gt;Implementing Account Lockout&lt;/li&gt;
&lt;li&gt;Adding CAPTCHA Verification&lt;/li&gt;
&lt;li&gt;Blocking Suspicious IPs&lt;/li&gt;
&lt;li&gt;Best Practices&lt;/li&gt;
&lt;li&gt;Conclusion&lt;/li&gt;
&lt;/ul&gt;




&lt;p&gt;Brute force attacks are a common security threat where an attacker attempts to gain unauthorized access to a system by trying numerous username/password combinations. The attacker systematically trying all possible combinations of credentials until the correct one is found. This can lead to unauthorized access, data breaches, and other security issues. To prevent such attacks, you must implement multiple layers of defense.&lt;/p&gt;

&lt;p&gt;This article will guide you through advanced techniques to mitigate brute force attacks, including rate limiting, account lockout mechanisms, and CAPTCHA integration in NestJS&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Understanding Brute Force Attacks&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Imagine a popular online banking platform, "SecureBank," which allows users to log in using their email addresses and passwords. The platform has thousands of users, including individuals and businesses, who rely on it for managing their finances. Unfortunately, SecureBank’s login system has a critical flaw: it doesn’t implement any mechanisms to prevent multiple login attempts. This oversight makes it an easy target for brute force attacks.&lt;/p&gt;

&lt;p&gt;The attacker, a malicious individual with basic programming skills, discovers SecureBank’s login page at &lt;code&gt;https://securebank.com/login&lt;/code&gt;. The page requires a username (email) and password for access. The attacker’s goal is to gain unauthorized access to user accounts, particularly those with high balances or administrative privileges.&lt;/p&gt;

&lt;p&gt;The attacker also obtains a list of common email addresses (e.g., from a previous data breach) and a dictionary of frequently used passwords (e.g., "password123", "admin123", "12345678").&lt;/p&gt;

&lt;p&gt;The attacker writes a simple Python script to automate the login attempts. The script reads email addresses and passwords from the prepared lists and sends them to the login endpoint. Here’s what the script looks like:&lt;br&gt;
&lt;/p&gt;

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

# List of common email addresses and passwords
emails = ["user1@example.com", "user2@example.com", "admin@securebank.com"]
passwords = ["password123", "admin123", "12345678", "qwerty"]

# Target login endpoint
login_url = "https://securebank.com/login"

# Launch the brute force attack
for email in emails:
    for password in passwords:
        payload = {
            "email": email,
            "password": password
        }
        response = requests.post(login_url, data=payload)

        if "Login successful" in response.text:
            print(f"Success! Email: {email}, Password: {password}")
            break
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The attacker runs the script, which begins sending thousands of login requests to SecureBank’s login endpoint. Each request tries a different combination of email and password. Since there are no restrictions on the number of attempts, the script can run indefinitely until it finds a valid combination.&lt;/p&gt;

&lt;p&gt;After several hours, the script successfully logs in with the following credentials:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Email: admin@securebank.com

Password: admin123
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The attacker now has access to the admin account, which grants them full control over the banking platform. They can transfer funds, steal sensitive customer data, or even lock legitimate users out of their accounts.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Advanced Techniques to Prevent Brute Force Attacks&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;a. &lt;strong&gt;Rate Limiting&lt;/strong&gt;&lt;br&gt;
Rate limiting restricts the number of requests a user can make within a specific time frame. This prevents attackers from making too many login attempts in a short period.&lt;/p&gt;

&lt;p&gt;NestJS provides a flexible way to implement rate limiting using the &lt;code&gt;@nestjs/throttler&lt;/code&gt; package.&lt;/p&gt;

&lt;p&gt;Install Dependencies&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;npm install @nestjs/throttler
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Configure ThrottlerModule&lt;br&gt;
In your &lt;code&gt;app.module.ts&lt;/code&gt;, configure the ThrottlerModule:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import { ThrottlerModule } from '@nestjs/throttler';

@Module({
  imports: [
    ThrottlerModule.forRoot({
      ttl: 60, // Time-to-live in seconds
      limit: 10, // Maximum number of requests within TTL
    }),
  ],
})
export class AppModule {}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Apply Rate Limiting to Routes&lt;br&gt;
Use the @Throttle() decorator to apply rate limiting to specific routes:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import { Throttle } from '@nestjs/throttler';

@Controller('auth')
export class AuthController {
  @Throttle(5, 60) // 5 requests per 60 seconds
  @Post('login')
  async login(@Body() credentials: any) {
    // Login logic
  }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;b. &lt;strong&gt;Account Lockout Mechanism&lt;/strong&gt;&lt;br&gt;
After a certain number of failed login attempts, the account is temporarily locked. This slows down brute force attacks and alerts the user of suspicious activity.&lt;/p&gt;

&lt;p&gt;Track Failed Login Attempts&lt;br&gt;
Store the number of failed login attempts in your database or an in-memory store like Redis.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import { Injectable } from '@nestjs/common';
import { InjectRepository } from '@nestjs/typeorm';
import { Repository } from 'typeorm';
import { User } from './user.entity';

@Injectable()
export class AuthService {
  constructor(
    @InjectRepository(User)
    private userRepository: Repository&amp;lt;User&amp;gt;,
  ) {}

  async incrementFailedAttempts(userId: number): Promise&amp;lt;void&amp;gt; {
    const user = await this.userRepository.findOne({ where: { id: userId } });
    user.failedAttempts += 1;
    await this.userRepository.save(user);
  }

  async resetFailedAttempts(userId: number): Promise&amp;lt;void&amp;gt; {
    const user = await this.userRepository.findOne({ where: { id: userId } });
    user.failedAttempts = 0;
    await this.userRepository.save(user);
  }

  async isAccountLocked(userId: number): Promise&amp;lt;boolean&amp;gt; {
    const user = await this.userRepository.findOne({ where: { id: userId } });
    return user.failedAttempts &amp;gt;= 5; // Lock after 5 failed attempts
  }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Lock Account After Too Many Failed Attempts&lt;br&gt;
In your login logic, check if the account is locked:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;@Post('login')
async login(@Body() credentials: any) {
  const user = await this.authService.validateUser(credentials);
  if (!user) {
    await this.authService.incrementFailedAttempts(user.id);
    throw new UnauthorizedException('Invalid credentials');
  }

  if (await this.authService.isAccountLocked(user.id)) {
    throw new UnauthorizedException('Account locked due to too many failed attempts');
  }

  await this.authService.resetFailedAttempts(user.id);
  // Proceed with login
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;c. &lt;strong&gt;CAPTCHA Integration&lt;/strong&gt;&lt;br&gt;
CAPTCHA challenges ensure that the login attempt is made by a human and not an automated script.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import axios from 'axios';

@Post('login')
async login(@Body() credentials: any) {
  const captchaResponse = await axios.post(
    `https://www.google.com/recaptcha/api/siteverify?secret=${process.env.RECAPTCHA_SECRET}&amp;amp;response=${credentials.captcha}`,
  );

  if (!captchaResponse.data.success) {
    throw new UnauthorizedException('CAPTCHA verification failed');
  }

  // Proceed with login
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;d. &lt;strong&gt;IP Blocking&lt;/strong&gt;&lt;br&gt;
Blocking IP addresses that exhibit suspicious behavior (e.g., too many failed login attempts) can effectively mitigate brute force attacks.&lt;/p&gt;

&lt;p&gt;Track IPs with Failed Attempts&lt;br&gt;
Use Redis to store IP addresses and their failed attempt counts.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import { Injectable } from '@nestjs/common';
import { InjectRedis, Redis } from '@nestjs-modules/ioredis';

@Injectable()
export class IpBlockingService {
  constructor(@InjectRedis() private readonly redis: Redis) {}

  async incrementFailedAttempts(ip: string): Promise&amp;lt;void&amp;gt; {
    const attempts = await this.redis.incr(`ip:${ip}:attempts`);
    if (attempts === 1) {
      await this.redis.expire(`ip:${ip}:attempts`, 3600); // Expire after 1 hour
    }
  }

  async isIpBlocked(ip: string): Promise&amp;lt;boolean&amp;gt; {
    const attempts = await this.redis.get(`ip:${ip}:attempts`);
    return attempts &amp;amp;&amp;amp; parseInt(attempts) &amp;gt;= 10; // Block after 10 failed attempts
  }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Check if the IP is blocked before processing the login request:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;@Post('login')
async login(@Body() credentials: any, @Req() req: Request) {
  const ip = req.ip;
  if (await this.ipBlockingService.isIpBlocked(ip)) {
    throw new UnauthorizedException('IP blocked due to suspicious activity');
  }

  // Proceed with login
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Preventing brute force attacks requires a multi-layered approach. By combining these techniques, you can significantly enhance the security of your NestJS backend. &lt;/p&gt;

&lt;p&gt;Remember that each project has its unique requirements, so adapt these techniques accordingly. Stay up-to-date with the NestJS documentation and the rapidly evolving Node.js ecosystem to make the most of this powerful framework. Happy coding!&lt;/p&gt;

&lt;p&gt;Let's Connect &lt;/p&gt;

&lt;p&gt;&lt;a href="https://www.linkedin.com/in/boyinbode-ebenezer/" rel="noopener noreferrer"&gt;Linkedln&lt;/a&gt; | &lt;a href="https://github.com/drbenzene" rel="noopener noreferrer"&gt;Github&lt;/a&gt; | &lt;a href="https://twitter.com/boyinbodeee" rel="noopener noreferrer"&gt;Twitter&lt;/a&gt; | &lt;/p&gt;

</description>
    </item>
    <item>
      <title>Building A Scalable Advanced Email Templating System with @react-email and NestJS</title>
      <dc:creator>Boyinbode Ebenezer Ayomide</dc:creator>
      <pubDate>Thu, 24 Oct 2024 12:50:06 +0000</pubDate>
      <link>https://forem.com/drbenzene/building-a-scalable-advanced-email-templating-system-with-react-email-and-nestjs-41fd</link>
      <guid>https://forem.com/drbenzene/building-a-scalable-advanced-email-templating-system-with-react-email-and-nestjs-41fd</guid>
      <description>&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%2Fde21vtfunb36l8ezub79.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%2Fde21vtfunb36l8ezub79.png" alt="Image description" width="800" height="450"&gt;&lt;/a&gt;Whether you're sending transactional emails or personalized messages, having a templating system in place can greatly enhance the efficiency and flexibility of your email handling process. In this tutorial, we will explore how to build an email templating system in NestJS using &lt;code&gt;@react-email&lt;/code&gt; for building dynamic and visually appealing email templates.&lt;/p&gt;

&lt;p&gt;This integration will allow you to leverage the power of React's components for email design while NestJS will handle the backend logic to send these emails. Let’s walk through the steps with sample code to make the process easier.&lt;/p&gt;

&lt;p&gt;Why Use @react-email/components?&lt;br&gt;
@react-email/components provides a powerful and flexible way to create reusable email templates. With React's component-driven architecture, you can break down your email layout into small, manageable pieces and use the same code for the frontend and backend. This library abstracts the complexity of HTML emails, which can be tedious to manage manually.&lt;/p&gt;

&lt;p&gt;Prerequisites&lt;br&gt;
Before we begin, ensure you have the following installed:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Node.js (v14 or later)
NestJS CLI
TypeScript
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A basic understanding of React&lt;br&gt;
Step 1: Set Up a New NestJS Project&lt;br&gt;
Start by setting up a new NestJS project if you don't already have one.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ nest new email-templating
$ cd email-templating
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Once your project is set up, we will install the necessary packages.&lt;/p&gt;

&lt;p&gt;Step 2: Install Required Dependencies&lt;br&gt;
Next, install the required packages for email templating and sending.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;React Email components
npm install @react-email/components react react-dom
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h1&gt;
  
  
  Nodemailer for sending emails
&lt;/h1&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;npm install nodemailer
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h1&gt;
  
  
  Required types
&lt;/h1&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;npm install --save-dev @types/nodemailer

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

&lt;/div&gt;



&lt;p&gt;Step 3: Create Email Templates Using @react-email/components&lt;br&gt;
Now, let’s create a simple email template using @react-email/components. First, create a new directory called email-templates inside the src folder.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;mkdir src/email-templates
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;To include Tailwind CSS for styling and a reusable layout wrapper for your emails, you’ll need to set up a global layout component that can be used across different email templates, while leveraging Tailwind's utility classes for styling.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ npm install tailwindcss

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

&lt;/div&gt;



&lt;p&gt;Inside the email-templates folder, create a file called welcome-email.tsx. This will be our first email template.&lt;/p&gt;

&lt;p&gt;Now, configure Tailwind CSS by creating a tailwind.config.js file in your root directory.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ npx tailwindcss init
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Next, we need to configure Tailwind to process styles within the email templates. Add the following content inside your tailwind.config.js file:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;module.exports = {
  content: [
    './src/email-templates/**/*.{js,ts,jsx,tsx}',
    './src/layout/**/*.{js,ts,jsx,tsx}',
  ],
  theme: {
    extend: {},
  },
  plugins: [],
};
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;To wrap every email with a consistent layout and styling, create a Layout component in your src/layout directory. This layout will serve as the base for all emails.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ mkdir src/layout
$ touch src/layout/EmailLayout.tsx
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Here’s the EmailLayout component that uses Tailwind for styling:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// src/layout/EmailLayout.tsx
import React from 'react';
import { Html, Head, Body, Container } from '@react-email/components';

const EmailLayout = ({ children }: { children: React.ReactNode }) =&amp;gt; {
  return (
    &amp;lt;Html&amp;gt;
      &amp;lt;Head /&amp;gt;
      &amp;lt;Body className="bg-gray-100 p-6"&amp;gt;
        &amp;lt;Container className="max-w-xl mx-auto bg-white p-6 shadow-md rounded-lg"&amp;gt;
          {/* Header */}
          &amp;lt;div className="text-center mb-8"&amp;gt;
            &amp;lt;h1 className="text-2xl font-semibold text-indigo-600"&amp;gt;Your Company&amp;lt;/h1&amp;gt;
          &amp;lt;/div&amp;gt;

          {/* Email Content */}
          {children}

          {/* Footer */}
          &amp;lt;div className="text-center mt-10 text-gray-500 text-sm"&amp;gt;
            &amp;lt;p&amp;gt;&amp;amp;copy; {new Date().getFullYear()} Your Company. All rights reserved.&amp;lt;/p&amp;gt;
          &amp;lt;/div&amp;gt;
        &amp;lt;/Container&amp;gt;
      &amp;lt;/Body&amp;gt;
    &amp;lt;/Html&amp;gt;
  );
};

export default EmailLayout;

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

&lt;/div&gt;



&lt;p&gt;This layout provides a structured, reusable container for all emails with Tailwind’s utility classes. It wraps the content in a clean layout, including a header and footer, ensuring consistency across all emails.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// src/mail/mail.service.ts
import { Injectable } from '@nestjs/common';
import nodemailer from 'nodemailer';
import { render } from '@react-email/components';
import WelcomeEmail from '../email-templates/welcome-email';

@Injectable()
export class MailService {
  private transporter;

  constructor() {
    this.transporter = nodemailer.createTransport({
      service: 'Gmail',
      auth: {
        user: 'your-email@gmail.com',
        pass: 'your-email-password',
      },
    });
  }

  async sendWelcomeEmail(to: string) {
    const emailHtml = render(&amp;lt;WelcomeEmail /&amp;gt;);

    const mailOptions = {
      from: 'your-email@gmail.com',
      to,
      subject: 'Welcome to Our Service!',
      html: emailHtml,
    };

    await this.transporter.sendMail(mailOptions);
  }
}

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

&lt;/div&gt;



&lt;p&gt;Step 4: Set Up Email Sending in NestJS&lt;br&gt;
Now that we have the template ready, let's integrate it with the backend to send emails. We'll be using nodemailer for email delivery.&lt;/p&gt;

&lt;p&gt;First, create a mail.service.ts file in the src/mail directory. This service will handle sending emails using nodemailer.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;mkdir src/mail
touch src/mail/mail.service.ts
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Here’s how you can set up the mail service:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;
// src/mail/mail.service.ts
import { Injectable } from '@nestjs/common';
import nodemailer from 'nodemailer';
import { render } from '@react-email/components';
import WelcomeEmail from '../email-templates/welcome-email';

@Injectable()
export class MailService {
  private transporter;

  constructor() {
    this.transporter = nodemailer.createTransport({
      service: 'Gmail',
      auth: {
        user: 'your-email@gmail.com',
        pass: 'your-email-password',
      },
    });
  }

  async sendWelcomeEmail(to: string) {
    const emailHtml = render(&amp;lt;WelcomeEmail /&amp;gt;);

    const mailOptions = {
      from: 'your-email@gmail.com',
      to,
      subject: 'Welcome to Our Service!',
      html: emailHtml,
    };

    await this.transporter.sendMail(mailOptions);
  }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In this service, we set up nodemailer with Gmail as the transport service. Replace the authentication details with your own email credentials. The sendWelcomeEmail method renders the React component to HTML using the render method from @react-email/components and sends it as an email.&lt;/p&gt;

&lt;p&gt;Step 5: Create a Mail Controller&lt;br&gt;
To trigger the email sending from an endpoint, create a new controller called mail.controller.ts.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;touch src/mail/mail.controller.ts
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Here’s how you can set up the controller:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// src/mail/mail.controller.ts
import { Controller, Post, Body } from '@nestjs/common';
import { MailService } from './mail.service';

@Controller('mail')
export class MailController {
  constructor(private readonly mailService: MailService) {}

  @Post('send-welcome')
  async sendWelcomeEmail(@Body('email') email: string) {
    await this.mailService.sendWelcomeEmail(email);
    return { message: 'Welcome email sent successfully!' };
  }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This controller exposes a POST /mail/send-welcome endpoint that takes an email address as the body parameter and triggers the sendWelcomeEmail method in the MailService.&lt;/p&gt;

&lt;p&gt;Step 6: Set Up the Mail Module&lt;br&gt;
Next, we’ll set up a MailModule to register both the MailService and MailController.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// src/mail/mail.module.ts
import { Module } from '@nestjs/common';
import { MailService } from './mail.service';
import { MailController } from './mail.controller';

@Module({
  providers: [MailService],
  controllers: [MailController],
})
export class MailModule {}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Finally, import the MailModule in your main application module.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// src/app.module.ts
import { Module } from '@nestjs/common';
import { MailModule } from './mail/mail.module';

@Module({
  imports: [MailModule],
})
export class AppModule {}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Step 7: Test the Integration&lt;br&gt;
Now, you can test the email sending functionality by running your NestJS application and making a POST request to the /mail/send-welcome endpoint with a valid email address in the request body.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;npm run start:dev
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Use a tool like Postman or curl to send the request:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;POST http://localhost:3000/mail/send-welcome
{
  "email": "recipient@example.com"
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If everything is set up correctly, you should receive the welcome email in the specified inbox.&lt;/p&gt;

&lt;p&gt;Feel free to expand this by adding more dynamic content to your templates, handling different types of emails (password resets, notifications), and exploring advanced features of nodemailer.&lt;/p&gt;

&lt;p&gt;Happy coding!&lt;/p&gt;

&lt;p&gt;Let's Connect &lt;/p&gt;

&lt;p&gt;&lt;a href="https://www.linkedin.com/in/boyinbode-ebenezer/" rel="noopener noreferrer"&gt;Linkedln&lt;/a&gt; | &lt;a href="https://github.com/drbenzene" rel="noopener noreferrer"&gt;Github&lt;/a&gt; | &lt;a href="https://twitter.com/boyinbodeee" rel="noopener noreferrer"&gt;Twitter&lt;/a&gt; | &lt;a href="https://www.youtube.com/channel/UCLv47WaIjDmnROud6FRmPJg" rel="noopener noreferrer"&gt;Youtube&lt;/a&gt;&lt;/p&gt;

</description>
    </item>
    <item>
      <title>Best Security implementation Practices In NestJS. A Comprehensive Guide</title>
      <dc:creator>Boyinbode Ebenezer Ayomide</dc:creator>
      <pubDate>Sat, 23 Mar 2024 10:21:39 +0000</pubDate>
      <link>https://forem.com/drbenzene/best-security-implementation-practices-in-nestjs-a-comprehensive-guide-2p88</link>
      <guid>https://forem.com/drbenzene/best-security-implementation-practices-in-nestjs-a-comprehensive-guide-2p88</guid>
      <description>&lt;p&gt;Ensuring robust security measures is paramount. Authentication and authorization are crucial aspects of security implementation, safeguarding sensitive data and resources from unauthorized access. NestJS, with its modular and scalable architecture, provides an excellent framework for building secure applications. In this blog post, we will explore the best practices for implementing authentication, authorization, and security measures in NestJS applications, along with code examples to guide you through the process.&lt;/p&gt;

&lt;p&gt;1 .  Setting Up Authentication:&lt;/p&gt;

&lt;p&gt;Authentication is the process of verifying the identity of users. NestJS offers various strategies for implementing authentication, including JWT (JSON Web Tokens), session-based authentication, and OAuth2. Here, we will focus on JWT authentication, which is widely used for its simplicity and scalability.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// auth.module.ts
import { Module } from '@nestjs/common';
import { JwtModule } from '@nestjs/jwt';
import { AuthService } from './auth.service';
import { JwtStrategy } from './jwt.strategy';
import { AuthController } from './auth.controller';

@Module({
  imports: [
    JwtModule.register({
      secret: process.env.JWT_SECRET,
      signOptions: { expiresIn: '1h' },
    }),
  ],
  controllers: [AuthController],
  providers: [AuthService, JwtStrategy],
})
export class AuthModule {}

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

&lt;/div&gt;



&lt;p&gt;2 . Implementing Authorization&lt;/p&gt;

&lt;p&gt;Authorization defines what actions users are allowed to perform within the application. NestJS provides decorators like @Roles() and @UseGuards() to enforce authorization rules at the controller and route levels.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// roles.guard.ts
import { Injectable, CanActivate, ExecutionContext } from '@nestjs/common';
import { Reflector } from '@nestjs/core';

@Injectable()
export class RolesGuard implements CanActivate {
  constructor(private reflector: Reflector) {}

  canActivate(context: ExecutionContext): boolean {
    const roles = this.reflector.get&amp;lt;string[]&amp;gt;('roles', context.getHandler());
    if (!roles) {
      return true;
    }
    const request = context.switchToHttp().getRequest();
    const user = request.user;
    return user &amp;amp;&amp;amp; user.roles &amp;amp;&amp;amp; user.roles.some(role =&amp;gt; roles.includes(role));
  }
}

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

&lt;/div&gt;



&lt;p&gt;3 . Securing Routes&lt;/p&gt;

&lt;p&gt;Protecting routes with guards ensures that only authorized users can access them. We can apply guards at the controller or route level using @UseGuards() decorator.&lt;br&gt;
typescript&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// app.controller.ts
import { Controller, Get, UseGuards } from '@nestjs/common';
import { AuthGuard } from '@nestjs/passport';
import { RolesGuard } from './roles.guard';
import { Roles } from './roles.decorator';

@Controller('app')
export class AppController {
  @Get()
  @UseGuards(AuthGuard('jwt'), RolesGuard)
  @Roles('admin')
  getApp(): string {
    return 'Protected Route Accessed Successfully!';
  }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;4 . Handling Authentication&lt;/p&gt;

&lt;p&gt;In NestJS, authentication logic is typically implemented in a service. This service is responsible for verifying user credentials and generating tokens.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// auth.service.ts
import { Injectable } from '@nestjs/common';
import { JwtService } from '@nestjs/jwt';
import { UserService } from '../user/user.service';

@Injectable()
export class AuthService {
  constructor(
    private readonly userService: UserService,
    private readonly jwtService: JwtService,
  ) {}

  async validateUser(username: string, password: string): Promise&amp;lt;any&amp;gt; {
    const user = await this.userService.findByUsername(username);
    if (user &amp;amp;&amp;amp; user.password === password) {
      const { password, ...result } = user;
      return result;
    }
    return null;
  }

  async login(user: any): Promise&amp;lt;any&amp;gt; {
    const payload = { username: user.username, sub: user.userId };
    return {
      access_token: this.jwtService.sign(payload),
    };
  }
}

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

&lt;/div&gt;



&lt;p&gt;5 . Protecting Sensitive Data&lt;/p&gt;

&lt;p&gt;In addition to authentication and authorization, protecting sensitive data is crucial for maintaining security. NestJS provides various mechanisms for encrypting sensitive information, such as passwords and access tokens.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// user.service.ts
import { Injectable } from '@nestjs/common';
import { User } from './user.entity';
import * as bcrypt from 'bcrypt';

@Injectable()
export class UserService {
  async findByUsername(username: string): Promise&amp;lt;User | undefined&amp;gt; {
    // Fetch user by username from the database
    return await User.findOne({ where: { username } });
  }

  async hashPassword(password: string): Promise&amp;lt;string&amp;gt; {
    const saltRounds = 10;
    return await bcrypt.hash(password, saltRounds);
  }

  async comparePasswords(enteredPassword: string, hashedPassword: string): Promise&amp;lt;boolean&amp;gt; {
    return await bcrypt.compare(enteredPassword, hashedPassword);
  }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ol&gt;
&lt;li&gt;Securing Configuration and Secrets&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Storing sensitive information like database credentials, API keys, and JWT secrets securely is crucial. NestJS provides a built-in configuration module and environment variables for managing sensitive data securely.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// .env
JWT_SECRET=mysecretkey
DATABASE_URL=your_database_url


// database.module.ts
import { Module } from '@nestjs/common';
import { TypeOrmModule } from '@nestjs/typeorm';

@Module({
  imports: [TypeOrmModule.forRoot({
    type: 'postgres',
    url: process.env.DATABASE_URL,
    // other database configurations
  })],
})
export class DatabaseModule {}

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

&lt;/div&gt;



&lt;p&gt;7 .  Rate Limiting and CSRF Protection&lt;/p&gt;

&lt;p&gt;Implementing rate limiting and CSRF protection further enhances security by preventing abuse and protecting against cross-site request forgery attacks. NestJS offers middleware and libraries such as express-rate-limit and csurf for implementing these security measures.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// main.ts
import { NestFactory } from '@nestjs/core';
import { AppModule } from './app.module';
import * as rateLimit from 'express-rate-limit';
import * as csurf from 'csurf';

async function bootstrap() {
  const app = await NestFactory.create(AppModule);

  // Apply rate limiting middleware
  app.use(rateLimit({
    windowMs: 15 * 60 * 1000, // 15 minutes
    max: 100, // limit each IP to 100 requests per windowMs
  }));

  // Apply CSRF protection middleware
  app.use(csurf());

  await app.listen(3000);
}
bootstrap();

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

&lt;/div&gt;



&lt;p&gt;8 . Logging and Monitoring&lt;/p&gt;

&lt;p&gt;Logging and monitoring are essential aspects of security implementation, enabling detection and response to security incidents. NestJS provides robust logging capabilities through various logging libraries such as winston or @nestjs/common. Additionally, integrating monitoring tools like Prometheus and Grafana can provide insights into application performance and security metrics.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// logger.service.ts
import { Injectable, Logger } from '@nestjs/common';

@Injectable()
export class LoggerService extends Logger {
  // Custom logging methods for different log levels
  log(message: string) {
    super.log(message);
    // Additional logging logic
  }

  error(message: string, trace: string) {
    super.error(message, trace);
    // Additional error logging logic
  }

  warn(message: string) {
    super.warn(message);
    // Additional warning logging logic
  }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;9 . Input Validation and Sanitization&lt;/p&gt;

&lt;p&gt;Input validation and sanitization are critical for preventing common security vulnerabilities such as SQL injection, XSS (Cross-Site Scripting), and CSRF (Cross-Site Request Forgery). NestJS provides built-in validation features using libraries like class-validator and class-transformer to validate incoming data against predefined rules.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// create-user.dto.ts
import { IsString, IsNotEmpty, IsEmail } from 'class-validator';

export class CreateUserDto {
  @IsString()
  @IsNotEmpty()
  username: string;

  @IsString()
  @IsNotEmpty()
  @IsEmail()
  email: string;

  // Add more validation rules for other fields
}

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

&lt;/div&gt;



&lt;p&gt;10 . Continuous Security Testing&lt;/p&gt;

&lt;p&gt;Continuous security testing is crucial for identifying and fixing security vulnerabilities throughout the development lifecycle. Integrating security testing tools like OWASP ZAP, SonarQube, and Snyk into your CI/CD pipeline helps automate security testing and ensures that security is an integral part of the development process.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# Example CI/CD pipeline configuration (using GitLab CI)
stages:
  - build
  - test
  - deploy

security_scan:
  stage: test
  image: owasp/zap2docker-stable
  script:
    - zap-baseline.py -t http://your-app-url
  allow_failure: true

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

&lt;/div&gt;



&lt;p&gt;11 . Error Handling and Reporting&lt;/p&gt;

&lt;p&gt;Proper error handling and reporting are crucial for security, as they help identify and mitigate potential vulnerabilities and security incidents. NestJS provides robust error handling mechanisms through interceptors and exception filters, allowing you to gracefully handle errors and log relevant information.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// error.interceptor.ts
import { Injectable, NestInterceptor, ExecutionContext, CallHandler } from '@nestjs/common';
import { Observable, throwError } from 'rxjs';
import { catchError } from 'rxjs/operators';
import { Logger } from '@nestjs/common';

@Injectable()
export class ErrorInterceptor implements NestInterceptor {
  private logger = new Logger('ErrorInterceptor');

  intercept(context: ExecutionContext, next: CallHandler): Observable&amp;lt;any&amp;gt; {
    return next.handle().pipe(
      catchError(error =&amp;gt; {
        this.logger.error(`Error occurred: ${error.message}`, error.stack);
        return throwError(error);
      }),
    );
  }
}

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

&lt;/div&gt;



&lt;ol&gt;
&lt;li&gt;Secure Session Management&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Session management is critical for web application security, ensuring that user sessions are securely managed and authenticated. NestJS provides built-in support for session management through libraries like express-session, allowing you to configure session settings and implement secure session storage mechanisms.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// session.config.ts
import * as session from 'express-session';
import { INestApplication } from '@nestjs/common';

export function configureSession(app: INestApplication) {
  app.use(
    session({
      secret: process.env.SESSION_SECRET,
      resave: false,
      saveUninitialized: false,
      cookie: { secure: true },
    }),
  );
}

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

&lt;/div&gt;



&lt;p&gt;13 . Security Headers and Content Security Policy (CSP)&lt;/p&gt;

&lt;p&gt;Implementing security headers and Content Security Policy (CSP) helps mitigate common web security vulnerabilities such as XSS and clickjacking attacks. NestJS allows you to configure security headers and CSP using middleware and global filters.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// security.middleware.ts
import { Injectable, NestMiddleware } from '@nestjs/common';
import { Request, Response, NextFunction } from 'express';

@Injectable()
export class SecurityMiddleware implements NestMiddleware {
  use(req: Request, res: Response, next: NextFunction) {
    res.setHeader('X-Frame-Options', 'DENY');
    res.setHeader('X-XSS-Protection', '1; mode=block');
    res.setHeader('Content-Security-Policy', "default-src 'self'");
    next();
  }
}

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

&lt;/div&gt;



&lt;p&gt;14 . Secure File Uploads&lt;/p&gt;

&lt;p&gt;Secure file uploads are essential for preventing malicious file uploads and protecting against security vulnerabilities such as file inclusion attacks. NestJS provides built-in support for file uploads using libraries like multer, allowing you to implement file upload validation and security measures.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// upload.service.ts
import { Injectable } from '@nestjs/common';
import { diskStorage } from 'multer';
import { extname } from 'path';

@Injectable()
export class UploadService {
  constructor() {}

  multerOptions = {
    storage: diskStorage({
      destination: './uploads',
      filename: (req, file, callback) =&amp;gt; {
        const uniqueSuffix = Date.now() + '-' + Math.round(Math.random() * 1e9);
        callback(null, uniqueSuffix + extname(file.originalname));
      },
    }),
    fileFilter: (req, file, callback) =&amp;gt; {
      if (!file.originalname.match(/\.(jpg|jpeg|png|gif)$/)) {
        return callback(new Error('Only image files are allowed!'), false);
      }
      callback(null, true);
    },
  };
}

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

&lt;/div&gt;



&lt;ol&gt;
&lt;li&gt;Secure Session Storage&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;When using session-based authentication, ensure that session data is stored securely to prevent session hijacking and tampering. Use secure session storage mechanisms such as encrypted cookies or session stores with strong encryption and access controls to safeguard session data from unauthorized access.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// session.config.ts
import * as session from 'express-session';
import * as connectRedis from 'connect-redis';
import * as redis from 'redis';
import { INestApplication } from '@nestjs/common';

export function configureSession(app: INestApplication) {
  const RedisStore = connectRedis(session);
  const redisClient = redis.createClient();

  app.use(
    session({
      store: new RedisStore({ client: redisClient }),
      secret: process.env.SESSION_SECRET,
      resave: false,
      saveUninitialized: false,
      cookie: { secure: true },
    }),
  );
}

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

&lt;/div&gt;



&lt;p&gt;Incorporating these additional security measures into your NestJS applications, you can enhance overall security resilience and mitigate potential security risks and vulnerabilities effectively. From secure communication and session storage to regular patching and updates, these practices help establish a robust security posture and protect your applications and data from various security threats. &lt;/p&gt;

&lt;p&gt;Also, Always Remember to stay informed about emerging security trends and best practices and continuously evaluate and enhance your security measures to address evolving security challenges effectively.&lt;/p&gt;

&lt;p&gt;Remember that each project has its unique requirements, so adapt these techniques accordingly. Stay up-to-date with the NestJS documentation and the rapidly evolving Node.js ecosystem to make the most of this powerful framework. Happy coding!&lt;/p&gt;

&lt;p&gt;Let's Connect &lt;/p&gt;

&lt;p&gt;&lt;a href="https://www.linkedin.com/in/boyinbode-ebenezer/" rel="noopener noreferrer"&gt;Linkedln&lt;/a&gt; | &lt;a href="https://github.com/drbenzene" rel="noopener noreferrer"&gt;Github&lt;/a&gt; | &lt;a href="https://twitter.com/boyinbodeee" rel="noopener noreferrer"&gt;Twitter&lt;/a&gt; | &lt;a href="https://www.youtube.com/channel/UCLv47WaIjDmnROud6FRmPJg" rel="noopener noreferrer"&gt;Youtube&lt;/a&gt;&lt;/p&gt;

</description>
    </item>
    <item>
      <title>Best NestJS Practices and Advanced Techniques</title>
      <dc:creator>Boyinbode Ebenezer Ayomide</dc:creator>
      <pubDate>Wed, 23 Aug 2023 19:11:10 +0000</pubDate>
      <link>https://forem.com/drbenzene/best-nestjs-practices-and-advanced-techniques-9m0</link>
      <guid>https://forem.com/drbenzene/best-nestjs-practices-and-advanced-techniques-9m0</guid>
      <description>&lt;p&gt;NestJS has quickly gained popularity as a powerful framework for building scalable and maintainable Node.js applications. Its modular architecture, dependency injection system, and support for TypeScript make it an excellent choice for developing complex backend systems. In this blog post, we will explore some of the best practices and advanced techniques for working with NestJS to ensure that your projects are well-structured, performant, and easy to maintain.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Table of Contents&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Setting Up a NestJS Project&lt;/li&gt;
&lt;li&gt;Modular Architecture with Modules and Services&lt;/li&gt;
&lt;li&gt;Dependency Injection and Providers&lt;/li&gt;
&lt;li&gt;Controllers and Routing&lt;/li&gt;
&lt;li&gt;Data Validation and DTOs&lt;/li&gt;
&lt;li&gt;Error Handling and Logging&lt;/li&gt;
&lt;li&gt;Authentication and Authorization&lt;/li&gt;
&lt;li&gt;Testing with Jest&lt;/li&gt;
&lt;li&gt;Performance Optimization&lt;/li&gt;
&lt;li&gt;Deployment and Continuous Integration&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Setting Up a NestJS Project&lt;/strong&gt;&lt;br&gt;
To start a new NestJS project, you can use the Nest CLI:&lt;/p&gt;

&lt;p&gt;Start by installing the NestJS CLI globally on your local machine and also  creating a new Project&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ npm install -g @nestjs/cli
$ nest new project-name
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This will create a basic project structure with a default app module.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Modular Architecture with Modules and Services&lt;/strong&gt;&lt;br&gt;
NestJS encourages a modular architecture to keep your codebase organized. Modules encapsulate related components, and services handle business logic. Here's an example of creating a module and service:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// users.module.ts
import { Module } from '@nestjs/common';
import { UsersController } from './users.controller';
import { UsersService } from './users.service';

@Module({
  controllers: [UsersController],
  providers: [UsersService],
})
export class UsersModule {}

// users.service.ts
import { Injectable } from '@nestjs/common';

@Injectable()
export class UsersService {
  private users = [];

  create(user) {
    this.users.push(user);
  }

  findAll() {
    return this.users;
  }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Dependency Injection and Providers&lt;/strong&gt;&lt;br&gt;
NestJS uses dependency injection to manage component dependencies. Providers are the building blocks of NestJS applications. They can be injected into other components like controllers and services.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// users.controller.ts
import { Controller, Post, Body } from '@nestjs/common';
import { UsersService } from './users.service';

@Controller('users')
export class UsersController {
  constructor(private readonly usersService: UsersService) {}

  @Post()
  create(@Body() user) {
    this.usersService.create(user);
  }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Controllers and Routing&lt;/strong&gt;&lt;br&gt;
Controllers define routes and request handling. They receive incoming requests, process them, and return responses.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// users.controller.ts
import { Controller, Get } from '@nestjs/common';
import { UsersService } from './users.service';

@Controller('users')
export class UsersController {
  constructor(private readonly usersService: UsersService) {}

  @Get()
  findAll() {
    return this.usersService.findAll();
  }
}

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

&lt;/div&gt;



&lt;p&gt;** Data Validation and DTOs**&lt;br&gt;
Using Data Transfer Objects (DTOs) helps validate incoming data and ensure type safety.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// create-user.dto.ts
export class CreateUserDto {
  readonly name: string;
  readonly age: number;
  readonly email: string;
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// users.controller.ts
import { Controller, Post, Body } from '@nestjs/common';
import { UsersService } from './users.service';
import { CreateUserDto } from './create-user.dto';

@Controller('users')
export class UsersController {
  // ...

  @Post()
  create(@Body() createUserDto: CreateUserDto) {
    this.usersService.create(createUserDto);
  }
}

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

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Error Handling and Logging&lt;/strong&gt;&lt;br&gt;
Implement global exception filters for consistent error handling and use logging for better debugging.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// http-exception.filter.ts
import { ExceptionFilter, Catch, ArgumentsHost, HttpException } from '@nestjs/common';

@Catch(HttpException)
export class HttpExceptionFilter implements ExceptionFilter {
  catch(exception: HttpException, host: ArgumentsHost) {
    // Handle exception and log details
  }
}

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

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Authentication and Authorization&lt;/strong&gt;&lt;br&gt;
Implement authentication and authorization using guards and strategies to ensure only authorized access to the controller.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// auth.guard.ts
import { Injectable, CanActivate, ExecutionContext } from '@nestjs/common';

@Injectable()
export class AuthGuard implements CanActivate {
  canActivate(context: ExecutionContext) {
    // Implement authentication logic
    return true;
  }
}

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

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Testing with Jest&lt;/strong&gt;&lt;br&gt;
NestJS supports testing with Jest. Write unit tests for services, controllers, and more.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// users.service.spec.ts
import { Test, TestingModule } from '@nestjs/testing';
import { UsersService } from './users.service';

describe('UsersService', () =&amp;gt; {
  let service: UsersService;

  beforeEach(async () =&amp;gt; {
    const module: TestingModule = await Test.createTestingModule({
      providers: [UsersService],
    }).compile();

    service = module.get&amp;lt;UsersService&amp;gt;(UsersService);
  });

  it('should be defined', () =&amp;gt; {
    expect(service).toBeDefined();
  });
});
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Performance Optimization&lt;/strong&gt;&lt;br&gt;
Performance optimization is crucial to ensuring that your NestJS application runs smoothly and efficiently. By following these techniques, you can enhance the speed, responsiveness, and scalability of your application.&lt;/p&gt;

&lt;p&gt;Implement caching mechanisms to store frequently accessed data in memory, reducing the need to fetch it from the database repeatedly. The cache-manager package can be integrated seamlessly with NestJS to manage caching.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ npm install cache-manager @nestjs/cache
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import { CacheModule, Module } from '@nestjs/common';
import { AppController } from './app.controller';
import { AppService } from './app.service';

@Module({
  imports: [CacheModule.register()],
  controllers: [AppController],
  providers: [AppService],
})
export class AppModule {}

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

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import { CacheInterceptor, CacheTTL, Controller, Get, UseInterceptors } from '@nestjs/common';
import { AppService } from './app.service';

@Controller()
export class AppController {
  constructor(private readonly appService: AppService) {}

  @Get('data')
  @UseInterceptors(CacheInterceptor)
  @CacheTTL(10) // Cache for 10 seconds
  getData() {
    return this.appService.getData();
  }
}

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

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Deployment and Continuous Integration&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Deploying your NestJS application and setting up a robust continuous integration (CI) pipeline are essential steps in the software development lifecycle. These processes ensure that your application is deployed reliably, tested thoroughly, and can scale seamlessly. Here's how to effectively deploy and manage your NestJS application.&lt;/p&gt;

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

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;FROM node:14

WORKDIR /app

COPY package*.json ./

RUN npm install

COPY . .

EXPOSE 3000

CMD ["npm", "run", "start:prod"]

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

&lt;/div&gt;



&lt;p&gt;For multi-container applications, use Docker Compose to define and manage your application's services and dependencies.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;version: '3'
services:
  app:
    build:
      context: .
      dockerfile: Dockerfile
    ports:
      - '3000:3000'

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

&lt;/div&gt;



&lt;p&gt;Setting up CI ensures that your application is automatically built, tested, and deployed whenever changes are pushed to your version control repository. Tools like Jenkins, Travis CI, CircleCI, and GitLab CI/CD can help streamline this process.&lt;/p&gt;

&lt;p&gt;Example .gitlab-ci.yml configuration:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;stages:
  - build
  - test
  - deploy

build:
  stage: build
  script:
    - npm install
    - npm run build

test:
  stage: test
  script:
    - npm run test

deploy:
  stage: deploy
  script:
    - npm install -g @nestjs/cli
    - nest build
    - docker build -t my-nest-app .
    - docker login -u $DOCKER_USERNAME -p $DOCKER_PASSWORD
    - docker push my-nest-app

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

&lt;/div&gt;



&lt;p&gt;By following these best practices and advanced techniques, you can create robust and maintainable applications using NestJS. Its modular architecture, dependency injection system, and support for TypeScript enable you to build scalable and efficient backend systems with ease.&lt;/p&gt;

&lt;p&gt;Remember that each project has its unique requirements, so adapt these techniques accordingly. Stay up-to-date with the NestJS documentation and the rapidly evolving Node.js ecosystem to make the most of this powerful framework. Happy coding!&lt;/p&gt;

&lt;p&gt;Let's Connect &lt;/p&gt;

&lt;p&gt;&lt;a href="https://www.linkedin.com/in/boyinbode-ebenezer/"&gt;Linkedln&lt;/a&gt; | &lt;a href="https://github.com/drbenzene"&gt;Github&lt;/a&gt; | &lt;a href="https://twitter.com/boyinbodeee"&gt;Twitter&lt;/a&gt; | &lt;/p&gt;

</description>
      <category>nestjs</category>
      <category>backenddevelopment</category>
      <category>backend</category>
      <category>typescript</category>
    </item>
  </channel>
</rss>
