Node.js is the runtime that let JavaScript escape the browser and become a first-class server language. Before writing a single line of code it pays to understand what problem it solves, because Node.js is not "yet another language": it is a very specific way of handling concurrency, and that choice explains almost every one of its strengths and every one of its weaknesses. Once you understand its non-blocking I/O model, the rest of the course stops being a collection of recipes and becomes a logical consequence.

In this lesson we will build that conceptual foundation. As an excuse we will use the project that accompanies us throughout the course: Escena Viva, a small company that sells tickets for cultural events at the Teatro Almendra, the Sala Bóveda and the Auditorio Ribera.

Contents

  1. What Node.js is and what problem it solves
  2. JavaScript outside the browser
  3. The conceptual pieces: V8 and libuv
  4. The non-blocking I/O model: the waiter analogy
  5. Comparison with the thread-per-request model
  6. What Node.js is good at (and what it is not)
  7. The npm ecosystem and "JavaScript everywhere"
  8. LTS versions and the release cadence
  9. Why Escena Viva is a good fit for Node.js

  1. What Node.js Is and What Problem It Solves

Node.js is a server-side JavaScript runtime. More precisely: it is a program you install on your computer or on a server that knows how to read .js files and execute them, giving them access to the operating system: files, network, processes, environment variables.

It is not a language. It is not a framework. It is the interpreter plus the libraries that surround it.

The historical problem it set out to solve was concurrency in web servers. In the late 2000s, traditional servers handled every request with a dedicated operating system thread. A thread consumes memory (typically anywhere from hundreds of KB to several MB of stack) and switching between threads has a cost. With thousands of simultaneous connections — many of them simply waiting for a database to answer or for the client to finish downloading — the server spent most of its resources managing idle threads.

This problem even has a name: the C10K problem (serving ten thousand concurrent connections on a single machine). Node.js answered it in a radical way: a single thread for your code, and every input/output operation performed asynchronously.

The core idea to hold on to: in Node.js, your code never sits idle waiting for a slow operation to finish. It requests the operation, carries on with something else and, when the result is ready, comes back to deal with it.

  1. JavaScript Outside the Browser

Until 2009, JavaScript was practically a synonym for "the language of web pages". Ryan Dahl took Google Chrome's V8 engine — already very fast — and packaged it together with an asynchronous input/output library. The result was Node.js.

Taking JavaScript out of the browser changes the tools at your disposal:

Concept In the browser In Node.js
Global object window (or globalThis) global (or globalThis)
User interface document, DOM, mouse events Does not exist: there is no DOM
Storage localStorage, IndexedDB File system, databases
Network fetch, XMLHttpRequest (with CORS) fetch, the http/https modules, TCP sockets, no CORS
File access Restricted and subject to user permission Complete (the fs module)
Process and environment Not accessible process.argv, process.env, process.exit()
Security model The browser sandbox The permissions of the system user

That last row matters: in the browser, JavaScript lives locked up for safety. In Node.js, your script can delete files, open ports and spawn processes. With that power comes the responsibility of never running code you do not trust.

We will see these differences in practice in the lesson Your First Node.js Program.

  1. The Conceptual Pieces: V8 and libuv

Node.js rests on two main components. We introduce them here at a conceptual level; the detailed architecture and the event loop with its phases are covered in Module 2.

V8: the JavaScript engine

V8 is the JavaScript engine developed by Google. Its job is to:

  • Parse your JavaScript code.
  • Compile it to machine code through just-in-time compilation, optimizing the functions that run most often.
  • Manage memory with an automatic garbage collector.

Thanks to V8, JavaScript on Node.js is not "a slow interpreted language": for many tasks it stands shoulder to shoulder with compiled languages running on a virtual machine.

libuv: the asynchronous I/O library

V8 knows how to execute JavaScript, but it does not know how to read files or open sockets. That is libuv's job — a library written in C that provides:

  • A uniform interface over each operating system's asynchronous I/O mechanisms (epoll on Linux, kqueue on macOS/BSD, IOCP on Windows).
  • The event loop, the engine that hands out the work.
  • An internal thread pool for operations the operating system cannot perform asynchronously (much of file access, compression, cryptography).
flowchart TB
    A["Your JavaScript code<br/>(src/catalog.js)"] --> B["Node.js<br/>APIs: fs, http, path..."]
    B --> C["V8<br/>Runs JavaScript"]
    B --> D["libuv<br/>Event loop + thread pool"]
    D --> E["Operating system<br/>Files, network, timers"]
    C --> D

One clarification that heads off a very common misunderstanding: when people say "Node.js is single-threaded" they mean the thread where your JavaScript runs. Underneath, libuv uses several threads. What never happens is two lines of your code running at the same time.

  1. The Non-Blocking I/O Model: the Waiter Analogy

This analogy is the best way to internalize the model. Picture the restaurant at the Teatro Almendra on an opening night.

Blocking model: one waiter per table

In a traditional restaurant, every table has its own assigned waiter. That waiter:

  1. Takes the order from table 1.
  2. Carries it to the kitchen.
  3. Stands in the kitchen waiting for the dish to be ready.
  4. Takes it to table 1.
  5. Only then attends another table.

To serve 50 tables you need 50 waiters. Most of the time they are standing still, waiting for the kitchen. Hiring waiters is expensive and the kitchen corridor fills up with people doing nothing.

Non-blocking model: one extremely well-organized waiter

Now picture a single, exceptionally efficient waiter:

  1. Takes the order from table 1 and drops it off at the kitchen.
  2. Does not wait: goes to table 2 and takes its order.
  3. Moves on to table 3, serves drinks to table 4, takes payment from table 5...
  4. When the kitchen rings the bell ("order for table 1 is up!"), the waiter picks up the dish and serves it.

A single waiter serves dozens of tables, because the slow work is not done by them: it is done by the kitchen. They only coordinate.

Translated into Node.js:

Restaurant Node.js
The waiter The main thread where your JavaScript runs
The tables The clients' requests
The kitchen The operating system, the database, the network, the disk
The kitchen bell The "operation completed" event
The notepad of pending orders The loop's event queue
The waiter cooking a three-hour stew themselves A CPU-intensive computation that blocks everything

That last row is the key to Node.js's limitations: while the waiter is cooking, nobody is serving the tables. We will come back to it in section 6.

What this looks like in code

Look at the conceptual difference (you do not need to understand every detail yet; this is purely illustrative):

// BLOCKING style (synchronous): the program stops on every line.
const fs = require('node:fs');

const catalog = fs.readFileSync('data/events.json', 'utf8'); // Waits here
console.log('Catalog loaded');
console.log('This line runs after the file has been read');
// NON-BLOCKING style (asynchronous): the program carries on and comes back later.
const fs = require('node:fs');

fs.readFile('data/events.json', 'utf8', (error, catalog) => {
  // This function runs WHEN the file is ready
  console.log('Catalog loaded');
});

console.log('This line runs BEFORE the file has finished being read');

In the second case, the console output appears in the "reverse" order to the one reading the file suggests. That is disconcerting at first and is exactly what we will explore in depth in Callbacks and Asynchronous Programming.

  1. Comparison with the Thread-Per-Request Model

Let's compare Node.js with the classic model used (or once used) by PHP with Apache or Java with traditional servlets.

Aspect One thread/process per request (classic PHP, traditional Java) Node.js (event loop)
Unit of concurrency Operating system thread or process Callback / promise on a single thread
Cost per idle connection High (the thread's stack memory, ~1 MB or more) Very low (a few KB of data structures)
Viable simultaneous connections Hundreds or a few thousand per machine Tens of thousands per machine
Programmer's mental model Simple: the code reads top to bottom Requires thinking asynchronously
Shared in-memory state Requires synchronization (locks, critical sections) No race conditions inside a callback
CPU-intensive computation Spread across threads and cores Blocks every user
Unhandled failure Usually affects only one request Can bring down the whole process if not handled
Use of multiple cores Automatic Requires cluster or several processes (Module 10)

There is no "better" model. There is a model suited to a given load profile. Node.js shines when the server spends most of its time waiting on other systems, which is precisely what a modern web API does.

sequenceDiagram
    participant C1 as Client A
    participant C2 as Client B
    participant N as Node.js (1 thread)
    participant DB as Database

    C1->>N: GET /events
    N->>DB: query events
    Note over N: Does not wait: stays free
    C2->>N: GET /sessions
    N->>DB: query sessions
    DB-->>N: events result
    N-->>C1: response A
    DB-->>N: sessions result
    N-->>C2: response B

  1. What Node.js Is Good At (and What It Is Not)

Cases where Node.js is an excellent fit

  • REST APIs and microservices: the work consists of receiving a request, querying a database and returning JSON. That is almost pure I/O, and JSON is JavaScript's native format.
  • Real-time applications: chats, notifications, live dashboards, capacity tracking. Thousands of open connections that are idle almost all the time: the ideal scenario.
  • Command-line tools (CLI): fast start-up, a huge ecosystem, easy to distribute through npm. Much of the modern web development toolchain is written in Node.
  • BFF (Backend For Frontend): a thin layer that aggregates calls to several internal services and composes exactly the response a single interface needs. Its job is to wait for several APIs at once: perfect for Node.
  • Data streaming: processing large files or continuous flows without loading them into memory in full (Module 3).
  • Prototyping and full-stack teams: the same language and the same validations on client and server.

Cases where Node.js is not the best choice

  • CPU-intensive computation: video processing, model training, scientific computing, bulk cryptography, generating reports with millions of rows in memory. While your function is calculating, the "waiter" is cooking and nobody is serving the tables.
  • Applications with strict typing requirements and complex shared-memory concurrency, where other ecosystems offer more mature tooling.
  • Tasks that demand decimal numeric precision, such as accounting with decimals: JavaScript's number type is floating point. (That is why, in Escena Viva, we will store prices in cents as integers; we will formalize this in the lesson The Course Project).

Important: "not the best choice" does not mean "impossible". Module 10 shows how to move heavy computation off the main thread with worker threads, how to spread load across cores with cluster, and how to defer work to queues with Redis. The limitation is real, but it has well-known solutions.

  1. The npm Ecosystem and "JavaScript Everywhere"

Node.js ships alongside npm (Node Package Manager), the largest package registry in the world, with millions of published libraries. This has two sides.

The good side: almost any common problem has already been solved. Need a web framework? Express. Validate data? Zod or Joi. Talk to MongoDB? Mongoose. Generate a PDF ticket? There are half a dozen options.

The side to watch: installing a package means bringing third-party code into your product. Dependency management, semantic versioning and security audits are part of the craft, and we devote the whole of Module 5 to them.

On top of that, "JavaScript everywhere" means the same language serves the browser, the server, build tooling, desktop applications (Electron) and serverless functions. For a small team like Escena Viva's, this cuts the cost of context switching: the person who validates a form in the browser can reuse that very same validation function in the API.

  1. LTS Versions and the Release Cadence

Node.js follows a very predictable release calendar. Knowing it avoids nasty surprises in production.

  • Every April an even-numbered major version is released (20, 22, 24, 26...).
  • Every October an odd-numbered major version is released (21, 23, 25...).
  • Only even versions become LTS (Long Term Support), in October of the year they were released.
  • Odd versions are short-lived (about six months) and exist to try out new features.

Life cycle of an even version:

Phase Approximate duration What it gets Use in production?
Current 6 months (April → October) Every new feature Only for experimenting
Active LTS 12 months Fixes and safe improvements Yes, recommended
Maintenance LTS 12 months Only critical and security fixes Yes, but plan the jump
End of Life — Nothing No, no security patches

The practical rule for a real project is simple: always use the latest Active LTS version and plan the upgrade when yours moves into Maintenance. For Escena Viva we will use an LTS version and pin it in the project so the whole team works the same way.

How to install versions and switch between them is exactly the subject of the next lesson, Installing and Setting Up the Environment.

  1. Why Escena Viva Is a Good Fit for Node.js

Escena Viva sells tickets for events such as the Concierto de Otoño (Teatro Almendra), the Noche de Monólogos (Sala Bóveda) and the Festival de Jazz de Primavera (Auditorio Ribera). Let's analyze its workload:

Business need Work profile Good fit for Node.js?
Public catalog of events and sessions Read from a database and return JSON Yes: pure I/O
Real-time available capacity during a presale Thousands of open, idle connections Yes: this is the flagship case
Traffic spikes when a festival goes on sale High concurrency, little CPU per request Yes
Order confirmation through an external payment gateway Waiting for another API to respond Yes
Emailing the ticket out Network I/O, can be deferred to a queue Yes (Module 10)
Generating 50,000 ticket PDFs in one go CPU-intensive Not on the main thread: worker threads or a queue
Monthly sales report in CSV Large file Yes, with streams (Module 3)

Of seven needs, six are I/O. That ratio — very common in business applications — is why Node.js is a sensible choice for this platform. And the exception (the PDFs) has a well-known solution inside the ecosystem itself.

This is what our API's entry point will look like a few modules from now. You do not need to understand it yet; just look at it as a destination:

// Illustrative snippet: do NOT write this yet.
// A minimal server that responds with the Escena Viva catalog.
const http = require('node:http');

const server = http.createServer((request, response) => {
  response.writeHead(200, { 'Content-Type': 'application/json' });
  response.end(JSON.stringify({ message: 'Welcome to Escena Viva' }));
});

server.listen(3000);

That is eight lines for a working HTTP server, with nothing else installed. That economy of means is also part of the appeal of Node.js.

Common Mistakes and Tips

Mistake 1: believing that "single-threaded" means "slow" or "does not use the hardware". A single-threaded Node.js process can serve more concurrent connections than a traditional server with hundreds of threads, because hardly any of them consume CPU. And to take advantage of several cores there is cluster (Module 10): you launch as many Node processes as you have cores.

Mistake 2: confusing "asynchronous" with "parallel". Asynchronous means "I don't wait here, they'll let me know when it's ready". Parallel means "two things happening at the same time". Node.js does a great deal of the former and, for your JavaScript code, none of the latter.

Mistake 3: thinking Node.js is a web framework. Node.js is used to write web servers, but also CLIs, automation scripts, file processors or integration bots. Express (Module 6) is the framework; Node.js is the platform.

Mistake 4: choosing an odd version for a serious project. Odd versions run out of support within a few months. If the major number is odd, do not take it to production.

Tip 1: always ask "who is doing the waiting". For any operation, ask yourself whether the waiting is done by your code (bad) or by the operating system (good). This question will follow you through the entire course.

Tip 2: measure before ruling things out. "Node.js is no good for computation" is true beyond a certain scale. A 2 ms operation blocks nothing noticeable; an 800 ms one does.

Tip 3: do not memorize APIs, understand the model. The functions in fs or http are documented and you can always look them up. The execution model, by contrast, is something you have to carry in your head.

Exercises

Exercise 1: classify the workloads

For each of these hypothetical Escena Viva features, say whether it is I/O-intensive or CPU-intensive, and whether Node.js is a good choice for handling it on the main thread:

  1. Querying the database for the tickets still available for the Concierto de Otoño.
  2. Compressing the 40,000 passes for the Festival de Jazz de Primavera into a ZIP file.
  3. Keeping a connection open with every browser to warn them when fewer than 20 tickets are left.
  4. Working out the optimal seat allocation for a group of 15 people at the Auditorio Ribera by trying every combination.
  5. Calling the payment gateway to confirm a charge.

Exercise 2: explain the analogy in your own words

Write a paragraph of 5-8 lines explaining to a colleague who only knows PHP why a single Node.js process can serve 10,000 simultaneous connections while their Apache server saturates at 300. You must use the waiter analogy and explicitly mention where the slow work happens in each case.

Exercise 3: pick a version

You are starting Escena Viva in August 2026. Consulting the Node.js release calendar:

  1. Which version line would you choose, and why?
  2. What will happen to that version roughly a year from now?
  3. What would be the downside of choosing the freshly released Current version?

Solutions

Solution 1

# Feature Type Node.js on the main thread?
1 Querying availability I/O Yes. The work is done by the database; Node just waits for the result.
2 Compressing 40,000 passes CPU No. It would block every user for seconds or minutes. Solve it with worker threads or a job queue (Module 10).
3 Open connections for alerts I/O Yes, and this is where Node.js stands out most: thousands of idle connections cost very little.
4 Brute-force seat combinations CPU No, if the number of combinations is large. Better to find a smarter algorithm or move it to a worker.
5 Call to the payment gateway I/O Yes. It is waiting on the network, exactly what the asynchronous model is for.

Nuance: point 4 depends on the size. If "every combination" amounts to a few dozen, the computation takes microseconds and there is no problem. The right question is never "is it CPU?", but "how long does it block for?".

Solution 2 (model answer)

In Apache with PHP, every visitor gets their own process or thread. That thread asks MySQL for the data and stands still waiting for the answer, occupying memory without doing anything useful; since each thread costs megabytes, by three hundred connections the server has run out of resources. Node.js works like a single, extremely well-organized waiter: it takes the order (the request), drops it off at the kitchen (the database or the operating system) and immediately goes off to serve another table instead of standing there waiting. It is not the one doing the slow work, the kitchen is; the waiter merely coordinates and serves when the bell rings. That is why ten thousand connections that spend nearly all their time waiting cost almost nothing: they are ten thousand orders jotted down on a notepad, not ten thousand waiters. The trade-off is that, if the waiter starts cooking a stew themselves — a heavy computation — every table goes unattended at once.

Solution 3

  1. The latest even line in Active LTS. In August 2026 that means the 24.x line, which entered LTS in October 2025. It is the one receiving stable fixes and the one supported by hosting providers and by the ecosystem's libraries.
  2. Around October 2027 it will move to Maintenance LTS (critical and security patches only), which is the moment to plan the jump to the next even line — 26.x, which will have entered LTS in October 2026.
  3. The freshly released Current version (26.x until October 2026) brings interesting new features, but it may introduce behavioral changes, many native libraries do not support it yet, and it is not designed for production loads. It is excellent for trying things out locally, not for selling tickets.

Conclusion

Node.js is a runtime that brings JavaScript to the server by combining the V8 engine (which executes the code) with libuv (which manages asynchronous input/output and the event loop). Its proposal is to replace the "one thread per request" model with a single thread that never waits: it delegates the slow work and responds to notifications, like a waiter who coordinates instead of cooking.

From that follow its strengths — APIs, real time, CLI tools, BFF layers, streaming — and its well-known weakness: CPU-intensive computation blocks everyone, something Module 10 solves with worker threads, cluster and queues. Around it, the npm ecosystem provides an unmatched library of solutions, and the LTS release calendar lets you choose sensibly which foundation to build on.

With this mental map in place, we have also seen why Escena Viva — a ticketing platform where almost everything is waiting on databases, payment gateways and clients — is a natural use case for Node.js.

In the next lesson, Installing and Setting Up the Environment, we move from theory to practice: we will install Node.js with a version manager, verify that everything works and create the escena-viva/ folder where the project will live throughout the course's twelve modules.

Node.js Course: From Beginner to Advanced

Module 1: Introduction to Node.js

Module 2: Core Concepts

Module 3: File System and I/O

Module 4: HTTP and Web Servers

Module 5: NPM and Package Management

Module 6: The Express.js Framework

Module 7: Databases and ORMs

Module 8: Authentication and Authorization

Module 9: Testing and Debugging

Module 10: Advanced Topics

Module 11: Deployment and DevOps

Module 12: Real-World Projects

© Copyright 2026. All rights reserved