<?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[Backend Journey]]></title><description><![CDATA[Backend Journey]]></description><link>https://ankitkrsinghbackend.hashnode.dev</link><generator>RSS for Node</generator><lastBuildDate>Fri, 04 Sep 2026 16:10:37 GMT</lastBuildDate><atom:link href="https://ankitkrsinghbackend.hashnode.dev/rss.xml" rel="self" type="application/rss+xml"/><language><![CDATA[en]]></language><ttl>60</ttl><item><title><![CDATA[How Express.js Handles Requests Behind the Scenes]]></title><description><![CDATA[While revising Express.js, I stumbled upon a simple question that I had never really thought about before:

What actually happens after a request hits an Express server?

I've used app.use(), app.get(]]></description><link>https://ankitkrsinghbackend.hashnode.dev/how-express-js-handles-requests-behind-the-scenes</link><guid isPermaLink="true">https://ankitkrsinghbackend.hashnode.dev/how-express-js-handles-requests-behind-the-scenes</guid><category><![CDATA[backend]]></category><dc:creator><![CDATA[Ankit kumar Singh]]></dc:creator><pubDate>Thu, 06 Aug 2026 20:13:48 GMT</pubDate><enclosure url="https://cdn.hashnode.com/uploads/covers/68eff4346be7ee72eabc4643/e64f276c-0932-41a9-8ff2-7d02eaf18a8f.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>While revising Express.js, I stumbled upon a simple question that I had never really thought about before:</p>
<blockquote>
<p><strong>What actually happens after a request hits an Express server?</strong></p>
</blockquote>
<p>I've used <code>app.use()</code>, <code>app.get()</code>, middleware, and <code>next()</code> in countless projects. I knew <em>how</em> to use them, but not exactly <em>how they worked together behind the scenes</em>.</p>
<p>At first, I assumed Express somehow "magically" knew which route to execute.</p>
<p>But the more I explored, the more I realized there isn't any magic.</p>
<p>Express follows a surprisingly simple process:</p>
<ul>
<li><p>It receives the request from Node.js.</p>
</li>
<li><p>It walks through a stack of middleware and routes.</p>
</li>
<li><p>Each middleware decides whether to pass the request to the next layer using <code>next()</code>.</p>
</li>
<li><p>Once a matching route sends a response, the request lifecycle ends.</p>
</li>
</ul>
<p>Understanding this completely changed how I think about middleware, routing, and debugging Express applications.</p>
<p>In this blog, we'll trace the complete journey of a request—from the moment it reaches the Node.js HTTP server to the moment a response is sent back to the client. Along the way, we'll uncover how Express internally handles middleware, route matching, and the request-response lifecycle.</p>
<p>Let's dive in. 🚀</p>
<hr />
<h2>Express.js Is Built on Top of Node.js</h2>
<p>Before we understand how Express handles requests, we need to answer one important question:</p>
<p><strong>Who receives the HTTP request first—Node.js or Express?</strong></p>
<p>The answer is <strong>Node.js</strong>.</p>
<p>Express doesn't create its own web server. Instead, it uses Node.js's built-in <code>http</code> module under the hood.</p>
<p>When we write:</p>
<pre><code class="language-js">const express = require("express");

const app = express();

app.listen(3000);
</code></pre>
<p>It looks like Express is creating the server for us.</p>
<p>But internally, it's conceptually doing something similar to this:</p>
<pre><code class="language-js">const http = require("http");

const server = http.createServer(app);

server.listen(3000);
</code></pre>
<p>The important thing to notice here is that <code>app</code> <strong>is actually a function</strong>.</p>
<p>Whenever a client sends a request, Node.js creates two objects:</p>
<ul>
<li><p><code>req</code> → Contains all information about the incoming request.</p>
</li>
<li><p><code>res</code> → Used to send a response back to the client.</p>
</li>
</ul>
<p>Node.js then simply calls the Express application like this:</p>
<pre><code class="language-js">app(req, res);
</code></pre>
<p>At this point, Express takes control of the request.</p>
<p>So the flow looks like this:</p>
<pre><code class="language-text">Browser
   │
HTTP Request
   │
   ▼
Node.js HTTP Server
   │
Creates req &amp; res
   │
   ▼
app(req, res)
   │
   ▼
Express starts processing the request
</code></pre>
<p>This is the first important concept to remember:</p>
<blockquote>
<p><strong>Node.js receives every HTTP request first. Express simply takes that request and decides which middleware and route handler should process it.</strong></p>
</blockquote>
<hr />
<h2>How Express Stores Middleware and Routes</h2>
<p>Now that we know <strong>Node.js passes every request to Express</strong>, the next question is:</p>
<blockquote>
<p><strong>How does Express know which middleware or route to execute?</strong></p>
</blockquote>
<p>The answer is surprisingly simple.</p>
<p>Whenever you register a middleware or a route, Express <strong>doesn't execute it immediately</strong>. Instead, it stores it in an internal stack (or list).</p>
<p>For example:</p>
<pre><code class="language-js">app.use(logger);

app.use(auth);

app.get("/users", getUsers);

app.post("/users", createUser);
</code></pre>
<p>Nothing runs when this code is executed.</p>
<p>Express simply remembers these registrations in the order they were added.</p>
<p>Conceptually, you can think of it like this:</p>
<pre><code class="language-text">Express Router Stack

1. logger middleware
2. auth middleware
3. GET /users
4. POST /users
</code></pre>
<p>This order is very important.</p>
<p>Express always starts from the <strong>top of the stack</strong> and moves downward until it finds a matching middleware or route.</p>
<p>Imagine a client sends this request:</p>
<pre><code class="language-http">GET /users
</code></pre>
<p>Express starts scanning its stack:</p>
<pre><code class="language-text">Incoming Request

        │
        ▼
1. logger
        │
        ▼
2. auth
        │
        ▼
3. GET /users
        │
        ▼
4. POST /users
</code></pre>
<p>Let's see what happens:</p>
<ul>
<li><p><strong>logger</strong> matches → execute it.</p>
</li>
<li><p>It calls <code>next()</code> → move to the next layer.</p>
</li>
<li><p><strong>auth</strong> matches → execute it.</p>
</li>
<li><p>It calls <code>next()</code> → continue.</p>
</li>
<li><p><strong>GET /users</strong> matches both the path and HTTP method → execute the route handler.</p>
</li>
<li><p>The handler sends a response.</p>
</li>
<li><p>Request processing ends.</p>
</li>
</ul>
<p>Notice something interesting.</p>
<p>Express isn't searching your files or folders.</p>
<p>It isn't looking for a function named <code>getUsers</code>.</p>
<p>It simply walks through the stack <strong>one layer at a time</strong>, in the exact order you registered everything.</p>
<p>That's why the order of middleware and routes matters so much.</p>
<p>For example:</p>
<pre><code class="language-js">app.use(logger);
app.get("/users", getUsers);
</code></pre>
<p>The logger runs <strong>before</strong> the route.</p>
<p>But if you accidentally reverse them:</p>
<pre><code class="language-js">app.get("/users", getUsers);

app.use(logger);
</code></pre>
<p>The route may send the response before the logger ever gets a chance to run.</p>
<p>This is why you'll often see middleware registered near the top of an Express application and routes defined afterwards.</p>
<p>In simple terms:</p>
<blockquote>
<p><strong>Express works like a person reading a checklist from top to bottom. For every incoming request, it checks each registered middleware and route in order until the request is handled.</strong></p>
</blockquote>
<hr />
<h2>Understanding <code>next()</code> — The Heart of Express Middleware</h2>
<p>Now we know that Express walks through its internal stack one layer at a time.</p>
<p>But here's the next question:</p>
<blockquote>
<p><strong>How does Express know when to move from one middleware to the next?</strong></p>
</blockquote>
<p>The answer is a single function:</p>
<pre><code class="language-js">next()
</code></pre>
<p>Think of <code>next()</code> as saying:</p>
<blockquote>
<p><strong>"I'm done with my work. Please continue to the next matching middleware or route."</strong></p>
</blockquote>
<p>Let's look at a simple middleware:</p>
<pre><code class="language-js">function logger(req, res, next) {
    console.log(`${req.method} ${req.url}`);

    next();
}
</code></pre>
<p>When a request comes in:</p>
<pre><code class="language-http">GET /users
</code></pre>
<p>Here's what happens:</p>
<ol>
<li><p>Express executes the <code>logger</code> middleware.</p>
</li>
<li><p>It prints:</p>
</li>
</ol>
<pre><code class="language-text">GET /users
</code></pre>
<ol>
<li>Then it reaches:</li>
</ol>
<pre><code class="language-js">next();
</code></pre>
<p>At this point, Express moves to the <strong>next layer</strong> in its stack.</p>
<p>The flow looks like this:</p>
<pre><code class="language-text">Request
   │
   ▼
Logger Middleware
   │
 next()
   ▼
Auth Middleware
   │
 next()
   ▼
Route Handler
</code></pre>
<h3>What if we don't call <code>next()</code>?</h3>
<p>Suppose our middleware looks like this:</p>
<pre><code class="language-js">function logger(req, res, next) {
    console.log(`${req.method} ${req.url}`);
}
</code></pre>
<p>Notice that we never call <code>next()</code>.</p>
<p>Now when a request arrives:</p>
<pre><code class="language-text">Request
   │
   ▼
Logger Middleware
   │
   ✖
</code></pre>
<p>Express has no idea what to do next.</p>
<p>It doesn't automatically move to the next middleware.</p>
<p>It simply waits.</p>
<p>From the browser's perspective, the request keeps loading until it eventually times out.</p>
<p>That's why every middleware should do <strong>one of these two things</strong>:</p>
<ol>
<li>Pass control to the next layer:</li>
</ol>
<pre><code class="language-js">next();
</code></pre>
<ol>
<li>End the request by sending a response:</li>
</ol>
<pre><code class="language-js">res.send("Done");
</code></pre>
<p>or</p>
<pre><code class="language-js">res.json(data);
</code></pre>
<p>Once a response is sent, the request-response cycle is complete, and Express stops processing any further layers.</p>
<h3>A Real Example: Authentication Middleware</h3>
<p>Authentication middleware is one of the best examples of how <code>next()</code> works.</p>
<pre><code class="language-js">function auth(req, res, next) {
    const token = req.headers.authorization;

    if (!token) {
        return res.status(401).send("Unauthorized");
    }

    next();
}
</code></pre>
<p>If the user is <strong>not authenticated</strong>:</p>
<pre><code class="language-text">Request
   │
   ▼
Logger ✅
   │
   ▼
Auth ❌
   │
401 Unauthorized
</code></pre>
<p>The route handler is <strong>never executed</strong>.</p>
<p>If the user <strong>is authenticated</strong>:</p>
<pre><code class="language-text">Request
   │
   ▼
Logger ✅
   │
   ▼
Auth ✅
   │
 next()
   ▼
Route Handler ✅
   │
   ▼
Response
</code></pre>
<h3>Key Takeaway</h3>
<p><code>next()</code> is what connects every middleware together.</p>
<p>Without it, Express cannot continue to the next layer.</p>
<p>You can think of middleware as a relay race—each runner must pass the baton (<code>next()</code>) to the next runner. If someone doesn't pass the baton, the race ends there.</p>
<hr />
<h2>How Express Matches Routes</h2>
<p>Now that we understand how <code>next()</code> moves a request through the middleware stack, let's answer another important question:</p>
<blockquote>
<p><strong>How does Express know which middleware or route matches the incoming request?</strong></p>
</blockquote>
<p>The answer depends on <strong>what you registered</strong>.</p>
<p>When Express reaches a layer in its stack, it checks two things:</p>
<ol>
<li><p><strong>Does the path match?</strong></p>
</li>
<li><p><strong>Does the HTTP method match?</strong> (Only for routes like <code>app.get()</code>, <code>app.post()</code>, etc.)</p>
</li>
</ol>
<p>Let's understand this with examples.</p>
<hr />
<h2><code>app.use()</code> — Prefix Matching</h2>
<p>Suppose you register a middleware like this:</p>
<pre><code class="language-js">app.use("/users", logger);
</code></pre>
<p>Now imagine these requests arrive:</p>
<pre><code class="language-text">GET /users
GET /users/1
POST /users
PUT /users/profile
</code></pre>
<p>Will the middleware run?</p>
<p>✅ Yes, for all of them.</p>
<p>Why?</p>
<p>Because <code>app.use()</code> performs <strong>prefix matching</strong>.</p>
<p>As long as the request path <strong>starts with</strong> <code>/users</code>, the middleware is executed.</p>
<p>You can think of it like this:</p>
<pre><code class="language-text">/users
   │
   ├── /users
   ├── /users/1
   ├── /users/profile
   └── /users/settings
</code></pre>
<p>Every request under <code>/users</code> passes through this middleware.</p>
<p>This is why <code>app.use()</code> is commonly used for:</p>
<ul>
<li><p>Authentication</p>
</li>
<li><p>Logging</p>
</li>
<li><p>Request Validation</p>
</li>
<li><p>CORS</p>
</li>
<li><p>Rate Limiting</p>
</li>
</ul>
<p>These are concerns that apply to multiple routes, not just one.</p>
<hr />
<h2><code>app.get()</code> — Exact Route + HTTP Method</h2>
<p>Now let's register a route:</p>
<pre><code class="language-js">app.get("/users", getUsers);
</code></pre>
<p>When a request comes in:</p>
<pre><code class="language-http">GET /users
</code></pre>
<p>Express checks:</p>
<ul>
<li><p>Path = <code>/users</code> ✅</p>
</li>
<li><p>Method = <code>GET</code> ✅</p>
</li>
</ul>
<p>Both match, so the route handler runs.</p>
<p>Now consider:</p>
<pre><code class="language-http">GET /users/1
</code></pre>
<p>Path doesn't match exactly.</p>
<p>So Express skips this route and continues searching.</p>
<p>Similarly:</p>
<pre><code class="language-http">POST /users
</code></pre>
<p>The path matches, but the HTTP method is different.</p>
<p>Again, this route is skipped.</p>
<hr />
<h2><code>app.all()</code> — Exact Route, Any HTTP Method</h2>
<p>Now suppose we write:</p>
<pre><code class="language-js">app.all("/users", handler);
</code></pre>
<p>Unlike <code>app.get()</code>, Express doesn't care about the HTTP method.</p>
<p>These requests will all match:</p>
<pre><code class="language-text">GET /users
POST /users
PUT /users
DELETE /users
</code></pre>
<p>However:</p>
<pre><code class="language-text">GET /users/1
</code></pre>
<p>❌ This still won't match because <code>app.all()</code> also performs <strong>exact path matching</strong>.</p>
<p>The only difference is that it accepts <strong>every HTTP method</strong>.</p>
<hr />
<h2>Visualizing the Difference</h2>
<p>Imagine these registrations:</p>
<pre><code class="language-js">app.use("/users", logger);

app.get("/users", getUsers);

app.all("/admin", adminHandler);
</code></pre>
<p>Now look at how different requests are handled:</p>
<table>
<thead>
<tr>
<th>Incoming Request</th>
<th><code>app.use("/users")</code></th>
<th><code>app.get("/users")</code></th>
<th><code>app.all("/admin")</code></th>
</tr>
</thead>
<tbody><tr>
<td><code>GET /users</code></td>
<td>✅</td>
<td>✅</td>
<td>❌</td>
</tr>
<tr>
<td><code>POST /users</code></td>
<td>✅</td>
<td>❌</td>
<td>❌</td>
</tr>
<tr>
<td><code>GET /users/1</code></td>
<td>✅</td>
<td>❌</td>
<td>❌</td>
</tr>
<tr>
<td><code>DELETE /admin</code></td>
<td>❌</td>
<td>❌</td>
<td>✅</td>
</tr>
<tr>
<td><code>GET /admin</code></td>
<td>❌</td>
<td>❌</td>
<td>✅</td>
</tr>
</tbody></table>
<hr />
<h2>Why This Design?</h2>
<p>Think about an authentication middleware.</p>
<p>You don't want to write:</p>
<pre><code class="language-js">app.get("/users", auth);

app.post("/users", auth);

app.put("/users", auth);

app.delete("/users", auth);
</code></pre>
<p>Instead, you simply write:</p>
<pre><code class="language-js">app.use("/users", auth);
</code></pre>
<p>Now every request under <code>/users</code> is automatically protected.</p>
<p>That's the power of middleware.</p>
<hr />
<h2>Key Takeaway</h2>
<p>Express doesn't magically know which function to execute.</p>
<p>For every incoming request, it walks through its stack and checks:</p>
<ul>
<li><p>Does the path match?</p>
</li>
<li><p>If it's a route, does the HTTP method match?</p>
</li>
</ul>
<p>If the answer is yes, Express executes that layer.</p>
<p>If not, it simply moves to the next one.</p>
<p>This simple matching mechanism is what powers routing in Express.</p>
<hr />
<h2>How Express Walks Through the Stack</h2>
<p>At this point, you might be wondering:</p>
<blockquote>
<p><strong>How does Express know which middleware to execute next?</strong></p>
</blockquote>
<p>Internally, Express maintains an <strong>index (or pointer)</strong> while traversing its router stack.</p>
<p>Imagine you registered the following:</p>
<pre><code class="language-js">app.use(logger);

app.use(auth);

app.get("/users", getUsers);

app.post("/users", createUser);
</code></pre>
<p>Express stores them in order:</p>
<pre><code class="language-text">Index   Layer

0       logger
1       auth
2       GET /users
3       POST /users
</code></pre>
<p>Now suppose a request arrives:</p>
<pre><code class="language-http">GET /users
</code></pre>
<p>Express starts with:</p>
<pre><code class="language-text">currentIndex = 0
</code></pre>
<p>It executes the first layer:</p>
<pre><code class="language-text">0 → logger
</code></pre>
<p>Inside the middleware:</p>
<pre><code class="language-js">function logger(req, res, next) {
    console.log("Logging request...");
    next();
}
</code></pre>
<p>When <code>next()</code> is called, Express simply increments its internal pointer.</p>
<pre><code class="language-text">currentIndex = 1
</code></pre>
<p>Now it executes:</p>
<pre><code class="language-text">1 → auth
</code></pre>
<p>If authentication succeeds:</p>
<pre><code class="language-js">next();
</code></pre>
<p>Express moves again:</p>
<pre><code class="language-text">currentIndex = 2
</code></pre>
<p>Now it reaches:</p>
<pre><code class="language-text">2 → GET /users
</code></pre>
<p>The route matches, so the handler executes and sends the response.</p>
<p>The journey looks like this:</p>
<pre><code class="language-text">currentIndex = 0
        │
        ▼
Logger
        │ next()
        ▼
currentIndex = 1
        │
        ▼
Auth
        │ next()
        ▼
currentIndex = 2
        │
        ▼
GET /users
        │
        ▼
Response
</code></pre>
<p>This also explains <strong>why the order of middleware and routes matters</strong>.</p>
<p>For example:</p>
<pre><code class="language-js">app.use(logger);

app.use(auth);

app.get("/users", getUsers);
</code></pre>
<p>The request always passes through:</p>
<pre><code class="language-text">Logger
   ↓
Auth
   ↓
Route
</code></pre>
<p>But if you accidentally write:</p>
<pre><code class="language-js">app.get("/users", getUsers);

app.use(logger);
</code></pre>
<p>The route handler sends the response before Express ever reaches the logger.</p>
<pre><code class="language-text">Route
   │
Response Sent
   │
Express Stops
</code></pre>
<p>The logger never runs.</p>
<p>That's why you'll almost always see middleware registered <strong>before</strong> routes in Express applications.</p>
<p>The important takeaway is this:</p>
<blockquote>
<p><strong>Express doesn't jump around your code. It simply walks through its internal stack from index 0 onward, moving one layer at a time whenever</strong> <code>next()</code> <strong>is called.</strong></p>
</blockquote>
<hr />
<h2>Following a Complete Request Journey</h2>
<p>We've covered all the individual pieces:</p>
<ul>
<li><p>Node.js receives the request.</p>
</li>
<li><p>Express stores middleware and routes in a stack.</p>
</li>
<li><p><code>next()</code> moves from one layer to the next.</p>
</li>
<li><p>Express matches middleware and routes based on the path and HTTP method.</p>
</li>
</ul>
<p>Now let's put everything together and follow a <strong>single request</strong> from start to finish.</p>
<p>Suppose we have the following Express application:</p>
<pre><code class="language-js">const express = require("express");

const app = express();

app.use(logger);

app.use(auth);

app.get("/users", getUsers);

app.listen(3000);
</code></pre>
<p>A client sends this request:</p>
<pre><code class="language-http">GET /users
</code></pre>
<p>Let's see what happens behind the scenes.</p>
<hr />
<h2>Step 1: Browser Sends the Request</h2>
<p>Everything starts with the client.</p>
<pre><code class="language-text">Browser
   │
GET /users
   │
   ▼
Node.js HTTP Server
</code></pre>
<p>The browser doesn't talk directly to Express.</p>
<p>It sends an HTTP request to the <strong>Node.js HTTP server</strong>.</p>
<hr />
<h2>Step 2: Node.js Creates <code>req</code> and <code>res</code></h2>
<p>When Node.js receives the request, it creates two objects:</p>
<ul>
<li><p><code>req</code> → Contains information about the incoming request.</p>
</li>
<li><p><code>res</code> → Used to send the response back to the client.</p>
</li>
</ul>
<p>Then Node simply calls the Express application:</p>
<pre><code class="language-js">app(req, res);
</code></pre>
<p>At this moment, Express takes over.</p>
<hr />
<h2>Step 3: Express Starts Walking Through Its Stack</h2>
<p>Internally, Express has already stored everything in the order you registered it.</p>
<pre><code class="language-text">1. logger
2. auth
3. GET /users
</code></pre>
<p>Express starts from the top.</p>
<pre><code class="language-text">Request
   │
   ▼
logger
</code></pre>
<hr />
<h2>Step 4: Logger Middleware Executes</h2>
<pre><code class="language-js">function logger(req, res, next) {
    console.log(`${req.method} ${req.url}`);
    next();
}
</code></pre>
<p>Output:</p>
<pre><code class="language-text">GET /users
</code></pre>
<p>Then:</p>
<pre><code class="language-js">next();
</code></pre>
<p>Express moves to the next layer.</p>
<hr />
<h2>Step 5: Authentication Middleware Executes</h2>
<pre><code class="language-js">function auth(req, res, next) {
    if (!req.headers.authorization) {
        return res.status(401).send("Unauthorized");
    }

    next();
}
</code></pre>
<p>Two things can happen here.</p>
<h3>Case 1: User is not authenticated</h3>
<pre><code class="language-text">Request
   │
Logger ✅
   │
Auth ❌
   │
401 Unauthorized
</code></pre>
<p>The request ends here.</p>
<p>The route handler is never executed.</p>
<hr />
<h3>Case 2: User is authenticated</h3>
<p>Authentication succeeds.</p>
<pre><code class="language-js">next();
</code></pre>
<p>Express continues searching.</p>
<hr />
<h2>Step 6: Route Matching</h2>
<p>The next layer is:</p>
<pre><code class="language-js">app.get("/users", getUsers);
</code></pre>
<p>Express checks:</p>
<ul>
<li><p>Is the request path <code>/users</code>?</p>
</li>
<li><p>Is the HTTP method <code>GET</code>?</p>
</li>
</ul>
<p>Both are true.</p>
<p>So it executes:</p>
<pre><code class="language-js">getUsers(req, res);
</code></pre>
<hr />
<h2>Step 7: Route Handler Executes</h2>
<pre><code class="language-js">function getUsers(req, res) {
    res.json(users);
}
</code></pre>
<p>The handler performs the business logic and sends a response.</p>
<p>Once a response is sent, Express stops processing any more layers.</p>
<hr />
<h2>Step 8: Node.js Sends the Response</h2>
<p>Express hands the response back to Node.js.</p>
<p>Node.js writes the HTTP response to the network.</p>
<p>Finally, the browser receives it.</p>
<pre><code class="language-text">Browser
   ▲
HTTP Response
   ▲
Node.js
   ▲
Express
   ▲
Route Handler
   ▲
Authentication Middleware
   ▲
Logger Middleware
</code></pre>
<p>The request-response cycle is complete.</p>
<hr />
<h2>The Entire Journey</h2>
<p>Here's the complete flow in one diagram:</p>
<pre><code class="language-text">                 GET /users

Browser
   │
   ▼
Node.js HTTP Server
   │
Creates req &amp; res
   │
   ▼
Express App
   │
   ▼
Logger Middleware
   │
 next()
   ▼
Authentication Middleware
   │
 next()
   ▼
Route Handler
   │
res.json(users)
   ▼
Node.js HTTP Server
   │
HTTP Response
   ▼
Browser
</code></pre>
<hr />
<h2>What I Learned</h2>
<p>Before revising Express internals, I thought routing and middleware were handled by some kind of magic.</p>
<p>But after digging deeper, I realized the entire request lifecycle is surprisingly simple.</p>
<p>For every request:</p>
<ol>
<li><p>Node.js receives the HTTP request.</p>
</li>
<li><p>Express gets <code>req</code> and <code>res</code>.</p>
</li>
<li><p>Express walks through its middleware and route stack.</p>
</li>
<li><p>Each middleware either calls <code>next()</code> or ends the request.</p>
</li>
<li><p>A matching route executes.</p>
</li>
<li><p>A response is sent back to the client.</p>
</li>
</ol>
<p>That's it.</p>
<p>Once you understand this flow, concepts like middleware, <code>next()</code>, route matching, and even debugging Express applications become much easier because you can mentally trace where every request is in the pipeline.</p>
]]></content:encoded></item><item><title><![CDATA[Node.js Internals Explained: What Every Backend Developer Should Know]]></title><description><![CDATA[If you are learning backend development with Node.js, understanding how Node.js works internally is very important.
Many developers use Node.js every day, but they don’t clearly know what happens behi]]></description><link>https://ankitkrsinghbackend.hashnode.dev/node-js-internals-explained-what-every-backend-developer-should-know</link><guid isPermaLink="true">https://ankitkrsinghbackend.hashnode.dev/node-js-internals-explained-what-every-backend-developer-should-know</guid><category><![CDATA[ChaiCode]]></category><dc:creator><![CDATA[Ankit kumar Singh]]></dc:creator><pubDate>Sun, 15 Mar 2026 22:10:40 GMT</pubDate><enclosure url="https://cdn.hashnode.com/uploads/covers/68eff4346be7ee72eabc4643/8abbf9c6-18c1-4283-9b8b-719d14ba6b1e.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>If you are learning backend development with Node.js, understanding how Node.js works internally is very important.</p>
<p>Many developers use Node.js every day, but they don’t clearly know what happens <strong>behind the scenes when we run a simple command like:</strong></p>
<pre><code class="language-javascript">node index.js
</code></pre>
<p>In this article we will understand the <strong>core internals of Node.js</strong> in a simple and beginner-friendly way.</p>
<h2>Topics Covered</h2>
<ul>
<li><p>What is Node.js</p>
</li>
<li><p>V8 Engine and libuv</p>
</li>
<li><p>Node.js Architecture Overview</p>
</li>
<li><p>How Node.js Executes a Program (<code>node index.js</code>)</p>
</li>
<li><p>Thread Pool in Node.js</p>
</li>
<li><p>Event Loop and its Phases</p>
</li>
<li><p>setTimeout vs setImmediate</p>
</li>
<li><p>process.nextTick()</p>
</li>
<li><p>Worker Threads</p>
</li>
<li><p>Conclusion</p>
</li>
</ul>
<hr />
<h1>What is Node.js</h1>
<p>Originally JavaScript was designed to run inside browsers like Chrome.</p>
<pre><code class="language-javascript">JavaScript → Browser
</code></pre>
<p>In <strong>2009</strong>, Ryan Dahl created Node.js by combining the <strong>V8 JavaScript engine</strong> with a library called <strong>libuv</strong>.</p>
<p>This allowed JavaScript to run outside the browser, especially on servers.</p>
<pre><code class="language-javascript">JavaScript → Server
</code></pre>
<p>Node.js is mainly built using:</p>
<pre><code class="language-javascript">V8 Engine + libuv + C++
</code></pre>
<p>Node.js works using a <strong>single-threaded event-driven architecture</strong>, which helps it handle many requests efficiently.</p>
<hr />
<h1>V8 Engine and libuv</h1>
<h2>V8 Engine</h2>
<p>V8 is the JavaScript engine used in <strong>Google Chrome</strong>.</p>
<p>Its job is to convert JavaScript code into <strong>machine code</strong> so the computer can execute it.</p>
<p>Example:</p>
<pre><code class="language-javascript">console.log("Hello")
</code></pre>
<p>V8 compiles this code into machine instructions.</p>
<hr />
<h2>libuv</h2>
<p>libuv is a C library that provides asynchronous capabilities to Node.js.</p>
<p>It manages:</p>
<ul>
<li><p>Event Loop</p>
</li>
<li><p>Thread Pool</p>
</li>
<li><p>File system operations</p>
</li>
<li><p>Network requests</p>
</li>
<li><p>Timers</p>
</li>
</ul>
<p>The event loop and worker threads in Node.js are implemented using <strong>libuv</strong>.</p>
<hr />
<h2>Node.js Architecture Overview</h2>
<p>The Node.js runtime consists of multiple components working together.</p>
<p>Main components include:</p>
<ul>
<li><p><strong>V8 Engine</strong> → executes JavaScript</p>
</li>
<li><p><strong>Event Loop</strong> → manages asynchronous callbacks</p>
</li>
<li><p><strong>libuv Thread Pool</strong> → handles heavy tasks</p>
</li>
<li><p><strong>Node.js APIs</strong> → fs, http, crypto etc.</p>
</li>
</ul>
<p>JavaScript code runs on the <strong>main thread</strong>, while heavy work can be delegated to the <strong>thread pool</strong>.</p>
<hr />
<h2>How Node.js Executes a Program</h2>
<p>Suppose we have a file:</p>
<pre><code class="language-javascript">index.js
</code></pre>
<p>We run it using:</p>
<pre><code class="language-javascript">node index.js
</code></pre>
<p>When this command runs:</p>
<ol>
<li><p>Node.js creates a <strong>process</strong></p>
</li>
<li><p>Inside the process there is a <strong>main thread</strong></p>
</li>
<li><p>JavaScript code starts executing on that thread</p>
</li>
</ol>
<hr />
<h1>Top Level Code</h1>
<p>The main thread first runs <strong>top-level code</strong>.</p>
<p>Top-level code means code that runs immediately when the file starts.</p>
<p>Example:</p>
<pre><code class="language-javascript">console.log("Hello from Top Level Code")
const fs = require("fs")
</code></pre>
<p>While executing this code Node.js also:</p>
<ul>
<li><p>loads modules using <code>require()</code></p>
</li>
<li><p>registers callbacks</p>
</li>
<li><p>prepares resources for asynchronous tasks</p>
</li>
</ul>
<hr />
<h2>Thread Pool in Node.js</h2>
<p>Node.js uses a <strong>thread pool</strong> managed by libuv.</p>
<p>It is used for CPU intensive operations such as:</p>
<ul>
<li><p>cryptography</p>
</li>
<li><p>hashing</p>
</li>
<li><p>file system operations</p>
</li>
<li><p>DNS lookups</p>
</li>
</ul>
<p>By default the thread pool contains:</p>
<pre><code class="language-javascript">4 threads
</code></pre>
<p>This allows heavy tasks to run in parallel without blocking the main thread.</p>
<p>You can change the size using:</p>
<pre><code class="language-javascript">process.env.UV_THREADPOOL_SIZE = 10
</code></pre>
<hr />
<h2>Event Loop</h2>
<p>After the top-level code finishes executing, Node.js starts the <strong>Event Loop</strong>.</p>
<p>The event loop continuously checks if there are tasks waiting to be executed.</p>
<p>It allows Node.js to perform <strong>non-blocking I/O operations even though JavaScript runs on a single thread</strong>.</p>
<p>If a task requires heavy work:</p>
<ol>
<li><p>It is sent to the thread pool</p>
</li>
<li><p>The main thread continues running</p>
</li>
<li><p>Once the task finishes, its callback is added to the queue</p>
</li>
</ol>
<hr />
<h2>Event Loop Phases</h2>
<p>The event loop runs in phases.</p>
<p>Important phases include:</p>
<h3>Timers Phase</h3>
<p>Runs callbacks from:</p>
<pre><code class="language-javascript">setTimeout()
setInterval()
</code></pre>
<h3>I/O Polling Phase</h3>
<p>Handles completed I/O operations such as:</p>
<ul>
<li><p>file reading</p>
</li>
<li><p>network requests</p>
</li>
</ul>
<h3>Check Phase</h3>
<p>Runs callbacks scheduled using:</p>
<pre><code class="language-plaintext">setImmediate()
</code></pre>
<h3>Close Callbacks Phase</h3>
<p>Handles events like socket closing.</p>
<hr />
<h2>Important Point: setTimeout vs setImmediate</h2>
<p>Example:</p>
<pre><code class="language-javascript">setTimeout(() =&gt; console.log("Hello from Timer 1"), 0)

setImmediate(() =&gt; console.log("Hello from Immediate Fn 1"))

console.log("Hello from Top Level Code")
</code></pre>
<p>Possible output:</p>
<pre><code class="language-javascript">Hello from Top Level Code
Hello from Timer 1
Hello from Immediate Fn 1
</code></pre>
<p>But sometimes:</p>
<pre><code class="language-javascript">Hello from Top Level Code
Hello from Immediate Fn 1
Hello from Timer 1
</code></pre>
<p>This happens because the execution order between <strong>setTimeout() and setImmediate() is non-deterministic</strong>.</p>
<p>It depends on the <strong>performance and timing of the Node.js process</strong>, so the order may change.</p>
<hr />
<h2>process.nextTick()</h2>
<p>Node.js also provides a special function:</p>
<pre><code class="language-javascript">process.nextTick()
</code></pre>
<p>This runs <strong>before the Event Loop continues to the next phase</strong>.</p>
<p>Example:</p>
<pre><code class="language-javascript">console.log("Start")

process.nextTick(() =&gt; {
  console.log("nextTick callback")
})

setTimeout(() =&gt; {
  console.log("timer callback")
}, 0)

console.log("End")
</code></pre>
<p>Output:</p>
<pre><code class="language-javascript">Start
End
nextTick callback
timer callback
</code></pre>
<p>Execution priority:</p>
<pre><code class="language-javascript">Top Level Code
↓
process.nextTick()
↓
Event Loop
</code></pre>
<hr />
<h2>Worker Threads</h2>
<p>Node.js also supports <strong>Worker Threads</strong>.</p>
<p>Worker threads allow JavaScript code to run in separate threads.</p>
<p>They are useful for:</p>
<ul>
<li><p>heavy computations</p>
</li>
<li><p>data processing</p>
</li>
<li><p>CPU intensive algorithms</p>
</li>
</ul>
<hr />
<p>Understand through visualization, I try my best to Explain this through diagram . how nodejs internals works</p>
<img src="https://cdn.hashnode.com/uploads/covers/68eff4346be7ee72eabc4643/0c8e8c90-5785-48ac-af20-3a7aa04c2652.png" alt="" style="display:block;margin:0 auto" />

<hr />
<h2>Conclusion</h2>
<p>In this article we learned how Node.js works internally.</p>
<p>Key takeaways:</p>
<ul>
<li><p>Node.js runs JavaScript using the <strong>V8 engine</strong></p>
</li>
<li><p><strong>libuv</strong> handles asynchronous operations</p>
</li>
<li><p>The <strong>Event Loop</strong> manages callbacks</p>
</li>
<li><p>Heavy tasks run in the <strong>thread pool</strong></p>
</li>
<li><p>Worker threads allow parallel processing</p>
</li>
</ul>
<p>Understanding these concepts helps you explain <strong>Node.js internals clearly in backend interviews</strong>.</p>
]]></content:encoded></item><item><title><![CDATA[Backend Engineering Series #1: Node.js Architecture Explained
]]></title><description><![CDATA[Welcome to the Backend Engineering Series.
In this series we will explore how backend systems actually work — from the basics of Node.js architecture to the internals of the event loop and asynchronou]]></description><link>https://ankitkrsinghbackend.hashnode.dev/backend-engineering-series-1-node-js-architecture-explained</link><guid isPermaLink="true">https://ankitkrsinghbackend.hashnode.dev/backend-engineering-series-1-node-js-architecture-explained</guid><dc:creator><![CDATA[Ankit kumar Singh]]></dc:creator><pubDate>Sun, 15 Mar 2026 06:31:29 GMT</pubDate><content:encoded><![CDATA[<p>Welcome to the Backend Engineering Series.</p>
<p>In this series we will explore how backend systems actually work — from the basics of Node.js architecture to the internals of the event loop and asynchronous programming.</p>
<p>In this first article, we will understand the architecture of Node.js and how it enables high-performance, non-blocking applications.</p>
<h2><strong>Node.js Architecture Explained (Super Easy Guide)</strong></h2>
<p>Before learning the <strong>Event Loop</strong>, it is very important to understand <strong>Node.js Architecture</strong>.</p>
<p>Because the Event Loop is just <strong>one part of Node.js</strong>.</p>
<p>If you understand the architecture, the rest of Node.js concepts become much easier.</p>
<p>So let's understand <strong>how Node.js works internally</strong>.</p>
<hr />
<h2><strong>1. JavaScript Cannot Run by Itself</strong></h2>
<p>JavaScript needs something called a <strong>JavaScript Engine</strong> to run.</p>
<p>Example engines:</p>
<table>
<thead>
<tr>
<th><strong>Platform</strong></th>
<th><strong>Engine</strong></th>
</tr>
</thead>
<tbody><tr>
<td>Chrome</td>
<td>V8</td>
</tr>
<tr>
<td>Firefox</td>
<td>SpiderMonkey</td>
</tr>
<tr>
<td>Node.js</td>
<td>V8</td>
</tr>
</tbody></table>
<p>Node.js uses <strong>Google's V8 Engine</strong>.</p>
<p>The V8 engine:</p>
<ul>
<li><p>reads JavaScript code</p>
</li>
<li><p>converts it into machine code</p>
</li>
<li><p>runs the program</p>
</li>
</ul>
<p>Example:</p>
<pre><code class="language-javascript">console.log("Hello Node.js")
</code></pre>
<p>When you run:</p>
<pre><code class="language-javascript">node app.js
</code></pre>
<p>the <strong>V8 engine executes this code</strong>.</p>
<hr />
<h2><strong>2. Node.js Is More Than Just V8</strong></h2>
<p>Node.js is not just the V8 engine.</p>
<p>It combines <strong>three main components</strong>:</p>
<pre><code class="language-javascript">V8 Engine
C++ bindings
libuv
</code></pre>
<p>Each part has a different job.</p>
<hr />
<h2><strong>3. C++ Bindings (Node APIs)</strong></h2>
<p>Node.js provides many built-in APIs like:</p>
<ul>
<li><p>fs (file system)</p>
</li>
<li><p>http (server)</p>
</li>
<li><p>crypto</p>
</li>
<li><p>timers</p>
</li>
</ul>
<p>Example:</p>
<pre><code class="language-javascript">const fs = require("fs")

fs.readFile("data.txt",(err,data)=&gt;{
 console.log(data)
})
</code></pre>
<p>When we call <code>fs.readFile()</code>, JavaScript actually calls <strong>C++ code inside Node.js</strong>, which talks to the <strong>operating system</strong>.</p>
<p>This is how Node.js interacts with your computer.</p>
<hr />
<h1><strong>4. libuv (The Most Important Part)</strong></h1>
<p>Node.js uses a library called <strong>libuv</strong>.</p>
<p>libuv handles things like:</p>
<ul>
<li><p>asynchronous operations</p>
</li>
<li><p>file system tasks</p>
</li>
<li><p>networking</p>
</li>
<li><p>event loop</p>
</li>
<li><p>thread pool</p>
</li>
</ul>
<p>libuv is the reason Node.js can handle <strong>many operations without blocking the program</strong>.</p>
<hr />
<h2><strong>5. Node.js Uses a Single Thread</strong></h2>
<p>Node.js runs JavaScript on <strong>one main thread</strong>.</p>
<p>This means JavaScript executes <strong>one task at a time</strong>.</p>
<p>Example:</p>
<pre><code class="language-javascript">console.log("Start")

for(let i=0;i&lt;1000000000;i++){}

console.log("End")
</code></pre>
<p>The loop blocks the program.</p>
<p>This is called <strong>blocking code</strong>.</p>
<p>Blocking code is bad for servers because it stops other users from being served.</p>
<hr />
<h2><strong>6. Non-Blocking Architecture</strong></h2>
<p>Node.js solves this problem using <strong>asynchronous programming</strong>.</p>
<p>Instead of waiting for slow tasks like:</p>
<ul>
<li><p>reading files</p>
</li>
<li><p>database queries</p>
</li>
<li><p>API calls</p>
</li>
</ul>
<p>Node.js sends these tasks to <strong>libuv</strong>.</p>
<p>While the task is running, Node.js continues doing other work.</p>
<p>When the task finishes, the result is returned as a <strong>callback</strong>.</p>
<p>Example:</p>
<pre><code class="language-javascript">fs.readFile("file.txt",(err,data)=&gt;{
 console.log(data)
})
</code></pre>
<p>Node.js does <strong>not wait</strong> for the file to finish reading.</p>
<hr />
<h2><strong>7. Thread Pool</strong></h2>
<p>Node.js also has something called a <strong>thread pool</strong>.</p>
<p>Default size:</p>
<pre><code class="language-plaintext">4 threads
</code></pre>
<p>These threads handle <strong>heavy tasks</strong> like:</p>
<ul>
<li><p>file system operations</p>
</li>
<li><p>cryptography</p>
</li>
<li><p>compression</p>
</li>
<li><p>DNS lookup</p>
</li>
</ul>
<p>Example:</p>
<pre><code class="language-javascript">const crypto = require("crypto")

crypto.pbkdf2("password","salt",100000,1024,"sha256",()=&gt;{
 console.log("Done")
})
</code></pre>
<p>This heavy operation runs in the <strong>thread pool</strong>, not in the main thread.</p>
<hr />
<h2><strong>8. How Node.js Handles a Request</strong></h2>
<p>Let's imagine a user sends a request to a Node.js server.</p>
<p>The flow looks like this:</p>
<pre><code class="language-plaintext">User Request
     ↓
Node.js Server
     ↓
JavaScript Code Runs
     ↓
Async Task Sent to libuv
     ↓
Thread Pool Handles Task
     ↓
Result Returned
     ↓
Callback Executes
</code></pre>
<p>Because Node.js does not block the main thread, it can handle <strong>many requests at the same time</strong>.</p>
<hr />
<h2><strong>9. Why Node.js Is Fast</strong></h2>
<p>Node.js is fast because it uses:</p>
<ul>
<li><p>single thread execution</p>
</li>
<li><p>non-blocking I/O</p>
</li>
<li><p>asynchronous architecture</p>
</li>
<li><p>thread pool for heavy tasks</p>
</li>
</ul>
<p>This allows Node.js to handle <strong>thousands of connections efficiently</strong>.</p>
<hr />
<h2><strong>CONCLUSION</strong></h2>
<p>Important things to remember:</p>
<ul>
<li><p>Node.js uses the <strong>V8 engine</strong> to run JavaScript</p>
</li>
<li><p>Node.js APIs are implemented using <strong>C++ bindings</strong></p>
</li>
<li><p><strong>libuv handles asynchronous operations</strong></p>
</li>
<li><p>Node.js uses a <strong>thread pool for heavy tasks</strong></p>
</li>
<li><p>Node.js follows a <strong>non-blocking architecture</strong></p>
</li>
</ul>
<p>Because of this design, Node.js can build <strong>fast and scalable backend applications</strong>.</p>
]]></content:encoded></item></channel></rss>