Node.js Tutorial
Node.js is a JavaScript runtime built on Chrome's V8 engine that lets you run JavaScript on the server side. It's fast, non-blocking, and perfect for building APIs, real-time apps, and microservices.
What is Node.js?
- Created by Ryan Dahl in 2009. Uses Google's V8 JavaScript engine.
- Event-driven, non-blocking I/O - handles many connections concurrently.
- Great for: REST APIs, real-time chat (WebSockets), file servers, CLI tools, microservices.
- npm (Node Package Manager) - largest software registry in the world.
Terminal
# Check installed version
node --version # v20.x.x
npm --version # 10.x.x
# Run a script
node app.js
# Interactive REPL
node
Modules
JavaScript (Node)
// CommonJS (default in Node.js)
// math.js
const PI = 3.14159;
function add(a, b) { return a + b; }
module.exports = { PI, add };
// app.js
const { PI, add } = require("./math");
const path = require("path"); // built-in modules use strings, no ./
// ES Modules (add "type": "module" to package.json)
// math.mjs
export const PI = 3.14159;
export function add(a, b) { return a + b; }
export default function main() {}
// app.mjs
import main, { PI, add } from "./math.mjs";
import { readFile } from "fs/promises"; // named import from built-in
npm & package.json
Terminal / JSON
# Initialize project
npm init -y
# Install package
npm install express
npm install -D nodemon # dev dependency
npm install -g typescript # global
# Other commands
npm uninstall lodash
npm update
npm outdated
npm audit fix
npm run build
// package.json
{
"name": "my-api",
"version": "1.0.0",
"scripts": {
"start": "node src/index.js",
"dev": "nodemon src/index.js",
"test": "jest"
},
"dependencies": {
"express": "^4.18.0"
},
"devDependencies": {
"nodemon": "^3.0.0"
}
}
File System (fs)
JavaScript (Node)
const fs = require("fs");
const { readFile, writeFile, appendFile, mkdir, readdir } = require("fs/promises");
const path = require("path");
// Async (Promises - recommended)
async function main() {
// Read file
const content = await readFile("./README.md", "utf-8");
console.log(content);
// Write file (creates/overwrites)
await writeFile("./output.txt", "Hello Node!\n", "utf-8");
// Append
await appendFile("./log.txt", `[${new Date().toISOString()}] Event\n`);
// Create directory
await mkdir("./uploads", { recursive: true });
// List directory
const files = await readdir("./");
console.log(files);
// Check if file exists
try { await fs.promises.access("./file.txt"); console.log("exists"); }
catch { console.log("not found"); }
}
// Path utilities
path.join(__dirname, "data", "file.json"); // OS-safe path
path.resolve("./src/index.js"); // absolute
path.basename("/path/to/file.txt"); // "file.txt"
path.extname("file.txt"); // ".txt"
path.dirname("/a/b/c.js"); // "/a/b"
Express.js
JavaScript (Node)
const express = require("express");
const app = express();
// Built-in middleware
app.use(express.json()); // parse JSON body
app.use(express.urlencoded({ extended: true })); // parse form data
app.use(express.static("public")); // serve static files
// Routes
app.get("/", (req, res) => {
res.send("Hello World!");
});
app.get("/about", (req, res) => {
res.sendFile(__dirname + "/public/about.html");
});
// Query params: GET /search?q=laptop&page=2
app.get("/search", (req, res) => {
const { q, page = 1 } = req.query;
res.json({ query: q, page: Number(page) });
});
// Route params: GET /users/42
app.get("/users/:id", (req, res) => {
const { id } = req.params;
res.json({ userId: id });
});
// POST with JSON body
app.post("/users", (req, res) => {
const { name, email } = req.body;
// validate, save to DB...
res.status(201).json({ id: 1, name, email });
});
// Start server
app.listen(3000, () => console.log("Server running on http://localhost:3000"));
Building a REST API
JavaScript (Node)
// routes/products.js - Router module
const router = require("express").Router();
// In-memory store (replace with DB in production)
let products = [
{ id: 1, name: "Laptop", price: 80000 },
{ id: 2, name: "Keyboard", price: 2500 }
];
let nextId = 3;
// GET /api/products
router.get("/", (req, res) => res.json(products));
// GET /api/products/:id
router.get("/:id", (req, res) => {
const p = products.find(p => p.id === Number(req.params.id));
if (!p) return res.status(404).json({ error: "Not found" });
res.json(p);
});
// POST /api/products
router.post("/", (req, res) => {
const { name, price } = req.body;
if (!name || !price) return res.status(400).json({ error: "name and price required" });
const product = { id: nextId++, name, price: Number(price) };
products.push(product);
res.status(201).json(product);
});
// PUT /api/products/:id
router.put("/:id", (req, res) => {
const idx = products.findIndex(p => p.id === Number(req.params.id));
if (idx === -1) return res.status(404).json({ error: "Not found" });
products[idx] = { ...products[idx], ...req.body, id: products[idx].id };
res.json(products[idx]);
});
// DELETE /api/products/:id
router.delete("/:id", (req, res) => {
products = products.filter(p => p.id !== Number(req.params.id));
res.status(204).end();
});
module.exports = router;
// app.js - mount router
app.use("/api/products", require("./routes/products"));
JWT Authentication
JavaScript (Node)
// npm install jsonwebtoken bcryptjs
const jwt = require("jsonwebtoken");
const bcrypt = require("bcryptjs");
const SECRET = process.env.JWT_SECRET; // store in .env!
// Register - hash password
app.post("/auth/register", async (req, res) => {
const { email, password } = req.body;
const hashed = await bcrypt.hash(password, 12);
const user = await User.create({ email, password: hashed });
res.status(201).json({ id: user.id, email });
});
// Login - issue token
app.post("/auth/login", async (req, res) => {
const { email, password } = req.body;
const user = await User.findOne({ email });
if (!user || !(await bcrypt.compare(password, user.password)))
return res.status(401).json({ error: "Invalid credentials" });
const token = jwt.sign({ userId: user.id, role: user.role }, SECRET, { expiresIn: "7d" });
res.json({ token });
});
// Auth middleware
function requireAuth(req, res, next) {
const auth = req.headers.authorization;
if (!auth?.startsWith("Bearer ")) return res.status(401).json({ error: "No token" });
try {
req.user = jwt.verify(auth.slice(7), SECRET);
next();
} catch { res.status(401).json({ error: "Invalid token" }); }
}
// Protected route
app.get("/api/profile", requireAuth, (req, res) => {
res.json({ userId: req.user.userId });
});
Environment Variables
bash / JavaScript
# .env file (NEVER commit to git)
PORT=3000
DB_URL=mongodb://localhost:27017/myapp
JWT_SECRET=super_secret_key_change_this
NODE_ENV=development
# .gitignore
.env
node_modules/
// Load with dotenv (npm install dotenv)
require("dotenv").config(); // CommonJS (at top of entry file)
// or
import "dotenv/config"; // ESM
const PORT = process.env.PORT || 3000;
app.listen(PORT, () => console.log(`Server on port ${PORT}`));
Async Patterns
Node.js apps spend a lot of time waiting for disk, network, and database operations. Learn to structure async code clearly.
Node.js
import fs from 'node:fs/promises';
async function loadConfig() {
try {
const text = await fs.readFile('./config.json', 'utf8');
return JSON.parse(text);
} catch (error) {
console.error('Could not load config:', error.message);
throw error;
}
}
Validation & Security
- Validate request bodies before business logic runs.
- Never trust IDs, emails, or roles coming from the client.
- Store secrets in environment variables, not source files.
- Use rate limiting, secure headers, and input validation in public APIs.
- Hash passwords with bcrypt or argon2, never plain text.
Testing & Logging
Production backends need tests and logs. Tests protect behavior; logs help you understand failures after deployment.
Node.js
app.get('/health', (req, res) => {
console.info('Health check requested');
res.json({ ok: true, uptime: process.uptime() });
});