<?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[Auth in Microservice Architecture]]></title><description><![CDATA[Auth in Microservice Architecture]]></description><link>https://auth-in-microservice-architecture.hashnode.dev</link><generator>RSS for Node</generator><lastBuildDate>Sun, 06 Sep 2026 22:46:49 GMT</lastBuildDate><atom:link href="https://auth-in-microservice-architecture.hashnode.dev/rss.xml" rel="self" type="application/rss+xml"/><language><![CDATA[en]]></language><ttl>60</ttl><item><title><![CDATA[Flow of Microservice Architecture]]></title><description><![CDATA[The Best Practice: Stateless JWTs for Microservices ✅
For a microservice architecture, you are correct that you don't want a central session database that every service has to call. This creates a bottleneck and a single point of failure. The best pr...]]></description><link>https://auth-in-microservice-architecture.hashnode.dev/flow-of-microservice-architecture</link><guid isPermaLink="true">https://auth-in-microservice-architecture.hashnode.dev/flow-of-microservice-architecture</guid><dc:creator><![CDATA[Syed Wasif Hussain]]></dc:creator><pubDate>Sat, 30 Aug 2025 06:49:29 GMT</pubDate><content:encoded><![CDATA[<h3 id="heading-the-best-practice-stateless-jwts-for-microservices">The Best Practice: Stateless JWTs for Microservices ✅</h3>
<p>For a microservice architecture, you are correct that you don't want a central session database that every service has to call. This creates a bottleneck and a single point of failure. The best practice is a stateless JWT flow.</p>
<p>Here’s how it works:</p>
<ol>
<li><p><strong>Central Auth Service:</strong> You have one dedicated microservice (e.g., <code>auth-service</code>) responsible for user registration and login.</p>
</li>
<li><p><strong>Token Issuance:</strong> When a user logs in via the <code>auth-service</code>, it verifies their credentials and issues a signed JWT. This JWT contains the <code>userId</code> and any other relevant, non-sensitive data (like user roles).</p>
</li>
<li><p><strong>Client Storage:</strong> The client (e.g., a web browser or mobile app) receives this JWT and stores it.</p>
</li>
<li><p><strong>API Gateway &amp; Service Calls:</strong> The client includes the JWT in the <code>Authorization</code> header for all future requests to other microservices (e.g., <code>product-service</code>, <code>order-service</code>).</p>
</li>
<li><p><strong>Stateless Verification:</strong> Each microservice has access to the same <code>JWT_SECRET</code>. When a request comes in, the service can independently verify the JWT's signature. If the signature is valid, the service trusts the payload and processes the request. <strong>No database call to Redis or a central auth server is needed for verification.</strong></p>
</li>
</ol>
<h3 id="heading-now-elaberating-the-5th-point"><strong>Now elaberating the 5th point:</strong></h3>
<p><strong>A Question In my Mind</strong></p>
<p><strong><em><mark>so each microservice will have jwt_secret in .env ets say authService have ‘</mark></em></strong><code>kafafjajfljflfajla</code> <strong><em><mark>‘ this as the secret key and productSerrvice have ‘</mark></em></strong><code>dshfsaflsajfsafa</code> <strong><em><mark>‘ this as a secret key in their .env file should the secret key must be same , and also how it will understand that the user is loged in or authenticated.</mark></em></strong></p>
<p>The secret key <strong>must be <mark>identical</mark></strong> across all services that need to validate the JWT.</p>
<hr />
<h3 id="heading-the-secret-key-the-master-key">The Secret Key: The Master Key 🔑</h3>
<p>Think of the <code>JWT_SECRET</code> as a master key for a building.</p>
<ul>
<li><p>The <code>auth-service</code> is the only one that can <strong>create and issue</strong> new master keys (by signing the JWTs).</p>
</li>
<li><p>All other microservices ( <code>product-service</code>, <code>order-service</code>, etc.) are given a <strong>copy of that same master key</strong>.</p>
</li>
</ul>
<p>If the <code>auth-service</code> signs a token with <code>key-A</code> and the <code>product-service</code> tries to verify it using <code>key-B</code>, the verification will fail. The <code>product-service</code> will conclude that the token is a forgery because it wasn't signed with the one and only master key it trusts.</p>
<p><strong>So, to be crystal clear:</strong></p>
<p><strong>If your auth-service .env has:</strong></p>
<p>JWT_SECRET="<code>kafafjajfljflfajla</code>"</p>
<p><strong>Then your product-service .env must also have:</strong></p>
<p>JWT_SECRET="<code>kafafjajfljflfajla</code>"</p>
<p>They must be exactly the same.</p>
<hr />
<h3 id="heading-how-a-service-knows-youre-authenticated-the-ticket-analogy">How a Service "Knows" You're Authenticated: The Ticket Analogy 🎟️</h3>
<p>This is the key difference between stateful (sessions) and stateless (JWT) thinking. A microservice doesn't "know" a user is logged in in the way a traditional application does.</p>
<p>Instead, it validates proof on a <strong>per-request basis</strong>.</p>
<p>Let's use a concert analogy:</p>
<ol>
<li><p><strong>Logging In (The Ticket Booth):</strong> You go to the <code>auth-service</code> (the ticket booth) with your credentials (username/password). It verifies who you are and gives you a ticket (the JWT). This ticket is now your proof of identity.</p>
</li>
<li><p><strong>Accessing a Route (The Concert Hall Entrance):</strong> You then go to the <code>product-service</code> (a concert hall entrance). To get in, you must present your ticket (the JWT in the <code>Authorization</code> header).</p>
</li>
<li><p><strong>Authentication (The Usher's Job):</strong> The <code>product-service</code> (the usher) doesn't remember you from the ticket booth. It doesn't need to. It just performs two checks on the ticket itself:</p>
<ul>
<li><p><strong>Is the ticket authentic?</strong> It checks the signature using its copy of the master key (<code>JWT_SECRET</code>). If the signature is valid, it knows the ticket was issued by the official ticket booth (<code>auth-service</code>) and hasn't been tampered with.</p>
</li>
<li><p><strong>Has the ticket expired?</strong> It checks the expiration date (<code>exp</code> claim) on the ticket.</p>
</li>
</ul>
</li>
</ol>
<p>If your ticket passes both checks, the usher lets you in for that one specific request. The service considers you "authenticated" <strong>for that transaction only</strong>. For your next request, you must show the ticket again, and it will be re-verified.</p>
<p>The service doesn't maintain a list of "logged-in" users. It simply trusts the verifiable proof (the JWT) that comes with every single request.</p>
<h3 id="heading-lets-understand-it-with-a-simple-example"><strong>Lets understand it with a Simple example :</strong></h3>
<p><strong>So basically we are talking about Asynchronous Microservice architecture.</strong></p>
<h3 id="heading-phase-1-the-front-door-synchronous">Phase 1: The "Front Door" (Synchronous)</h3>
<p>Every user interaction that requires immediate feedback must be synchronous. This is the entry point to your system.</p>
<ul>
<li><p><strong>Action:</strong> User logs in, user submits an order, user updates their profile.</p>
</li>
<li><p><strong>Mechanism:</strong> Standard HTTP Request/Response.</p>
</li>
<li><p><strong>Security:</strong> This is where <strong>JWT</strong> is essential. The server authenticates the user and provides an immediate response (<code>token</code>, <code>200 OK</code>, <code>401 Unauthorized</code>).</p>
<p>  <strong>Imagine we have two microservices:</strong></p>
<ul>
<li><p><strong>Auth Service</strong> (runs on <a target="_blank" href="http://localhost:4000"><code>localhost:4000</code></a>): Handles user login and creates JWTs.</p>
</li>
<li><p><strong>Data Service</strong> (runs on <a target="_blank" href="http://localhost:5000"><code>localhost:5000</code></a>): Serves protected data that only authenticated users can access.</p>
</li>
</ul>
</li>
</ul>
<hr />
<h3 id="heading-step-1-the-shared-secret-setup">Step 1: The Shared Secret (Setup)</h3>
<p>    Both services need the exact same secret key.</p>
<p>    <strong>In both the</strong> <code>auth-service</code> and <code>data-service</code> project folders, create a <code>.env</code> file:</p>
<p>    Code snippet</p>
<pre><code class="lang-javascript">    # This MUST be identical <span class="hljs-keyword">in</span> both services
    JWT_SECRET=<span class="hljs-string">"this-is-our-shared-secret-key-12345"</span>
</code></pre>
<p>    You'll also need to install packages in both projects: <code>npm install express jsonwebtoken dotenv</code>.</p>
<hr />
<h3 id="heading-step-2-auth-service-creating-the-token">Step 2: Auth Service (Creating the Token) 🎟️</h3>
<p>    This service gives the user their "ticket" (the JWT) after they log in.</p>
<p>    <code>auth-service/index.js</code></p>
<p>    JavaScript</p>
<pre><code class="lang-javascript">    <span class="hljs-built_in">require</span>(<span class="hljs-string">'dotenv'</span>).config();
    <span class="hljs-keyword">const</span> express = <span class="hljs-built_in">require</span>(<span class="hljs-string">'express'</span>);
    <span class="hljs-keyword">const</span> jwt = <span class="hljs-string">'jsonwebtoken'</span>;

    <span class="hljs-keyword">const</span> app = express();
    app.use(express.json());

    <span class="hljs-comment">// A simple login route</span>
    app.post(<span class="hljs-string">'/login'</span>, <span class="hljs-function">(<span class="hljs-params">req, res</span>) =&gt;</span> {
        <span class="hljs-comment">// For this example, we'll accept any user named "test"</span>
        <span class="hljs-keyword">const</span> { username, password } = req.body;
        <span class="hljs-keyword">if</span> (username === <span class="hljs-string">'test'</span> &amp;&amp; password === <span class="hljs-string">'password'</span>) {
            <span class="hljs-comment">// User is valid, create the "ticket" (JWT)</span>
            <span class="hljs-keyword">const</span> userPayload = { <span class="hljs-attr">userId</span>: <span class="hljs-string">'user123'</span>, <span class="hljs-attr">role</span>: <span class="hljs-string">'customer'</span> };

            <span class="hljs-keyword">const</span> token = jwt.sign(
                userPayload,
                process.env.JWT_SECRET,
                { <span class="hljs-attr">expiresIn</span>: <span class="hljs-string">'1h'</span> } <span class="hljs-comment">// Token expires in 1 hour</span>
            );

            res.json({ <span class="hljs-attr">token</span>: token });
        } <span class="hljs-keyword">else</span> {
            res.status(<span class="hljs-number">401</span>).send(<span class="hljs-string">'Invalid credentials'</span>);
        }
    });

    app.listen(<span class="hljs-number">4000</span>, <span class="hljs-function">() =&gt;</span> <span class="hljs-built_in">console</span>.log(<span class="hljs-string">'Auth Service running on port 4000'</span>));
</code></pre>
<p>    <strong>To test this:</strong> Run the service and send a POST request to <a target="_blank" href="http://localhost:4000/login"><code>http://localhost:4000/login</code></a>. You will get a JWT back.</p>
<hr />
<h3 id="heading-step-3-the-client-using-the-token">Step 3: The Client (Using the Token)</h3>
<p>    Now the user's browser (the client) has the token. To get protected data, it sends that token to the Data Service.</p>
<p>    Here's how that request looks in JavaScript:</p>
<ul>
<li><p>The code is an example of what the <strong>client (frontend)</strong> does. You would <strong>not</strong> put this code inside your microservices.</p>
<pre><code class="lang-javascript">  <span class="hljs-comment">// The token we got from the /login endpoint</span>
  <span class="hljs-keyword">const</span> myToken = <span class="hljs-string">"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."</span>; <span class="hljs-comment">// a long string</span>

  fetch(<span class="hljs-string">'http://localhost:5000/data'</span>, {
      <span class="hljs-attr">method</span>: <span class="hljs-string">'GET'</span>,
      <span class="hljs-attr">headers</span>: {
          <span class="hljs-comment">// This is the crucial part</span>
          <span class="hljs-string">'Authorization'</span>: <span class="hljs-string">`Bearer <span class="hljs-subst">${myToken}</span>`</span>
      }
  })
  .then(<span class="hljs-function"><span class="hljs-params">res</span> =&gt;</span> res.json())
  .then(<span class="hljs-function"><span class="hljs-params">data</span> =&gt;</span> <span class="hljs-built_in">console</span>.log(data));
</code></pre>
<hr />
<h3 id="heading-step-4-data-service-verifying-the-token">Step 4: Data Service (Verifying the Token) 🛡️</h3>
<p>  This service acts as the "usher" checking the ticket. It uses the shared secret to verify the token it receives.</p>
<p>  <code>data-service/index.js</code></p>
<p>  JavaScript</p>
<pre><code class="lang-javascript">  <span class="hljs-built_in">require</span>(<span class="hljs-string">'dotenv'</span>).config();
  <span class="hljs-keyword">const</span> express = <span class="hljs-built_in">require</span>(<span class="hljs-string">'express'</span>);
  <span class="hljs-keyword">const</span> jwt = <span class="hljs-built_in">require</span>(<span class="hljs-string">'jsonwebtoken'</span>);

  <span class="hljs-keyword">const</span> app = express();

  <span class="hljs-comment">// Middleware to check the "ticket" (JWT)</span>
  <span class="hljs-keyword">const</span> verifyToken = <span class="hljs-function">(<span class="hljs-params">req, res, next</span>) =&gt;</span> {
      <span class="hljs-keyword">const</span> authHeader = req.headers[<span class="hljs-string">'authorization'</span>];
      <span class="hljs-keyword">const</span> token = authHeader &amp;&amp; authHeader.split(<span class="hljs-string">' '</span>)[<span class="hljs-number">1</span>];

      <span class="hljs-keyword">if</span> (!token) <span class="hljs-keyword">return</span> res.status(<span class="hljs-number">401</span>).send(<span class="hljs-string">'Access Denied'</span>);

      <span class="hljs-keyword">try</span> {
          <span class="hljs-comment">// Here, it uses the SAME secret to verify the ticket's authenticity</span>
          <span class="hljs-keyword">const</span> verifiedPayload = jwt.verify(token, process.env.JWT_SECRET);
          req.user = verifiedPayload; <span class="hljs-comment">// Attach user info to the request</span>
          next(); <span class="hljs-comment">// Ticket is valid, proceed</span>
      } <span class="hljs-keyword">catch</span> (err) {
          res.status(<span class="hljs-number">403</span>).send(<span class="hljs-string">'Invalid Token'</span>);
      }
  };

  <span class="hljs-comment">// A protected route. The verifyToken middleware runs first.</span>
  app.get(<span class="hljs-string">'/data'</span>, verifyToken, <span class="hljs-function">(<span class="hljs-params">req, res</span>) =&gt;</span> {
      <span class="hljs-comment">// If we reach here, the token was valid.</span>
      <span class="hljs-comment">// We can safely use the user data attached by the middleware.</span>
      res.json({
          <span class="hljs-attr">message</span>: <span class="hljs-string">'Here is your protected data!'</span>,
          <span class="hljs-attr">accessedBy</span>: req.user.userId <span class="hljs-comment">// e.g., "user123"</span>
      });
  });

  app.listen(<span class="hljs-number">5000</span>, <span class="hljs-function">() =&gt;</span> <span class="hljs-built_in">console</span>.log(<span class="hljs-string">'Data Service running on port 5000'</span>));
</code></pre>
<h3 id="heading-the-flow-summarized">The Flow Summarized:</h3>
<ol>
<li><p>A user sends their credentials to the <strong>Auth Service</strong>.</p>
</li>
<li><p>The <strong>Auth Service</strong> validates them and uses the <code>JWT_SECRET</code> to <em>sign</em> and create a JWT. It sends this token back.</p>
</li>
<li><p>The user's browser makes a request to the <strong>Data Service</strong>, putting the JWT in the <code>Authorization: Bearer &lt;token&gt;</code> header.</p>
</li>
<li><p>The <strong>Data Service</strong>'s middleware intercepts the request. It uses its identical copy of the <code>JWT_SECRET</code> to <em>verify</em> the token.</p>
</li>
<li><p>If verification succeeds, the request is allowed, and the service knows who the user is from the token's payload. If not, it's rejected.</p>
</li>
</ol>
</li>
</ul>
<h3 id="heading-phase-2-the-back-office-asynchronous">Phase 2: The "Back Office" (Asynchronous)</h3>
<p>Once the initial synchronous request is authenticated and accepted, you can then trigger background tasks that don't need immediate user feedback.</p>
<ul>
<li><p><strong>Action:</strong> Sending a confirmation email, processing a video upload, generating a report.</p>
</li>
<li><p><strong>Mechanism:</strong> The API server publishes a job to a message queue like <strong>BullMQ</strong>, RabbitMQ, or Kafka.</p>
</li>
<li><p><strong>Security:</strong> The job payload contains the <strong>trusted data</strong> (like <code>userId</code>) that was extracted from the JWT during Phase 1. Downstream services trust this data because it comes from an internal, secure source.</p>
</li>
</ul>
<p>So, the flow is exactly as you said:</p>
<ol>
<li><p><strong>Synchronous Authentication:</strong> Secure the entry points with JWT.</p>
</li>
<li><p><strong>Asynchronous Processing:</strong> Pass trusted data internally using message queues to handle background work.</p>
<p> Let's build out "Phase 2" step-by-step using your three services: <code>AuthService</code>, <code>OrderService</code>, and <code>NotificationService</code>.</p>
<p> We will show how the <code>OrderService</code> handles the synchronous request and then passes a job to the <code>NotificationService</code> asynchronously using BullMQ and Redis.</p>
<h3 id="heading-the-scenario">The Scenario</h3>
<ol>
<li><p><strong>AuthService:</strong> (Already built) User logs in and gets a JWT.</p>
</li>
<li><p><strong>OrderService (Producer):</strong> A user submits an order via a synchronous API call. This service validates the JWT, immediately tells the user "Order received," and then adds a <code>send-order-email</code> job to a queue.</p>
</li>
<li><p><strong>NotificationService (Worker):</strong> This is a background service that does one thing: it listens for jobs in the queue. When it gets the <code>send-order-email</code> job, it processes it (simulating sending an email).</p>
</li>
</ol>
</li>
</ol>
<h3 id="heading-prerequisites">Prerequisites</h3>
<ul>
<li><p><strong>Redis:</strong> You need a Redis server running. The easiest way is with Docker: <code>docker run -p 6379:6379 -d redis</code></p>
</li>
<li><p><strong>Packages:</strong> In your <code>OrderService</code> and <code>NotificationService</code>, you'll need to install a few packages: <code>npm install express jsonwebtoken dotenv bullmq ioredis</code></p>
</li>
</ul>
<hr />
<h3 id="heading-step-1-the-shared-secret-amp-setup">Step 1: The Shared Secret &amp; Setup</h3>
<p>    Just like before, the <code>JWT_SECRET</code> in your <code>.env</code> file must be <strong>identical</strong> across all three services: <code>AuthService</code>, <code>OrderService</code>, and <code>NotificationService</code>.</p>
<p>    <strong>In all project folders, the</strong> <code>.env</code> file:</p>
<p>    The Redis host and port <strong>must be the same</strong> for every service that needs to communicate through that specific BullMQ queue.</p>
<pre><code class="lang-bash">    JWT_SECRET=<span class="hljs-string">"this-is-our-shared-secret-key-12345"</span>
    REDIS_HOST=<span class="hljs-string">"localhost"</span>
    REDIS_PORT=<span class="hljs-string">"6379"</span>
</code></pre>
<hr />
<h3 id="heading-step-2-the-orderservice-the-producer">Step 2: The <code>OrderService</code> (The Producer 🏭)</h3>
<p>    This service acts as the bridge. It handles the synchronous request and <em>produces</em> an asynchronous job.</p>
<p>    <code>order-service/index.js</code></p>
<pre><code class="lang-javascript">    <span class="hljs-built_in">require</span>(<span class="hljs-string">'dotenv'</span>).config();
    <span class="hljs-keyword">const</span> express = <span class="hljs-built_in">require</span>(<span class="hljs-string">'express'</span>);
    <span class="hljs-keyword">const</span> jwt = <span class="hljs-built_in">require</span>(<span class="hljs-string">'jsonwebtoken'</span>);
    <span class="hljs-keyword">const</span> { Queue } = <span class="hljs-built_in">require</span>(<span class="hljs-string">'bullmq'</span>);

    <span class="hljs-comment">// --- Setup ---</span>
    <span class="hljs-keyword">const</span> app = express();
    app.use(express.json());

    <span class="hljs-comment">// --- BullMQ Queue Setup ---</span>
    <span class="hljs-comment">// This connects to Redis and creates a queue named 'email-queue'</span>
    <span class="hljs-keyword">const</span> emailQueue = <span class="hljs-keyword">new</span> Queue(<span class="hljs-string">'email-queue'</span>, {
        <span class="hljs-attr">connection</span>: {
            <span class="hljs-attr">host</span>: process.env.REDIS_HOST,
            <span class="hljs-attr">port</span>: process.env.REDIS_PORT,
        },
    });

    <span class="hljs-comment">// --- Middleware (Copied from before) ---</span>
    <span class="hljs-keyword">const</span> verifyToken = <span class="hljs-function">(<span class="hljs-params">req, res, next</span>) =&gt;</span> {
        <span class="hljs-keyword">const</span> authHeader = req.headers[<span class="hljs-string">'authorization'</span>];
        <span class="hljs-keyword">const</span> token = authHeader &amp;&amp; authHeader.split(<span class="hljs-string">' '</span>)[<span class="hljs-number">1</span>];
        <span class="hljs-keyword">if</span> (!token) <span class="hljs-keyword">return</span> res.status(<span class="hljs-number">401</span>).send(<span class="hljs-string">'Access Denied'</span>);
        <span class="hljs-keyword">try</span> {
            <span class="hljs-keyword">const</span> verifiedPayload = jwt.verify(token, process.env.JWT_SECRET);
            req.user = verifiedPayload; <span class="hljs-comment">// Attach { userId, role } to request</span>
            next();
        } <span class="hljs-keyword">catch</span> (err) {
            res.status(<span class="hljs-number">403</span>).send(<span class="hljs-string">'Invalid Token'</span>);
        }
    };

    <span class="hljs-comment">// --- API Endpoint ---</span>
    <span class="hljs-comment">// This is the synchronous "Front Door" for creating an order</span>
    app.post(<span class="hljs-string">'/create-order'</span>, verifyToken, <span class="hljs-keyword">async</span> (req, res) =&gt; {
        <span class="hljs-keyword">const</span> { orderDetails } = req.body;
        <span class="hljs-keyword">const</span> { userId } = req.user; <span class="hljs-comment">// Get userId from the verified JWT</span>

        <span class="hljs-built_in">console</span>.log(<span class="hljs-string">`Order received for user: <span class="hljs-subst">${userId}</span>`</span>);

        <span class="hljs-comment">// 1. Give IMMEDIATE feedback to the user</span>
        res.status(<span class="hljs-number">200</span>).json({ <span class="hljs-attr">message</span>: <span class="hljs-string">'Order received! A confirmation email will be sent shortly.'</span> });

        <span class="hljs-comment">// 2. Add the background job to the queue (The Asynchronous Part)</span>
        <span class="hljs-keyword">await</span> emailQueue.add(<span class="hljs-string">'send-order-email'</span>, {
            <span class="hljs-attr">userId</span>: userId,
            <span class="hljs-attr">orderDetails</span>: orderDetails,
        });
        <span class="hljs-built_in">console</span>.log(<span class="hljs-string">`Job added to queue for user: <span class="hljs-subst">${userId}</span>`</span>);
    });

    app.listen(<span class="hljs-number">6000</span>, <span class="hljs-function">() =&gt;</span> <span class="hljs-built_in">console</span>.log(<span class="hljs-string">'Order Service running on port 6000'</span>));
</code></pre>
<hr />
<h3 id="heading-step-3-the-notificationservice-the-worker">Step 3: The <code>NotificationService</code> (The Worker 👷)</h3>
<p>    This service is a background worker. It doesn't have any API endpoints. Its only job is to connect to the queue and process jobs.</p>
<p>    <code>notification-service/index.js</code></p>
<pre><code class="lang-javascript">    <span class="hljs-built_in">require</span>(<span class="hljs-string">'dotenv'</span>).config();
    <span class="hljs-keyword">const</span> { Worker } = <span class="hljs-built_in">require</span>(<span class="hljs-string">'bullmq'</span>);

    <span class="hljs-built_in">console</span>.log(<span class="hljs-string">'Notification Worker is starting...'</span>);

    <span class="hljs-comment">// --- BullMQ Worker Setup ---</span>
    <span class="hljs-comment">// The worker connects to the SAME queue and processes jobs</span>
    <span class="hljs-keyword">const</span> worker = <span class="hljs-keyword">new</span> Worker(<span class="hljs-string">'email-queue'</span>, <span class="hljs-keyword">async</span> job =&gt; {
        <span class="hljs-comment">// This is the processor function. It runs for every job.</span>
        <span class="hljs-built_in">console</span>.log(<span class="hljs-string">`\nProcessing job: <span class="hljs-subst">${job.name}</span> (ID: <span class="hljs-subst">${job.id}</span>)`</span>);

        <span class="hljs-comment">// Extract the trusted data passed from the OrderService</span>
        <span class="hljs-keyword">const</span> { userId, orderDetails } = job.data;
        <span class="hljs-built_in">console</span>.log(<span class="hljs-string">`Data received: User ID -&gt; <span class="hljs-subst">${userId}</span>`</span>);

        <span class="hljs-comment">// Simulate sending an email</span>
        <span class="hljs-built_in">console</span>.log(<span class="hljs-string">`Simulating: Sending order confirmation email to user <span class="hljs-subst">${userId}</span>...`</span>);
        <span class="hljs-comment">// In a real app, you would use a service like Nodemailer or SendGrid here</span>
        <span class="hljs-keyword">await</span> <span class="hljs-keyword">new</span> <span class="hljs-built_in">Promise</span>(<span class="hljs-function"><span class="hljs-params">resolve</span> =&gt;</span> <span class="hljs-built_in">setTimeout</span>(resolve, <span class="hljs-number">3000</span>)); <span class="hljs-comment">// Simulate a 3-second task</span>
        <span class="hljs-built_in">console</span>.log(<span class="hljs-string">`Email sent successfully to user <span class="hljs-subst">${userId}</span>!`</span>);

    }, {
        <span class="hljs-attr">connection</span>: {
            <span class="hljs-attr">host</span>: process.env.REDIS_HOST,
            <span class="hljs-attr">port</span>: process.env.REDIS_PORT,
        },
    });

    worker.on(<span class="hljs-string">'completed'</span>, <span class="hljs-function"><span class="hljs-params">job</span> =&gt;</span> {
      <span class="hljs-built_in">console</span>.log(<span class="hljs-string">`Job <span class="hljs-subst">${job.id}</span> has completed!`</span>);
    });

    worker.on(<span class="hljs-string">'failed'</span>, <span class="hljs-function">(<span class="hljs-params">job, err</span>) =&gt;</span> {
      <span class="hljs-built_in">console</span>.log(<span class="hljs-string">`Job <span class="hljs-subst">${job.id}</span> has failed with <span class="hljs-subst">${err.message}</span>`</span>);
    });
</code></pre>
<hr />
<h3 id="heading-how-to-run-and-see-the-full-flow">How to Run and See the Full Flow</h3>
<ol>
<li><p><strong>Start Redis:</strong> <code>docker run -p 6379:6379 -d redis</code></p>
</li>
<li><p><strong>Start all three services</strong> in separate terminal windows:</p>
<ul>
<li><p><code>node auth-service/index.js</code></p>
</li>
<li><p><code>node order-service/index.js</code></p>
</li>
<li><p><code>node notification-service/index.js</code> (You'll see "Notification Worker is starting...")</p>
</li>
</ul>
</li>
<li><p><strong>Simulate the Client:</strong></p>
<ul>
<li><p><strong>First, get a token</strong> (using <code>curl</code> or Postman):</p>
<p>  Bash</p>
<pre><code class="lang-javascript">  curl -X POST -H <span class="hljs-string">"Content-Type: application/json"</span> -d <span class="hljs-string">'{"username":"test", "password":"password"}'</span> http:<span class="hljs-comment">//localhost:4000/login</span>
</code></pre>
<p>  This will return a token. Copy it.</p>
</li>
<li><p><strong>Second, create an order:</strong> Replace <code>YOUR_TOKEN_HERE</code> with the token you just copied.</p>
<p>  Bash</p>
<pre><code class="lang-javascript">  curl -X POST -H <span class="hljs-string">"Content-Type: application/json"</span> -H <span class="hljs-string">"Authorization: Bearer YOUR_TOKEN_HERE"</span> -d <span class="hljs-string">'{"orderDetails": "1x Super Widget"}'</span> http:<span class="hljs-comment">//localhost:6000/create-order</span>
</code></pre>
</li>
</ul>
</li>
</ol>
<p>    <strong>What you will see:</strong></p>
<ol>
<li><p>The <code>curl</code> command for creating an order will <strong>immediately</strong> return: <code>"Order received! A confirmation email will be sent shortly."</code></p>
</li>
<li><p>In the <strong>OrderService</strong> terminal, you will see logs like:</p>
<ul>
<li><p><code>Order received for user: user123</code></p>
</li>
<li><p><code>Job added to queue for user: user123</code></p>
</li>
</ul>
</li>
<li><p>A moment later, in the <strong>NotificationService</strong> terminal, you will see the worker spring to life:</p>
<ul>
<li><p><code>Processing job: send-order-email...</code></p>
</li>
<li><p><code>Data received: User ID -&gt; user123</code></p>
</li>
<li><p><code>Simulating: Sending order confirmation email...</code></p>
</li>
<li><p>(after 3 seconds)</p>
</li>
<li><p><code>Email sent successfully to user user123!</code></p>
</li>
<li><p><code>Job ... has completed!</code></p>
</li>
</ul>
</li>
</ol>
<p>    This demonstrates the complete flow: a synchronous, authenticated request triggers a completely separate, asynchronous background task.</p>
]]></content:encoded></item></channel></rss>