01. Hello World

In this module, you'll learn the fundamentals of Node.js: how to run scripts, understand the event loop, and work with asynchronous code.

Demo Files

Run with: node hello.js

What is Node.js?

Node.js is a JavaScript runtime - it lets you run JavaScript code outside of a web browser. It uses the same V8 engine that powers Google Chrome.

Key Insight: Node.js is NOT a web server by itself. It's a runtime that CAN create web servers. You write the server code.

Your First Node.js Script

Create a file called hello.js:

// hello.js - Run with: node hello.js

const school = 'UCSD';
const course = 'CSE 135';
const year = new Date().getFullYear();

// Template strings (ES6) work naturally
console.log(`Welcome to ${course} at ${school}, ${year}!`);

// Unlike PHP, this script runs once and exits
// There's no HTTP request triggering it - we just ran a file

Run it from the terminal:

$ node hello.js
Welcome to CSE 135 at UCSD, 2026!

That's it. Node.js executed your JavaScript file, printed output, and exited.

Running Node.js Interactively

Beyond running files with node filename.js, there are other ways to execute Node.js code:

The REPL (Read-Eval-Print Loop)

Run node with no arguments to enter an interactive shell:

$ node
Welcome to Node.js v22.x.x
> 2 + 2
4
> const x = 'hello'
undefined
> x.toUpperCase()
'HELLO'
> .exit

Loading Files in the REPL

Use .load to execute a file within the REPL, keeping its variables available:

$ node
> .load hello.js
Welcome to CSE 135 at UCSD, 2026!
> school
'UCSD'
> course
'CSE 135'

This is useful for testing and exploring your code interactively.

Unix Input Redirection

You can pipe a file into Node.js using Unix shell redirection:

$ node < hello.js
Welcome to CSE 135 at UCSD, 2026!

This is equivalent to node hello.js but uses stdin. Useful in shell scripts or when combining with other Unix tools.

ES6+ Features Just Work

Since Node.js uses V8, all modern JavaScript features are available:

// Arrow functions
const greet = (name) => `Hello, ${name}!`;

// Destructuring
const { method, url } = request;

// Spread operator
const merged = { ...defaults, ...options };

// async/await
const data = await fetch(url);

// Classes
class Server {
    constructor(port) {
        this.port = port;
    }
}
No Babel needed: Unlike browser JavaScript where you might need transpilation for older browsers, Node.js supports modern syntax directly. As of Node.js 22+, you can even run TypeScript natively without a separate compilation step.
Module Systems: Node.js historically used require() (CommonJS) for modules, which differs from the browser's ES6 import syntax. Modern Node.js now supports ES modules, but the ecosystem is still transitioning. You'll encounter both styles in real-world code.

The Event Loop

Here's the most important concept in Node.js: the event loop.

Node.js is single-threaded, but it handles many concurrent operations using an event loop. When you do something that takes time (reading a file, making an HTTP request), Node doesn't wait. It schedules the work and continues running other code.

// async-demo.js - Demonstrates the event loop

console.log('1. Script starts');

// setTimeout schedules work for later
setTimeout(() => {
    console.log('3. This runs after 2 seconds');
}, 2000);

console.log('2. Script continues immediately');

// Output order:
// 1. Script starts
// 2. Script continues immediately
// (2 seconds pass)
// 3. This runs after 2 seconds

Run it:

$ node async-demo.js 1. Script starts 2. Script continues immediately (2 seconds pass) 3. This runs after 2 seconds

Notice that "Script continues" prints BEFORE the setTimeout callback. Node doesn't block waiting for the timer.

Why This Matters for Web Servers

This non-blocking behavior is why Node.js handles many concurrent connections efficiently:

// Conceptual example (not runnable yet)
server.on('request', (req, res) => {
    // Read from database (takes time)
    database.query('SELECT * FROM users', (err, results) => {
        // This callback runs when data is ready
        res.json(results);
    });
    // Node doesn't wait here - it can handle other requests!
});

While waiting for the database, Node.js can handle other requests on the same thread. Note that PHP with Apache handles concurrency differently: Apache typically spawns multiple worker processes or threads, so while each PHP script blocks within its own process, the server as a whole still handles concurrent requests. The trade-off is in memory and resource usage per connection.

Async Patterns: Callbacks, Promises, and Async/Await

Node.js has three patterns for handling async operations. Each is syntactically different, but all fundamentally deal with the same challenge: managing chained dependencies where one operation depends on the result of another.

Callbacks

fs.readFile('data.txt', (err, data) => {
    if (err) throw err;
    console.log(data);
});

Promises

fs.promises.readFile('data.txt')
    .then(data => console.log(data))
    .catch(err => console.error(err));

Async/Await

async function readData() {
    try {
        const data = await fs.promises.readFile('data.txt');
        console.log(data);
    } catch (err) {
        console.error(err);
    }
}
Note: These patterns are different syntax for the same underlying problem: what do you do when operation B depends on the result of operation A? Deeply nested callbacks can become hard to read, and Promises/async-await provide alternative ways to structure this code. However, they don't eliminate the fundamental complexity of chained dependencies.

Key Differences from PHP

Aspect PHP Node.js
Execution Per-request (starts, runs, exits) Persistent (runs continuously)
State Resets each request Persists in memory
Concurrency Model Multi-process (Apache spawns workers) Single-threaded event loop
Server Role Runs inside Apache/nginx Creates own HTTP listener
Production Setup Apache handles HTTP directly Usually behind nginx/reverse proxy
Production Reality: While Node.js can create its own HTTP server, in production it typically runs behind a reverse proxy like nginx. The proxy handles SSL termination, static files, load balancing, and various HTTP edge cases. Node.js doesn't handle all the edge cases you'd need for direct internet exposure as robustly as battle-tested servers like Apache or nginx.

Summary