How Node.js Handles Multiple Requests with a Single Thread

I like making things with code. This is where I share my projects and the bugs I ran into.
Last year I had to get some details fixed on my Aadhaar card, my phone number, my father's name, a few things I knew I would need in my final year and during placements, especially once an offer letter shows up. So one Monday morning I went to the government office to get it done.
And what followed was a proper government-office experience. I ask one person where to update Aadhaar, he sends me to B block, room 21. I go there, and they say no no, second floor, A block, room 4. Yaar, ek baar mein bata do. I finally reach the right room, and there it is, one single employee doing all the Aadhaar corrections, with a big line already snaking out ahead of me. I waited a solid one and a half hours for my turn, they fixed everything, charged me, and I walked out, finally free.
Here is the funny part. When I first learned that Node.js runs on a single thread, one worker for everything, my brain went straight back to that office. One employee, endless line, everyone waiting for hours. I thought, how on earth is that a good idea for a server that has to handle thousands of people at once? Turns out Node's single thread is nothing like that Aadhaar clerk, and that difference is the whole magic. Chalo, let me explain.
The obvious fix, and why it is actually worse
Look at that one overloaded Aadhaar clerk and the fix jumps right out at you, just hire more clerks. One dedicated employee for every applicant. And that is genuinely how servers worked before Node. Every incoming request got its own thread, sometimes even its own whole process, a personal clerk each.
It sounds great until you count the cost. Picture that office actually hiring one full employee for every single applicant who walks in. Hundreds of staff, and most of them just standing around waiting while their one applicant slowly fills a form. In server terms, every thread eats memory, and most of them sit idle, waiting for a slow database query or file. A thousand users means a thousand threads, mostly doing nothing but still costing you. The building runs out of room. Bahut mehenga, aur bahut waste.
Quick foundation: thread vs process
Before we go on, two words. A process is like a whole separate office branch, its own building, its own staff, its own resources, in computer terms its own chunk of memory that it shares with nobody. A thread is one employee inside a branch, sharing that branch's resources with the others. One process can run one thread or many.
The point to feel is cost. Opening a new branch (a process) is heavy. Even adding one employee (a thread) is not free. Node runs your JavaScript on just one thread, inside one process. That is what single-threaded means.
Node's actual trick: one clerk who never sits idle
Node refused both traps. It did not want the slow single Aadhaar clerk, and it did not want an army of idle clerks either. So it did something clever. Keep just one worker, but never, ever let him sit and wait.
Think about why that Aadhaar clerk was so painfully slow. He handled each person completely, start to finish, before calling the next, and any time something slow happened, verifying a document, taking the payment, both of you just sat there frozen. Node's single thread does the exact opposite. The moment a request needs something slow, Node hands that slow part off to the background and immediately serves the next person in line. When the slow bit is done, it comes back and finishes it. The clerk never freezes, so the line never crawls. That "start it, hand it off, move on, come back when ready" cycle is the job of the event loop, and I broke down exactly how it works in my event loop post if you want the deep mechanics.
Delegating the slow work to the background
So where does the slow work actually go? Not onto your one JavaScript thread, that would freeze it, straight back into the Aadhaar trap.
Node hands the heavy, slow stuff, file reads, database queries, network calls, off to background workers, a pool of helper threads (this lives in a part of Node called libuv) and the operating system itself. They do the actual waiting. Your one JS thread stays free the whole time, quickly accepting requests and dispatching them. It is exactly like that clerk sending your document to a back office for verification while he moves straight on to the next applicant, instead of the whole line freezing while it processes.
This is the bit that surprises people. Node is single-threaded for your code, but it is not alone. It has a back office of workers handling the slow chores.
So how does one thread serve a thousand clients?
Put it together and here is the actual flow. A thousand users hit your Node server. They do not each get a thread. They all share the one thread, which cycles through them fast: take request A, kick off its database call, move to request B, kick off its file read, respond to A the moment its data is back, move to C, and on and on.
The reason this works so well is that most of what a web request does is wait, wait for the DB, wait for a file, wait for another API. And waiting is the one thing Node refuses to do on the main thread. So one thread, juggling a huge crowd of mostly-waiting requests, gets shocking mileage. This is concurrency, one worker interleaving many jobs, not true parallelism, and I dug into that difference properly in my "Why Node.js is Fast" post.
Why Node scales so well
Now the payoff. The old model scaled by adding more clerks, more threads or processes, and that burns memory fast, so it hits a wall. Node scales by keeping one clerk busy and idle-free, so it holds thousands of concurrent connections on a fraction of the resources. That is exactly why Node became the go-to for real-time and high-traffic apps, chat, live feeds, APIs, streaming.
One honest caveat. This whole magic only holds when the work is mostly waiting, that is, I/O-heavy. If you drop a heavy CPU task straight onto that single thread, it freezes everyone, and you are right back to the Aadhaar line. That exact trap is what I covered in my blocking vs non-blocking post. Node is a genius at waiting efficiently, not at heavy number-crunching.
Try it yourself
Do not just read it, dekho khud. Save this as a tiny server and run it with node.
const http = require("http");
http.createServer((req, res) => {
console.log("Got a request at", new Date().toLocaleTimeString());
setTimeout(() => res.end("Done"), 3000); // pretend this is slow work
}).listen(3000, () => console.log("Server on http://localhost:3000"));
Now open http://localhost:3000 in three or four browser tabs at almost the same time. Watch your terminal, all the requests get logged instantly, none of them waits three seconds for the others to finish. One thread, one process, happily handling all of them at once, because the slow part was handed off and the thread kept accepting requests.
Wrapping up
So that is the trick. My Aadhaar office was one clerk doing every job fully, start to finish, so a whole line waited hours. The old server way tried to fix that by hiring a clerk per person, and drowned in idle, memory-hungry workers. Node found the smart middle, one clerk who never sits and waits, hands the slow chores to a back office, and cycles through the whole line with the event loop.
That is how a single thread quietly serves thousands, and why Node scales the way it does. Node's single thread is not that overloaded Aadhaar clerk, it is the version of him who never wastes a second. If you want to go deeper, on the loop itself, on concurrency versus parallelism, or on the blocking trap that breaks all of this, my event loop, "Why Node.js is Fast," and blocking vs non-blocking posts are the natural next reads.
I hope you enjoyed reading this.




