21 Jul, 2026 · 7 min read · Technical

What Actually Happens When You Run Node.js

I'm a frontend engineer working on becoming fullstack, and instead of rushing to write code in Node.js, I spent a week figuring out what Node is actually doing underneath. Here's what finally made the event loop click.

If you've checked out my home page, you already know I'm a frontend engineer right now, working on becoming a fullstack one. And like most people making that switch, my plan was the obvious one: learn how backend systems actually work before I try building something of my own with them.

The push to actually sit down and do it came from Instagram, of all places. An ad for Frontend Masters (they've since rebranded to master.dev) kept showing up on my feed, and I got pulled in. Part of it was timing, but a bigger part of it was something I'd noticed about myself before. When I was job switching, I paid for a course on neetcode.io, and having actual money on the line made me show up and study in a way that free resources never quite managed. So when master.dev showed up, I took the subscription without thinking about it too much.

They have a prebuilt learning path that starts with Node.js and ends with you as a fullstack developer. Since I already knew the frontend half reasonably well, I leaned into the Node section first, mostly because I wanted to actually understand what's happening under my code instead of just wiring up routes and hoping they work.

So this post is what I've learned so far about how Node actually works - not the API surface, but what's happening underneath when you run node app.js.

JavaScript alone can't do much

JavaScript, on its own, is just a language. It can add numbers, loop through arrays, and manipulate objects in memory. But it has no built-in way to read a file, open a network connection, or set a timer. Those are operating-system-level tasks, and JavaScript has no direct line to the OS.

This is where Node comes in. Node is not JavaScript. It's a runtime, and the actual engine running your JS code is V8, the same engine Chrome uses. V8 is written in C++, and it takes your JavaScript, compiles it, and executes it. But V8 alone still can't talk to your file system or your network card either.

So Node wraps V8 with a layer written in C++ that gives JavaScript access to things it couldn't touch before: files, sockets, timers, the equivalents of a DOM for a server environment. When you call fs.readFile() in your code, you're not calling a JavaScript function that reads files. You're calling a JavaScript wrapper around a C++ binding that eventually asks the operating system to go read something off disk.

So the flow, roughly, looks like this: your JS code runs on V8, V8 hands off any system-level work to Node's C++ layer, and that C++ layer talks to the actual machine - the OS kernel, which handles the disk, network, and other hardware. Once the OS finishes that work, the result gets passed back up through C++, back into V8, and your JavaScript callback runs with the result.

None of this happens instantly, and none of it happens in a straight line. Which brings me to the part that actually clicked things into place for me.

Why the event loop exists at all

Here's a thing about JavaScript that trips people up when they first meet it: it runs on a single thread. One line of code executes at a time. There's no true parallel execution happening in your JS code itself.

So if JS can only do one thing at a time, how does Node handle thousands of simultaneous requests without freezing up waiting on a slow database query or file read?

The answer is that Node doesn't wait. When you call something like fs.readFile() or make a network request, Node hands that task off to the C++ layer, which either uses the operating system's own async capabilities or, for things the OS can't do async, pushes the task to a background thread pool (this is handled by a library called libuv). Your JS code moves on immediately to the next line. It doesn't sit around waiting.

The event loop is the mechanism that checks, again and again, whether any of those background tasks have finished. When one finishes, its callback gets queued up, and once your current stack of running code is done, the event loop picks up that queued callback and runs it. I won't go in detail on how event loop works because it is itself a big topic to cover but here's an analogy to understand it.

The easiest way I found to picture it is a restaurant with one waiter. The waiter takes your order and immediately walks to the next table instead of standing at yours waiting for the kitchen. When the kitchen finishes a dish, it rings a bell, and the waiter comes back to deliver it. The waiter is JavaScript's single thread. The kitchen is the C++ and OS layer doing the actual work. The bell, and the waiter checking on bells, is the event loop.

That mental model is what finally made it click why async code in Node doesn't block, even though JS itself is single-threaded. The waiting part was never happening in JS to begin with.

A small detail I didn't expect

One thing that surprised me is that the event loop isn't a single queue. It actually runs in phases - timers, pending callbacks, poll (where most I/O callbacks get handled), and check - each running in a set order, then looping back around. Microtasks like Promise callbacks get squeezed in between each phase too, which is part of why async/await behaves the way it does compared to setTimeout.

I'm still working through the details of each phase, and honestly I don't think I need to memorize the exact order to write good Node code day to day. But knowing that the loop has structure, instead of just being some vague background magic, made the whole system feel a lot less mysterious.

Where I'm at now

Honestly, I still have questions I want to dig into. But just understanding the basic shape of it - JS on top, C++ in the middle, the OS underneath, and the event loop stitching the waiting and the running together - has already changed how I read Node code. I catch myself thinking about what's actually happening on the way down now, instead of just trusting that async/await works like magic. And I think that is enough in this era of AI where you don't really need to go in deep most of the time but you are still required to have an understanding of how things work.

If you're learning Node right now too, I'd genuinely recommend spending an afternoon on this instead of rushing to frameworks. It made everything after this point make a lot more sense.