devnotes.

How Node.js Handles a Request

Knowing the architecture is one thing — seeing it in motion is another. Let's trace what actually happens, step by step, when a Node.js server receives requests from multiple users at the same time.

The setup

Imagine a small server with one route: it looks up a product's price in a database and sends it back. Three users — call them A, B, and C — hit this route within a few milliseconds of each other.

Step by step

  1. Request A arrives. Node.js's single main thread picks it up and starts running the route's code.
  2. The code asks the database for the price. This is an I/O operation, so Node.js doesn't sit and wait — it hands the database query off (through libuv) and immediately becomes free again.
  3. Request B arrives, milliseconds later. Since the main thread isn't stuck waiting on A's database call, it's free to pick up B right away and start its own database query.
  4. Request C arrives. Same story — the main thread handles it, kicks off its own I/O, and moves on.
  5. Database results start coming back, not necessarily in the order the requests came in — maybe B's answer arrives first because that particular query was faster. The event loop notices B's callback is ready and runs it, sending B's response.
  6. A's and C's results follow whenever they're ready, each handled by the event loop in turn.

Notice what didn't happen: at no point did the server dedicate a separate thread to each user, and at no point did request B have to wait in a queue until request A was fully finished. The single thread stayed busy the entire time, but never blocked.

What happens with a CPU-heavy task

Now swap the database call for something that isn't I/O — say, calculating something computationally expensive directly on the main thread, like resizing a large image with pure JavaScript math. This does run on the main thread, and the main thread can only do one thing at a time. While that calculation runs, request B and C simply have to wait, even though they only wanted a fast, simple response. This is the classic Node.js pitfall: one heavy synchronous task can stall every other user connected to your server at that moment.

The practical takeaway

  • I/O-bound work (databases, file reads, network calls, API requests) → Node.js shines, because that work is delegated away from the main thread.
  • CPU-bound work (image processing, encryption of huge payloads, complex math) → best moved off the main thread entirely, using worker threads or a separate service, or the whole server's responsiveness suffers.

This single idea — delegate what can wait, protect the main thread from what can't — is really the core design philosophy behind everything Node.js does.

How Node.js Handles a Request | DevNotes