In this module, you'll learn the fundamentals of Node.js: how to run scripts, understand the event loop, and work with asynchronous code.
Run with: node hello.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.
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.
Beyond running files with node filename.js, there are other ways to execute Node.js code:
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
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.
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.
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;
}
}
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.
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:
Notice that "Script continues" prints BEFORE the setTimeout callback. Node doesn't block waiting for the timer.
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.
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.
fs.readFile('data.txt', (err, data) => {
if (err) throw err;
console.log(data);
});
fs.promises.readFile('data.txt')
.then(data => console.log(data))
.catch(err => console.error(err));
async function readData() {
try {
const data = await fs.promises.readFile('data.txt');
console.log(data);
} catch (err) {
console.error(err);
}
}
| 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 |
node filename.js, or interactively via the REPL.load in the REPL or node < file.js for alternative execution