Node.js β JavaScript as Backend
The same JavaScript you've written all semester β now running the server itself. Every server on this page runs live, in a Node emulator built into your browser.
0 Week at a glance
- Server-side programming β code that runs on the server, hidden from visitors.
- Node.js β Chrome's V8 engine, unplugged from the browser and given server superpowers.
- Event loop, level 2 β Week 9's loop returns, now with the microtask queue.
- npm β the world's biggest code registry, and package.json, your project's manifest.
- Core modules β http, url, fs, path: everything you need for a real server.
- MVC β the architecture that keeps server code from becoming spaghetti.
New this week: the Node emulator. Node code can't normally run in a web page β so this island ships a mini Node.js written in JavaScript. Examples marked π’ node emulator support require(), a virtual disk, and real servers you can visit with the built-in mini-browser. Everything is editable, as always.
1 Why backends?
- Server-side pages are programs β PHP, Java/JSP, Ruby on Rails, ASP.NET, Python, and now JavaScript.
- They make pages dynamic: different content per user, per time, per context.
- They talk to other services: databases, e-mail, payment systems.
- They authenticate users and process form data safely.
- They give security: server code can never be viewed from a browser.
Prove it β what can the visitor actually see?
// Secrets can live here β the browser NEVER receives this file
const API_KEY = "sk-live-9f2β¦TOP-SECRET";
const db = connect("mongodb://anime-db:27017");
server.get("/anime", (req, res) => {
const anime = db.query("SELECT * FROM anime");
res.send(renderPage(anime)); // only the RESULT is sent
});
Performance & compatibility wins
- Compute once, cache β don't repeat heavy work for every visitor.
- Stronger machines β heavy computation runs on server hardware, not a student laptop.
- Near the data β data-intensive work runs next to the database.
- Compatibility β dynamic behaviour even for clients with little or no JS support.
Web hosting β where servers live
Hosting = storing files on a server and making them accessible over the internet, securely and stably.
| Hosting type | The idea |
|---|---|
| Shared | Many websites share one machine β cheapest, least control. |
| VPS (virtual private server) | One machine sliced into isolated virtual servers. |
| Dedicated | A whole physical machine for you alone. |
| Managed | Someone else handles updates, backups and security. |
| Cloud | Elastic resources across many machines β scale up and down on demand. |
2 What is Node.js
Node.js = a JavaScript runtime environment, written in C++, that lets JavaScript run in the backend. It interprets and executes JS, and ships a set of libraries (the Node API) for building server programs.
The three key features
| Feature | Meaning |
|---|---|
| Event-driven architecture | Handles multiple connections using an event loop β work happens when events fire. |
| Non-blocking I/O | Input/output (network, files, databases) runs asynchronously β waiting costs nothing. |
| Single-threaded | One thread β yet it manages thousands of requests efficiently. Week 9 explained how! |
Traditional server vs. Node
| Traditional (thread-per-request) | Node.js (event loop) | |
|---|---|---|
| Each request gets⦠| Its own thread | A callback in the queue |
| While waiting for the DB⦠| The thread sits blocked, wasting memory | The loop serves other requests |
| 10 000 connections | 10 000 threads π° | Still one thread π |
| Wins | Simple mental model, true parallel CPU work | Scalability, responsiveness, resource efficiency, simpler error handling |
Why developers love it
- Lightweight and fast β V8 compiles JS to machine code.
- One language everywhere β JS is the only language that runs both frontend and backend: shared code, shared types, shared libraries.
- Easy learning curve β you already know the language!
- Ecosystem β npm hosts over two million packages.
- Community-powered β cross-platform and multi-purpose.
3 One engine, two worlds
parser Β· execution engine Β· garbage collector Β· call stack
document, window, alert, fetch, eventsβ¦
the exact same engine!
http, fs, path, os, processβ¦
- Ask Chrome's V8 to run console.log('V8') β works. Ask it for document.querySelector('div') β the DOM API answers.
- Ask Node's V8 to run http.createServer() β the Node API answers.
- Ask Node for document.querySelector('div') β ???????? There is no DOM on a server!
// This runs in the NODE EMULATOR β there is no web page here.
console.log("Node says hi β no browser needed!");
const os = require('os');
console.log("Platform:", os.platform(), "Β· CPU cores:", os.cpus().length);
// Now try the impossible:
document.querySelector("div"); // π₯ servers have no document!
The sorting game: will it run?
document.querySelector("h1")
const http = require('http')
console.log("hello")
alert("Hi!")
const fs = require('fs')
setTimeout(cb, 1000)
fetch("https://api.github.com")
window.location.href
4 The event loop, level 2
- Call stack β runs your functions, one at a time (Week 9 recap).
- Internal implementation β the C++ code (browser or Node) behind setTimeout & friends. The host itself is multi-threaded β your JS is not.
- Task queue β callbacks from setTimeout, addEventListenerβ¦ wait here.
- Micro-task queue β Promise callbacks are special: they get their own, higher-priority queue.
- Event loop β when the stack empties, it drains all micro-tasks first, then takes one task.
Watch the VIP lane in action
console.log("Start");setTimeout(() => console.log("Timeout!"), 0);Promise.resolve().then(() => console.log("Promise!"));console.log("End");
π Call stack (JS thread)
βοΈ Internal implementation
β‘ Micro-task queue (VIP)
β³ Task queue
π₯οΈ Console
Don't believe the simulation? Run the real thing
console.log("Start");
setTimeout(() => console.log("Timeout!"), 0); // task queue
Promise.resolve().then(() => console.log("Promise!")); // MICRO-task queue
console.log("End");
Exam favourite: the order is Start β End β Promise! β Timeout! Even at 0 ms, the timeout callback waits in the task queue while the Promise callback cuts the line through the micro-task queue.
Extra practice: jsv9000.app β paste any program and step through the loop frame by frame. Edit the code and run it again!
5 Getting Node on your machine
node -v // v22.x β the runtime
npm -v // 10.x β the package manager (ships WITH Node)
Installation instructions: the Pre-Lab inside Lab 4 on Brightspace has the full step-by-step guide. Install before the lab β it takes five minutes.
6 Modules & require()
| You write | Node loads |
|---|---|
| require("fs") | A core module (built into Node) β or an npm package from node_modules/. |
| require("./greet") | Your file greet.js, from the same directory. |
| require("../utils/greet") | Your file, from the parent directory. |
| require("/abs/path/greet") | Your file, by absolute path. |
The core modules toolbox
| Module | Job |
|---|---|
| fs | File system operations β reading & writing files. |
| http | Creating HTTP servers and handling requests. |
| path | Working with file and directory paths. |
| os | System information β CPU, memory, platform. |
| events | Event-driven programming (EventEmitter). |
| crypto | Encryption, hashing and security operations. |
const os = require('os');
const path = require('path');
console.log("You are on:", os.platform());
console.log("CPU cores:", os.cpus().length);
console.log("joined path:", path.join("students", "week10", "app.js"));
// The emulator ships core modules only. Try uncommenting:
// require('express'); // β Error: Cannot find module 'express'
User-defined modules β your own files
function greet(name) {
return "Hello, " + name + "!";
}
module.exports = greet; // make it borrowableconst greet = require("./greet");
console.log(greet("Prof. Abul Qasim"));
// β Hello, Prof. Abul Qasim!7 npm & package.json
- npm (Node Package Manager) comes with the Node.js installation.
- npm install <package> downloads a package into node_modules/ and records it in package.json.
- Installed packages are used via require() β same as core modules.
- npm install -g nodemon installs globally β a command available everywhere (nodemon auto-restarts your server on every save).
The practice terminal β set up a real project
my-first-server/
What is package.json?
package.json is the configuration file of a Node project: its name, version, scripts, metadata β and most importantly its dependencies. Created automatically with npm init -y (-y = "yes to all questions").
- Running npm install with no arguments installs everything listed in package.json.
- That's why node_modules/ is never committed to git β anyone can rebuild it from package.json.
dependencies vs. devDependencies
| dependencies | devDependencies | |
|---|---|---|
| Neededβ¦ | At runtime β the app breaks without them | Only while developing |
| Examples | express | nodemon, test runners |
| Install flag | npm install express (--save is the default) | npm install --save-dev nodemon |
| In production | Installed | Skipped β smaller, faster deploys |
8 Semantic versioning & the lockfile
The ^ and ~ playground
package.json says "express": "^4.21.2". Which versions is npm allowed to install? Pick a prefix:
Memory hook: ~ is a small wiggle (patches only) Β· ^ points up higher (minors too) Β· no prefix = frozen solid. npm's default when you install is ^.
package-lock.json β the exact receipt
- Generated on npm install β catalogs exactly what landed in node_modules/.
- On the next install, package-lock.json trumps package.json: the locked versions win.
- Result: your teammate installs the identical versions you have. No "works on my machine".
| Feature | package.json | package-lock.json |
|---|---|---|
| Purpose | Project metadata & dependency ranges | Locks exact versions of everything |
| Versioning | Semantic ranges ("express": "^4.21.2") | Exact pins ("version": "4.21.2") |
| Created by | You (via npm init, then edited) | npm, automatically on install |
| Edit by hand? | Yes, routinely | Never |
| Guarantees stability? | No β ranges allow updates | Yes β prevents version drift |
9 Your first server
http.createServer(callback) creates a web server. The callback receives req (the incoming request β URL, method, headers) and res (an object that collects your response). server.listen(port) opens the port and starts accepting visitors.
Hello, World β a real server, right here
const http = require('http');
const server = http.createServer(function (req, res) {
res.writeHead(200, { "Content-Type": "text/plain" });
res.write("Hello World from my first server!");
res.end();
});
server.listen(3000, () => {
console.log("Server running at http://localhost:3000");
});
Spot the bug β the original slide version
var http = require('http');
var server = http.createServer(function (req, res) {
res.writeHead(200);
resp.write('Hello World'); // π hmmβ¦
res.end();
});
server.listen(3000);
Lesson inside the bug: the callback only runs per request β a broken handler starts up fine and explodes on the first visit. Always test by visiting!
What's inside req?
const http = require('http');
const server = http.createServer((req, res) => {
console.log("method:", req.method); // 'GET', 'POST', β¦
console.log("url:", req.url); // '/users', '/about?x=1', β¦
console.log("host header:", req.headers.host);
console.log("http version:", req.httpVersion);
res.end("Request received β now check the console output!");
});
server.listen(3000);
Building the response
| Method | Job |
|---|---|
| res.writeHead(code, headers) | Set the status code + headers in one go. |
| res.write(chunk) | Write a piece of the response body. |
| res.end([chunk]) | Finish the response β mandatory! Forget it and the browser waits forever. |
| res.json(obj) | Send a JSON response (an Express nicety β the emulator has it too). |
| res.download(path) | Send a file back (Express). |
Status codes & MIME types
| Family | Meaning | Famous examples |
|---|---|---|
| 1xx | Informational | 100 Continue |
| 2xx | Success | 200 OK, 201 Created |
| 3xx | Redirection | 301 Moved Permanently, 304 Not Modified |
| 4xx | Client error | 404 Not Found, 403 Forbidden |
| 5xx | Server error | 500 Internal Server Error (you met it in the buggy server!) |
10 The URL module
Anatomy of a URL β click each piece
Properties & searchParams β live
const { URL } = require('url');
const myUrl = new URL("https://www.example.com:8080/products/shoes?color=red&size=10#reviews");
console.log("protocol:", myUrl.protocol);
console.log("hostname:", myUrl.hostname);
console.log("port:", myUrl.port);
console.log("pathname:", myUrl.pathname);
console.log("search:", myUrl.search);
console.log("hash:", myUrl.hash);
// searchParams: get / set / append / delete
const p = myUrl.searchParams;
console.log("color =", p.get("color"));
p.set("size", "11");
p.append("coupon", "WEEK10");
console.log("updated URL:", myUrl.href);
A server that reads the query string
const http = require('http');
const { URL } = require('url');
const server = http.createServer(function (req, res) {
res.writeHead(200, { "Content-Type": "text/html" });
const q = new URL(req.url, "http://" + req.headers.host);
res.write("host = " + req.headers.host);
res.write("<br>pathname = " + q.pathname);
res.write("<br>search = " + q.search);
const qdata = q.searchParams;
res.write("<br><b>Month = " + qdata.get("month") + "</b>");
res.write("<br><b>Year = " + qdata.get("year") + "</b>");
res.end();
});
server.listen(8080, () => console.log("Server running at http://localhost:8080"));
11 The fs & path modules
- Node supports reading, writing, creating and deleting files.
- require('fs').promises imports the modern, non-blocking, Promise-based API β made for await.
const fs = require('fs').promises; // the Promise-based API
// Week 9 skills, server edition:
const data = await fs.readFile('example.txt', 'utf8');
console.log("example.txt says:", data);
await fs.writeFile('notes.txt', 'Node.js writes files!');
await fs.appendFile('notes.txt', ' And appends too.');
console.log("notes.txt says:", await fs.readFile('notes.txt', 'utf8'));
console.log("the disk now holds:", await fs.readdir('.'));
The Week 9 connection: fs.promises methods return Promises β so await, try/catch and everything from the Island of Time applies, unchanged.
path β never glue paths by hand
- Windows uses \, Mac/Linux use / β path handles both, cross-platform.
const path = require('path');
const p = "/students/week10/report.pdf";
console.log("basename:", path.basename(p)); // file name
console.log("dirname :", path.dirname(p)); // its directory
console.log("extname :", path.extname(p)); // file extension
// join: cleans up slashes AND resolves '..' automatically
console.log("join :", path.join("students", "week10", "..", "week11", "lab.js"));
// resolve: always returns an ABSOLUTE path
console.log("resolve :", path.resolve("images", "logo.png"));
12 Model β View β Controller
| Part | Represents | Responsibilities |
|---|---|---|
| π§ Model | Knowledge | Stores the data + the logic to access and change it. Calculations, data manipulation. Exists independently of how it's displayed. |
| π¨ View | Presentation | How data is shown to the user β renders HTML (or JSON, XMLβ¦). Receives data from the controller. Presentation logic only. |
| π¦ Controller | The link user β system | The brains: reads the user's input, validates & sanitizes it, decides how the model changes, and picks which view to render. |
Follow one request through MVC
π§ Browser
the user
π¦ Controller
decides
π§ Model
data + logic
π¨ View
renders
Why bother?
- Separation of concerns β data, logic and looks live apart.
- Data can change independently of its presentation.
- The same data, different views β one model can feed a web page, an app and an API.
- Easier maintenance and testing β each part tests in isolation.
- Team-friendly β coding tasks split cleanly between people.
- Reusability β one model, many views.
13 Key takeaways
- Server-side code is invisible to visitors β that's where secrets, databases and authentication live.
- Node.js = V8 + Node APIs β Chrome's engine, with http/fs/path instead of the DOM. No document on a server!
- Node is event-driven, non-blocking, single-threaded β one thread serves thousands of connections.
- The micro-task queue (Promises) beats the task queue (setTimeout) β every time the stack empties.
- require() loads core modules ("fs"), your files ("./greet"), or npm packages ("express").
- npm init -y creates package.json Β· npm install reads it Β· package-lock.json pins exact versions and wins.
- Semver: ^ allows minor + patch, ~ allows patch only, no prefix = exactly that version.
- http.createServer((req, res)) + server.listen(port) β and always res.end(), or the browser waits forever.
- new URL() + searchParams dissect URLs and query strings safely.
- MVC: Model = data + logic Β· View = presentation Β· Controller = the brains between them.
14 Self-check quiz
No grades, instant feedback. Can you get all eight?
Q1. What is Node.js?
Q2. What does this print, in order?
console.log("A"); setTimeout(() => console.log("B"), 0); Promise.resolve().then(() => console.log("C")); console.log("D");
Q3. package.json says "express": "^4.21.2". Which version may npm install?
Q4. Both package.json and package-lock.json list express. On npm install, which wins?
Q5. What's the difference between require("fs") and require("./fs")?
Q6. Which line crashes when run in Node.js?
Q7. Your handler runs res.write("hi") but never calls res.end(). The visitor's browserβ¦
Q8. In MVC, which part validates the user's input and decides which view to render?
15 Reading & practice
Zybooks β Chapter 10 "node.js": sections 10.1 β 10.3. Do the participation activities β they mirror this week's lab.
- Official Node.js guides
- Full Node.js API reference β every core module documented.
- The Node.js event loop, timers and nextTick
- Don't block the event loop
- jsv9000.app β step-by-step event loop visualizer.