<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0"><channel><title><![CDATA[Code With Momin]]></title><description><![CDATA[Code With Momin]]></description><link>https://codewithmomin.hashnode.dev</link><image><url>https://cdn.hashnode.com/res/hashnode/image/upload/v1752997046662/b62623b5-d8af-4a3b-97ab-594d24799f51.png</url><title>Code With Momin</title><link>https://codewithmomin.hashnode.dev</link></image><generator>RSS for Node</generator><lastBuildDate>Thu, 03 Sep 2026 10:18:36 GMT</lastBuildDate><atom:link href="https://codewithmomin.hashnode.dev/rss.xml" rel="self" type="application/rss+xml"/><language><![CDATA[en]]></language><ttl>60</ttl><item><title><![CDATA[How I Used Redis and BullMQ for Background Tasks in My Internship]]></title><description><![CDATA[When I started my internship, I quickly learned that building an app isn't just about making cool buttons and fetching data. Sometimes, your app needs to do a lot of work in the background, like sending a bazillion push notifications. And if you're n...]]></description><link>https://codewithmomin.hashnode.dev/redis-and-bullmq-and-my-crashing-servers</link><guid isPermaLink="true">https://codewithmomin.hashnode.dev/redis-and-bullmq-and-my-crashing-servers</guid><category><![CDATA[Redis]]></category><category><![CDATA[bullmq]]></category><category><![CDATA[Node.js]]></category><category><![CDATA[Express]]></category><category><![CDATA[push notifications]]></category><category><![CDATA[background tasks]]></category><dc:creator><![CDATA[Abdurrahman Momin]]></dc:creator><pubDate>Thu, 24 Jul 2025 06:07:55 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1753336991162/a956600b-5d2b-4065-80f1-8f6fe0f025b5.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>When I started my internship, I quickly learned that building an app isn't just about making cool buttons and fetching data. Sometimes, your app needs to do <em>a lot</em> of work in the background, like sending a bazillion push notifications. And if you're not careful, your shiny new feature can accidentally turn into a server-crashing nightmare. (Yep, almost happened. Don't worry, it's a rite of passage!)</p>
<p>That's where I met my new best friends for scalable apps: <strong>Redis</strong> and <strong>BullMQ</strong>. They helped me solve the "Why is my app so slow when I try to send a simple notification?" puzzle.</p>
<h3 id="heading-the-problem-when-your-app-tries-to-do-too-much-all-at-once">The Problem: When Your App Tries to Do Too Much All at Once</h3>
<p>Imagine you've just uploaded an awesome video, and all your followers need to know. If our app tried to send a push notification to <em>every single follower</em> (let's say hundreds of thousands!) <em>right then and there</em>, it would be like:</p>
<ul>
<li><p><strong>Your App:</strong> "Hold on, I'm trying to send notifications... <em>pant, pant</em>... to... <em>gasp</em>... everyone... <em>system freezes</em>... HELP!"</p>
</li>
<li><p><strong>Users:</strong> "Ugh, why is this app so slow today? Did it crash again?" (Spoiler: it probably did.)</p>
</li>
<li><p><strong>Me (Intern):</strong> <em>Sweating nervously, checking server logs, wondering if I just broke production.</em></p>
</li>
</ul>
<p>We clearly needed a better way. We needed to tell these long, boring tasks, "Hey, you! Go do your thing in the background. Don't bother the main app!"</p>
<hr />
<h3 id="heading-the-solution-our-dynamic-duo-redis-and-bullmq">The Solution: Our Dynamic Duo – Redis and BullMQ!</h3>
<p>So, we decided to build a "task dispatcher" system. Think of it like a super-efficient post office for all the background work. When a new notification needs to go out, we drop it into a special mailbox (our "queue"). Then, dedicated "mail carriers" (our "workers") pick them up and deliver them without interrupting the main show.</p>
<h4 id="heading-redis-the-flashy-super-fast-mailbox">Redis: The Flashy, Super-Fast Mailbox!</h4>
<p>We chose <strong>Redis</strong> to be our super-speedy mailbox. Why Redis? Because it's like a lightning-fast memory bank.</p>
<ul>
<li><p><strong>It Never Forgets (Mostly!):</strong> Even if our server decides to take a coffee break (aka restart), Redis can remember all the tasks still waiting in the mailbox. No lost notifications!</p>
</li>
<li><p><strong>"POOF! Task Dispatched!":</strong> Adding a task to Redis or pulling one out is so fast, it practically happens before you can blink. Essential for handling tons of tasks.</p>
</li>
<li><p><strong>Fun Fact:</strong> Redis is often called a "data structure store" because it can do more than just simple key-value storage; it handles lists, sets, and more, which makes it perfect for queues!</p>
</li>
</ul>
<h4 id="heading-bullmq-the-super-organized-postmaster">BullMQ: The Super Organized Postmaster!</h4>
<p>While Redis is our speedy mailbox, <strong>BullMQ</strong> is the brilliant postmaster that keeps everything in order. It's a fantastic library for Node.js that speaks directly to Redis. BullMQ helps us:</p>
<ul>
<li><p><strong>Label the Packages:</strong> We can clearly define what each task is (e.g., "Send New Video Notification," "Process Image Upload").</p>
</li>
<li><p><strong>Assign Mail Carriers:</strong> We set up separate "workers" whose <em>only job</em> is to pick up specific types of tasks from the mailboxes.</p>
</li>
<li><p><strong>Track Every Package:</strong> Did the notification get sent? Did it fail? BullMQ knows! No more guessing games.</p>
</li>
<li><p><strong>"Oops, Try Again!":</strong> If a task fails (maybe someone's phone was off), BullMQ can automatically try sending it again later. Phew!</p>
</li>
<li><p><strong>No Traffic Jams:</strong> It makes sure our mail carriers aren't tripping over each other, managing how many tasks they handle at once.</p>
</li>
<li><p><strong>"Boss, Look!":</strong> BullMQ provides tools to see how busy our mailroom is, which tasks are pending, and which ones are causing trouble.</p>
</li>
</ul>
<hr />
<h3 id="heading-my-hello-world-of-background-tasks-sending-push-notifications">My "Hello World" of Background Tasks: Sending Push Notifications</h3>
<div data-node-type="callout">
<div data-node-type="callout-emoji">💡</div>
<div data-node-type="callout-text">Here’s a simplified peek at how we used Redis and BullMQ to send those thousands of notifications without breaking a sweat (or the server!).</div>
</div>

<h4 id="heading-1-setting-up-our-notification-mailbox-queue">1. Setting Up Our Notification Mailbox (Queue)</h4>
<p>First, we need to tell BullMQ where our notification tasks will wait. It's like putting up a sign for the "Notification Department" mailbox.</p>
<p>JavaScript</p>
<pre><code class="lang-bash">// A super simple way to <span class="hljs-built_in">set</span> up our notification waiting list (aka <span class="hljs-string">'queue'</span>)
const { Queue } = require(<span class="hljs-string">"bullmq'); // We're using the BullMQ library
const connection = require('../../config/redis'); // This connects us to our super-fast Redis server!

// Let's create a new mailbox. We'll call it "</span>notificationQueue<span class="hljs-string">".
// All notification tasks will line up here.
const notificationQueue = new Queue('notificationQueue', { connection });

module.exports = notificationQueue; // So other parts of our app can use it!</span>
</code></pre>
<h4 id="heading-2-dropping-a-task-into-the-mailbox">2. Dropping a Task into the Mailbox</h4>
<p>When a new video is posted, instead of sending notifications directly, we package up all the notification details and drop them into our <code>notificationQueue</code>. "Here's a job, BullMQ! Handle it when you can!"</p>
<p>JavaScript</p>
<pre><code class="lang-bash">// This is the code that adds a new notification task to our special mailbox
const notificationQueue = require(<span class="hljs-string">'./notificationQueue'</span>);

/**
 * Puts a push notification task into the waiting list <span class="hljs-keyword">for</span> our worker to pick up.
 * No more blocking the user experience! Woohoo!
 * @param {Object} options - All the juicy details <span class="hljs-keyword">for</span> our notification
 * @param {String} options.creatorId - The rockstar who created the content
 * @param {String} options.type - What kind of awesome notification is this? (e.g., <span class="hljs-string">"new_video"</span>)
 * @param {String} options.title - The catchy title of the notification
 * @param {String} options.message - The actual message the user will see
 * @param {Object} [options.data] - Extra bonus data (like video ID, thumbnail image URL, deep links!)
 */
async <span class="hljs-keyword">function</span> sendPushToFollowersByType(options) {
  // We<span class="hljs-string">'re adding a '</span>sendPush<span class="hljs-string">' task to our queue. BullMQ handles the rest!
  const job = await notificationQueue.add('</span>sendPush<span class="hljs-string">', options);
  return job.id; // Here'</span>s a unique ID <span class="hljs-keyword">for</span> this specific task, just <span class="hljs-keyword">in</span> <span class="hljs-keyword">case</span> we need to check on it later!
}

module.exports = sendPushToFollowersByType;
</code></pre>
<h4 id="heading-3-the-dedicated-worker-our-notification-delivery-person">3. The Dedicated Worker: Our Notification Delivery Person</h4>
<p>Separately, we have a "worker" program running. This guy's sole purpose in life is to constantly check the <code>notificationQueue</code>. When a new task pops up, it grabs it and gets to work sending out those push notifications!</p>
<p>JavaScript</p>
<pre><code class="lang-bash">// This is our tireless worker. It never sleeps (unless the server crashes, oops!).
const { Worker } = require(<span class="hljs-string">'bullmq'</span>);
const connection = require(<span class="hljs-string">'../config/redis'</span>); // Same Redis connection, so it knows <span class="hljs-built_in">where</span> the mailbox is

// For a real app, these would be proper <span class="hljs-built_in">functions</span> interacting with databases and push services:
// const fetchFollowersFromDatabase = require(<span class="hljs-string">'../models/Follow'</span>); // To find out who follows whom
// const fetchUserPushTokens = require(<span class="hljs-string">'../models/User'</span>); // To get the actual phone tokens
// const firebaseAdmin = require(<span class="hljs-string">'../utils/pushNotification/firebaseAdmin'</span>); // Our magical push notification sender

const worker = new Worker(<span class="hljs-string">'notificationQueue'</span>, async (job) =&gt; {
  const { creatorId, <span class="hljs-built_in">type</span>, title, message, data = {} } = job.data;

  console.log(`🎉 Worker picking up a job <span class="hljs-keyword">for</span> creator <span class="hljs-variable">${creatorId}</span> (Type: <span class="hljs-variable">${type}</span>). Let<span class="hljs-string">'s get these notifications out!`);

  // Step 1: Log this notification event. Because history is cool.
  // Imagine doing something like: await NotificationEvent.create({ creatorId, type, title, message, ...data });

  // Step 2: Find all the awesome followers who should get this notification!
  // This is where we'</span>d hit our database:
  // const followers = await fetchFollowersFromDatabase({ followingId: creatorId, notificationsEnabled: <span class="hljs-literal">true</span> });
  const followers = [<span class="hljs-string">'followerBob'</span>, <span class="hljs-string">'followerAlice'</span>, <span class="hljs-string">'followerCharlie'</span>]; // Placeholder <span class="hljs-keyword">for</span> our imaginary followers

  <span class="hljs-keyword">if</span> (!followers.length) {
    console.log(<span class="hljs-string">"🦗 Crickets... no followers to notify this time. Moving on!"</span>);
    <span class="hljs-built_in">return</span>; // Nothing to <span class="hljs-keyword">do</span> here!
  }

  // Step 3: Get their device tokens! (The unique IDs <span class="hljs-keyword">for</span> their phones)
  // Again, a database query here:
  // const usersWithTokens = await fetchUserPushTokens({ _id: { <span class="hljs-variable">$in</span>: followers.map(f =&gt; f.followerId) } });
  const deviceTokens = [<span class="hljs-string">'phoneToken123'</span>, <span class="hljs-string">'phoneToken456'</span>, <span class="hljs-string">'phoneToken789'</span>]; // Imaginary phone tokens!

  <span class="hljs-keyword">if</span> (!deviceTokens.length) {
    console.log(<span class="hljs-string">"📱 No phone tokens found for these followers. Guess they don't want push notifications!"</span>);
    <span class="hljs-built_in">return</span>;
  }

  // Step 4: Time to send the notifications!
  // IMPORTANT: Push services (like Firebase) often have limits, so we send <span class="hljs-keyword">in</span> batches!
  const chunkSize = 500; // Firebase<span class="hljs-string">'s typical limit
  for (let i = 0; i &lt; deviceTokens.length; i += chunkSize) {
    const chunk = deviceTokens.slice(i, i + chunkSize);
    console.log(`📦 Sending a batch of ${chunk.length} notifications to the push service!`);

    // In a real app, this is where we'</span>d call our actual push notification service:
    // const response = await firebaseAdmin.messaging().sendEach(chunk.map(token =&gt; ({
    //   token,
    //   notification: { title, body: message },
    //   data: { ...data, <span class="hljs-built_in">type</span>, creatorId: String(creatorId) }
    // })));

    // Handle any errors from the push service (e.g., token expired)
    // response.responses.forEach((res, idx) =&gt; {
    //   <span class="hljs-keyword">if</span> (!res.success) {
    //     console.warn(<span class="hljs-string">"🚨 Failed for token"</span>, chunk[idx], res.error?.message);
    //     // Optional: Mark this token as invalid <span class="hljs-keyword">in</span> your database to avoid future failures
    //   }
    // });
  }

  console.log(`✅ Notification task <span class="hljs-keyword">for</span> creator <span class="hljs-variable">${creatorId}</span> completed! Mission accomplished.`);
}, {
  connection, // Our trusty Redis connection again!
});

// Listener <span class="hljs-keyword">for</span> when a task is <span class="hljs-keyword">done</span>. Yay!
worker.on(<span class="hljs-string">'completed'</span>, (job) =&gt; {
  console.log(`✨ Job <span class="hljs-variable">${job.id}</span> finished successfully. Time <span class="hljs-keyword">for</span> a virtual high-five!`);
});

// Listener <span class="hljs-keyword">for</span> when a task fails. Boo! But we can learn from it.
worker.on(<span class="hljs-string">'failed'</span>, (job, err) =&gt; {
  console.error(`💔 Job <span class="hljs-variable">${job.id}</span> failed:`, err.message, <span class="hljs-string">'...checking the logs for clues!'</span>);
});
</code></pre>
<h3 id="heading-what-i-gained-besides-not-crashing-the-server">What I Gained (Besides Not Crashing the Server!)</h3>
<p>Using this Redis and BullMQ magic during my internship gave me some seriously cool insights:</p>
<ul>
<li><p><strong>My App Breathed Easier:</strong> No more slow app! Users got instant responses while notifications were handled quietly in the background.</p>
</li>
<li><p><strong>Scalability Superpowers:</strong> We could handle way more users and tasks without breaking a sweat. Just add more workers if things get busy!</p>
</li>
<li><p><strong>"Oops, I Did It Again" Recovery:</strong> If a notification failed (it happens!), BullMQ could automatically retry it. So much less manual work for me!</p>
</li>
<li><p><strong>Happy Developers, Happy Codebase:</strong> Splitting up tasks made our code much cleaner and easier to manage.</p>
</li>
<li><p><strong>Intern Confession:</strong> This setup probably saved me from accidentally crashing the production server at least once. Phew!</p>
</li>
</ul>
<p>My internship project with Redis and BullMQ taught me that handling long-running tasks asynchronously is a game-changer for building robust, high-performance applications. If you're looking to make your app fast, reliable, and intern-proof, definitely check these tools out!</p>
]]></content:encoded></item><item><title><![CDATA[From Localhost to Live : How I Used CyberPanel & SSH to Deploy My First Backend Server]]></title><description><![CDATA[📌 Introduction – Why Even Learn This?
As developers, we often build our backend projects on localhost, test APIs with Postman, and store data in a local database. But deploying those same projects for the world to use is an entirely different game.
...]]></description><link>https://codewithmomin.hashnode.dev/from-localhost-to-live-how-i-used-cyberpanel-and-ssh-to-deploy-my-first-backend-server</link><guid isPermaLink="true">https://codewithmomin.hashnode.dev/from-localhost-to-live-how-i-used-cyberpanel-and-ssh-to-deploy-my-first-backend-server</guid><category><![CDATA[#cyberpanel]]></category><category><![CDATA[ssh]]></category><category><![CDATA[GitHub]]></category><category><![CDATA[hosting]]></category><category><![CDATA[backend]]></category><dc:creator><![CDATA[Abdurrahman Momin]]></dc:creator><pubDate>Sun, 20 Jul 2025 06:49:47 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1752994087741/cd8c6547-90c6-4fb0-a328-af536206fd74.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<h2 id="heading-introduction-why-even-learn-this">📌 <strong>Introduction – Why Even Learn This?</strong></h2>
<p>As developers, we often build our backend projects on <strong>localhost</strong>, test APIs with Postman, and store data in a local database. But deploying those same projects for the world to use is an entirely different game.</p>
<p>Most companies — whether startups or tech giants — <strong>host their applications on VPS (Virtual Private Servers)</strong> or dedicated cloud infrastructure. These servers don’t come with fancy UI by default — you’re expected to know how to manage them using tools like:</p>
<ul>
<li><p><strong>SSH (Secure Shell)</strong> for secure remote access</p>
</li>
<li><p><strong>Server control panels</strong> like <strong>CyberPanel</strong>, <strong>cPanel</strong>, or even raw Nginx/Apache configuration</p>
</li>
</ul>
<p>When I joined a company as a backend intern, I had the opportunity to get hands-on with <strong>CyberPanel + SSH</strong> to deploy real applications. In this article, I’ll walk you through <strong>what these tools are</strong>, <strong>why they matter</strong>, <strong>how I used them</strong>, and how you can get started too.</p>
<h2 id="heading-what-is-cyberpanel">🧩 What Is <strong><mark>CyberPanel </mark></strong> ?</h2>
<p>CyberPanel is a <strong>web-based hosting control panel</strong> built on top of <strong>OpenLiteSpeed</strong>, a fast and lightweight web server. It’s a powerful tool that simplifies the deployment and management of web applications.</p>
<p>Instead of writing long Nginx or Apache config files manually, CyberPanel gives you an intuitive UI to manage:</p>
<ul>
<li><p><strong>Websites</strong> (custom domains, subdomains)</p>
</li>
<li><p><strong>Databases</strong> (MySQL/PostgreSQL)</p>
</li>
<li><p><strong>SSL Certificates</strong> (via Let’s Encrypt)</p>
</li>
<li><p><strong>File Management</strong></p>
</li>
<li><p><strong>Cron Jobs</strong> (to run scripts at intervals)</p>
</li>
<li><p><strong>FTP, PHP, Email, DNS,</strong> and more</p>
</li>
</ul>
<div data-node-type="callout">
<div data-node-type="callout-emoji">💡</div>
<div data-node-type="callout-text"><strong>Fun fact:</strong> CyberPanel is popular among small businesses, agencies, and indie developers who want to host their own applications affordably, yet professionally.</div>
</div>

<hr />
<h2 id="heading-what-is-ssh">🔐 <strong>What Is <mark>SSH</mark></strong></h2>
<p>SSH (Secure Shell) is the <strong>standard way to securely access and control your VPS/server remotely</strong> from your computer.</p>
<p>With SSH, you can:</p>
<ul>
<li><p>Log in to your server from anywhere using the terminal</p>
</li>
<li><p>Install packages, edit files, or restart services</p>
</li>
<li><p>Pull your code from GitHub or run <code>npm install</code></p>
</li>
<li><p>Check logs, errors, and performance in real-time</p>
</li>
</ul>
<blockquote>
<div data-node-type="callout">
<div data-node-type="callout-emoji">💡</div>
<div data-node-type="callout-text">Think of it like having <strong>“admin-level terminal access”</strong> to a computer that’s sitting in a data center miles away.</div>
</div>

</blockquote>
<hr />
<h3 id="heading-how-to-get-started-with-ssh-using-putty">🛠️ <strong>How to Get Started with <mark>SSH (Using PuTTY )</mark></strong></h3>
<p>If you’re on Windows, one of the easiest ways to use SSH is with <strong>PuTTY</strong>.</p>
<ol>
<li><p><strong>Download PuTTY:</strong><br /> <a target="_blank" href="https://www.putty.org/">https://www.putty.org/</a></p>
</li>
<li><p><strong>Get Your VPS IP and Root Password:</strong><br /> Usually provided by your VPS provider (Hostlastic, Hostinger, DigitalOcean, Hostlasting etc.)</p>
</li>
<li><p><strong>Login to Server:</strong><br /> Open PuTTY, enter your server IP, and connect. Use username <code>root</code> and the password to log in.</p>
</li>
<li><p><strong>Run Commands on the Server:</strong><br /> Now you can install Node.js, configure your app, run logs, etc., right from your terminal.</p>
</li>
</ol>
<p>If you're on Mac/Linux, just open your terminal and use:</p>
<pre><code class="lang-bash">root@your-server-ip
</code></pre>
<hr />
<h2 id="heading-how-i-used-cyberpanel-ssh-as-backend-engineer">🌐 <strong>How I Used <mark>CyberPanel + SSH </mark> as Backend Engineer</strong></h2>
<p>Here’s how my actual workflow looked during my internship:</p>
<h3 id="heading-1-developed-and-tested-apis-locally">1. <strong>Developed and Tested APIs Locally</strong></h3>
<ul>
<li><p>Built RESTful APIs using <strong>Node.js</strong>, <strong>Express</strong>, and <strong>MongoDB</strong></p>
</li>
<li><p>Used <strong>Postman</strong> to test all routes locally (<a target="_blank" href="http://localhost:3000"><code>localhost:3000</code></a>)</p>
</li>
</ul>
<h3 id="heading-2-pushed-code-to-github">2. <strong>Pushed Code to GitHub</strong></h3>
<p>After local testing:</p>
<pre><code class="lang-bash">bashCopyEditgit add .
git commit -m <span class="hljs-string">"Added notification module"</span>
git push origin main
</code></pre>
<p>Code was now ready to be deployed to the live server.</p>
<h3 id="heading-3-logged-into-vps-via-ssh">3. <strong>Logged into VPS via SSH</strong></h3>
<p>Used <strong>SSH</strong> to securely connect to the remote server:</p>
<pre><code class="lang-bash">bashCopyEditssh root@your-vps-ip
</code></pre>
<p>This gave me terminal access to run commands directly on the server.</p>
<ul>
<li><p>I used <strong>PuTTY</strong> on Windows</p>
</li>
<li><p>On Mac/Linux, I used the default terminal</p>
</li>
</ul>
<h3 id="heading-4-cloned-pulled-the-code-on-the-server">4. <strong>Cloned / Pulled the Code on the Server</strong></h3>
<p>After SSH access, I navigated to my project folder and pulled the latest code:</p>
<pre><code class="lang-bash"><span class="hljs-built_in">cd</span> /home/yourdomain.com/public_html
git pull origin main
</code></pre>
<h3 id="heading-5-installed-project-dependencies">5. <strong>Installed Project Dependencies</strong></h3>
<p>If there were changes in <code>package.json</code> (new packages):</p>
<pre><code class="lang-bash">bashCopyEditnpm install
</code></pre>
<p>This ensured all required modules were present for the latest version.</p>
<h3 id="heading-6-configured-app-to-run-via-openlitespeed">6. <strong>Configured App to Run via OpenLiteSpeed</strong></h3>
<p>Instead of PM2, I configured <strong>OpenLiteSpeed</strong> to serve my Node.js app:</p>
<ul>
<li><p>Created a <strong>new virtual host</strong> from the CyberPanel dashboard</p>
</li>
<li><p>Set up the <strong>listener</strong> to point to the correct domain/subdomain</p>
</li>
<li><p>Edited <strong>startup script</strong> to run <code>node app.js</code> via OLS’s WebAdmin Console (or created custom script using <code>nohup</code> or background process)</p>
</li>
<li><p>Bound the backend app to a specific port like <code>3001</code>, and used <strong>reverse proxy rules</strong> to serve that port through the domain (e.g., <a target="_blank" href="http://api.mysite.com"><code>api.mysite.com</code></a>)</p>
</li>
</ul>
<blockquote>
<div data-node-type="callout">
<div data-node-type="callout-emoji">💡</div>
<div data-node-type="callout-text"><strong>Reverse Proxy</strong> is the key step: OLS listens on port 443 (HTTPS), and forwards the request to your app running on <a target="_self" href="http://localhost:3001"><code>localhost:3001</code></a>.</div>
</div>

</blockquote>
<h3 id="heading-8-tested-apis-on-live-server">8. <strong>Tested APIs on Live Server</strong></h3>
<p>I then replaced <a target="_blank" href="http://localhost">localhost</a> URLs in Postman with my live domain:</p>
<pre><code class="lang-bash">bashCopyEdithttps://api.myapp.com/login
https://api.myapp.com/notifications
</code></pre>
<p>I tested all endpoints again to ensure:</p>
<ul>
<li><p>Data was coming from MongoDB</p>
</li>
<li><p>Auth tokens worked</p>
</li>
<li><p>Everything was fast and secure</p>
</li>
</ul>
<hr />
<h2 id="heading-why-this-workflow-was-valuable">Why This Workflow Was Valuable</h2>
<ul>
<li><p><strong>CyberPanel</strong> simplified complex server setup</p>
</li>
<li><p><strong>OpenLiteSpeed</strong> gave great performance with minimal configuration</p>
</li>
<li><p><strong>SSH</strong> gave me full control over my project like a real DevOps engineer</p>
</li>
<li><p><strong>Reverse proxy</strong> setup made Node.js APIs production-ready</p>
</li>
<li><p>I deployed <strong>secure</strong>, <strong>scalable</strong>, and <strong>real-time</strong> backend APIs that handled real user data</p>
</li>
</ul>
<hr />
<h2 id="heading-useful-resources">📚 Useful Resources</h2>
<ul>
<li><p><a target="_blank" href="https://cyberpanel.net/KnowledgeBase/">CyberPanel Docs</a></p>
</li>
<li><p><a target="_blank" href="https://docs.openlitespeed.org/">OpenLiteSpeed Docs</a></p>
</li>
<li><p><a target="_blank" href="https://docs.openlitespeed.org/config/appserver/nodejs/">Set up Node.js with OpenLiteSpeed</a></p>
</li>
<li><p><a target="_blank" href="https://www.digitalocean.com/community/tutorials?q=ssh">SSH Essentials – DigitalOcean</a></p>
</li>
<li><p><a target="_blank" href="https://docs.github.com/en/actions/deployment">GitHub Deployment Guide</a></p>
</li>
</ul>
<h2 id="heading-final-thoughts">Final Thoughts</h2>
<p>Learning to deploy and manage my own backend server using CyberPanel and OpenLiteSpeed was a huge step forward for me. It taught me not just to build software, but to run it reliably in production — just like real companies do.</p>
]]></content:encoded></item></channel></rss>