Node.js — JavaScript on the Server
What you'll learn
- What Node.js is and why it changed backend development
- The idea of non-blocking (async) code
- Modules, npm, and a simple server shape
- You'll write async logic that actually runs
JavaScript escapes the browser 🚀
For years JavaScript only ran inside browsers. Node.js freed it: a runtime that runs JS anywhere — on servers, your laptop, tiny devices. Suddenly you could use one language for both frontend and backend. That's a huge part of why Node exploded.
Node's superpower is being non-blocking. Imagine a waiter taking your order, then — instead of standing frozen at the kitchen until your food is ready — going to serve other tables, and coming back when the food's up. That's asynchronous code: start a slow task (read a file, query a database), don't freeze, handle other work, react when it finishes.
We express "do this when it's ready" with async/await:
async function getUser(id) {
const user = await database.find(id); // "await" = wait here, but don't freeze the whole server
return user;
}
Node also uses modules (split code into files) and npm (the Node Package Manager) — the world's largest library of free, reusable code. Need a web framework? npm install express. A date library? It's one command away.
A tiny Express server looks like this — notice it's just the REST routing you already learned:
const express = require("express");
const app = express();
app.get("/users", (req, res) => {
res.json([{ id: 1, name: "Sara" }]);
});
app.listen(3000); // now listening for requests on port 3000
Practice async. Write an async function fetchDouble(n) that uses await on the provided slowDouble helper (it returns a promise that doubles a number) and returns the doubled value.
slowDouble is given. Just await it and return the result.
The event loop — one thread, thousands of requests 🔁
How does a single-threaded runtime like Node handle 10,000 simultaneous users without 10,000 threads? The answer is the event loop, and it's worth building a mental picture of it.
Node runs your JavaScript on one thread. When your code calls something slow — reading a file, querying a database, making a network request — Node doesn't wait there. It hands that slow operation off to the system (or a background thread pool) and immediately moves on to the next line of code that's ready to run. When the slow operation finishes, its callback gets placed in a queue, and the event loop picks it up the moment the main thread is free.
Call stack (your running code)
↓ "this is slow, I'll notify you later"
Background (OS / thread pool handles file I/O, network, timers)
↓ "done! here's your callback"
Callback queue → event loop pushes it onto the call stack when it's empty
This is why a single Node process can juggle thousands of open connections: it's never computing for all of them at once, it's just never sitting idle waiting for slow I/O either. The trade-off is the flip side of the same coin — if your own JavaScript code runs a genuinely slow computation (not I/O, like a giant loop), it blocks that one thread and everything else really does have to wait, since there's only one. That's why heavy CPU work in Node is usually pushed to a separate worker process, while I/O-bound work (which is most of what backends do — waiting on databases and APIs) is exactly what the event loop is built for.
Predict and produce event-loop ordering. Complete orderedLog() so that it logs (and returns as an array) the strings in the order Node's event loop would actually run them: synchronous code first, then the async one, in the order shown by the comments.
Push each string onto the order array instead of just console.log-ing it, so we can check the exact sequence.
Real-world case study: why Node.js exists at all 🔍
Node.js wasn't an accident — it was a direct reaction to a specific, well-known performance problem. Ryan Dahl, its creator, presented Node at JSConf in 2009, and the motivating example he opened with was famously simple: a file upload progress bar. On traditional web servers of that era (like Apache, using one thread or process per connection), handling thousands of slow, simultaneous connections — like many users uploading files at once — meant thousands of threads, each consuming memory just to sit around waiting for slow disk or network I/O to finish. That waiting was the whole problem: threads are expensive, and most of a web server's life is spent waiting on I/O, not computing.
Dahl's insight was to flip the model entirely: use one thread, but never let it sit idle waiting. Instead, hand off every slow operation and let an event loop route callbacks back in when they're ready — exactly the mechanism this lesson just walked through. Because JavaScript already had a natural culture of callbacks (from browser event handlers like "onclick") and, critically, no existing built-in blocking I/O APIs to break compatibility with, it turned out to be an unusually good fit for this non-blocking model — arguably better suited than most server-side languages available at the time.
The bet paid off dramatically: companies including Netflix, PayPal, LinkedIn, and Uber rebuilt significant parts of their backend infrastructure on Node specifically because I/O-heavy workloads (which is most of what a typical backend does — waiting on databases, APIs, and file systems) run remarkably efficiently on this model. LinkedIn famously reported replacing 30 servers running their old Ruby-based mobile backend with 3 Node.js servers after a rewrite. None of that is about JavaScript being a "faster" language in the raw-computation sense — it's entirely about the event loop model this lesson covers being the right tool for I/O-bound work.
Question 1 of 5
What did Node.js make possible?
Question 2 of 5
What does 'non-blocking' mean?
Question 3 of 5
What is npm?
Question 4 of 5
In Node's event loop, what runs first for a given piece of code?
Question 5 of 5
What made JavaScript an especially good fit for Node's non-blocking model?
Summary
- Node.js runs JavaScript on servers — one language, whole stack
- Non-blocking async (async/await) lets one server handle many requests
- npm gives you a giant library ecosystem; Express makes REST routing simple
- You wrote real async/await code, then traced the exact order the event loop runs sync code, promises, and timers
- Node's whole design — one thread, never blocking on I/O — is why Netflix, PayPal, and LinkedIn rebuilt backends on it
Build it: npm init -y, npm install express, and make the tiny server above return your own JSON. Next: Caching & Queues — making backends fast and resilient.
Practice
Reading is not learning until you run something. Pick one.