Node.js lets you run JavaScript outside the browser — on servers, in the terminal, and anywhere you need backend code. If you already know some JavaScript, Node.js is your path to building web servers, APIs, and command-line tools. If you're brand new, this tutorial covers everything from installation to building real projects.
What you'll learn
| Topic | What you'll be able to do |
|---|---|
| Setup | Install Node.js and run your first script |
| Modules | Organize code with require and import |
| npm | Install and manage packages |
| File system | Read and write files |
| HTTP & Express | Build web servers and REST APIs |
| Async programming | Handle async code with callbacks, Promises, async/await |
| Real projects | Build a CLI tool, REST API, and file processor |
Why Node.js?
| Use case | Example |
|---|---|
| Web servers | Express, Fastify, Hono |
| REST APIs | JSON APIs for mobile/web apps |
| Real-time apps | Chat, live notifications (Socket.io) |
| CLI tools | Scripts, automation, build tools |
| Microservices | Lightweight services at scale |
| Serverless | AWS Lambda, Vercel functions |
| Job market | ~300k+ Node.js jobs worldwide |
Node.js vs other backend languages
| Node.js | Python | Go | Java | |
|---|---|---|---|---|
| Language | JavaScript | Python | Go | Java |
| Speed | Fast (V8 engine) | Moderate | Very fast | Fast |
| Async model | Event loop, non-blocking | Threads / asyncio | Goroutines | Threads |
| Best for | APIs, real-time, microservices | Data science, scripts | Systems, CLIs | Enterprise |
| Learning curve | Low (if you know JS) | Low | Medium | High |
| npm packages | 2M+ | PyPI 500k+ | Limited | Maven Central 550k+ |
1. Installation and Setup
Install Node.js
Go to nodejs.org and download the LTS version (Long Term Support — stable and recommended).
Verify installation:
node --version # v22.x.x (or similar)
npm --version # 10.x.x (comes bundled with Node)
Your first Node.js script
Create a file hello.js:
console.log("Hello, Node.js!");
console.log("Node version:", process.version);
console.log("Platform:", process.platform);
Run it:
node hello.js
# Hello, Node.js!
# Node version: v22.6.0
# Platform: linux
That's it — JavaScript running outside the browser.
The REPL (interactive shell)
node
> 2 + 2
4
> "hello".toUpperCase()
'HELLO'
> .exit
2. Node.js vs Browser JavaScript
Node.js is JavaScript, but some things are different:
| Feature | Browser | Node.js |
|---|---|---|
window / document |
✅ | ❌ (no DOM) |
process object |
❌ | ✅ |
| File system access | ❌ | ✅ (fs module) |
| Network (raw TCP) | Limited | ✅ (net module) |
fetch |
✅ | ✅ (built-in since Node 18) |
| ES modules | ✅ | ✅ (plus CommonJS require) |
console.log |
✅ | ✅ |
3. Modules
Node.js has two module systems. CommonJS (older, still widely used) and ES Modules (modern, same as browsers).
CommonJS (require / module.exports)
// math.js
function add(a, b) {
return a + b;
}
function multiply(a, b) {
return a * b;
}
module.exports = { add, multiply };
// main.js
const { add, multiply } = require("./math");
console.log(add(3, 4)); // 7
console.log(multiply(3, 4)); // 12
ES Modules (import / export)
Add "type": "module" to package.json, or use .mjs file extension.
// math.mjs
export function add(a, b) {
return a + b;
}
export function multiply(a, b) {
return a * b;
}
// main.mjs
import { add, multiply } from "./math.mjs";
console.log(add(3, 4)); // 7
console.log(multiply(3, 4)); // 12
CommonJS vs ES Modules
| CommonJS | ES Modules | |
|---|---|---|
| Syntax | require() / module.exports |
import / export |
| Loading | Synchronous | Asynchronous |
| File extension | .js (default) |
.mjs or "type": "module" |
| Dynamic import | Supported | import() function |
| Tree-shaking | ❌ | ✅ (bundlers can optimize) |
| When to use | Legacy projects, Node scripts | Modern apps, bundlers |
Built-in modules
Node.js ships with many useful modules — no installation needed:
| Module | Purpose | Example |
|---|---|---|
fs |
File system | Read/write files |
path |
Path manipulation | Join, resolve paths |
http |
HTTP server | Create raw web server |
https |
HTTPS support | Secure connections |
os |
Operating system info | Platform, CPU, memory |
events |
Event emitter | Custom events |
stream |
Streams | Large data processing |
crypto |
Cryptography | Hashing, encryption |
child_process |
Run system commands | exec, spawn |
url |
URL parsing | Parse and format URLs |
util |
Utilities | promisify, inspect |
net |
TCP/socket | Raw TCP connections |
4. npm (Node Package Manager)
npm is the world's largest software registry with 2M+ packages.
Initialize a project
mkdir my-project
cd my-project
npm init -y # creates package.json with defaults
package.json
{
"name": "my-project",
"version": "1.0.0",
"description": "My Node.js project",
"main": "index.js",
"scripts": {
"start": "node index.js",
"dev": "node --watch index.js"
},
"dependencies": {},
"devDependencies": {}
}
Installing packages
npm install express # install and save to dependencies
npm install -D nodemon # install as devDependency
npm install # install all dependencies from package.json
npm uninstall express # remove package
npm update # update packages
npm list # list installed packages
npm scripts
npm start # runs "start" script in package.json
npm run dev # runs "dev" script
npm test # runs "test" script
Key concepts
| Term | Meaning |
|---|---|
dependencies |
Packages needed to run the app |
devDependencies |
Packages only needed during development |
node_modules/ |
Where packages are installed (never commit this) |
package-lock.json |
Exact versions of every installed package |
.npmrc |
npm configuration file |
Always add node_modules/ to .gitignore:
node_modules/
.env
5. File System (fs module)
The fs module lets you read, write, and manage files.
Read a file
const fs = require("fs");
// Synchronous (blocks execution)
const content = fs.readFileSync("file.txt", "utf8");
console.log(content);
// Asynchronous (recommended)
fs.readFile("file.txt", "utf8", (err, data) => {
if (err) throw err;
console.log(data);
});
Write a file
const fs = require("fs");
// Write (creates or overwrites)
fs.writeFileSync("output.txt", "Hello, file!");
// Append
fs.appendFileSync("output.txt", "\nNew line");
// Async version
fs.writeFile("output.txt", "Hello!", (err) => {
if (err) throw err;
console.log("File written");
});
fs/promises (modern async approach)
const fs = require("fs/promises");
async function main() {
// Read
const content = await fs.readFile("file.txt", "utf8");
console.log(content);
// Write
await fs.writeFile("output.txt", "Hello!");
// Read directory
const files = await fs.readdir("./");
console.log(files);
// Check if file exists
try {
await fs.access("file.txt");
console.log("File exists");
} catch {
console.log("File not found");
}
}
main();
Path module
const path = require("path");
path.join("folder", "subfolder", "file.txt"); // folder/subfolder/file.txt
path.resolve("./file.txt"); // absolute path
path.extname("file.txt"); // .txt
path.basename("/path/to/file.txt"); // file.txt
path.dirname("/path/to/file.txt"); // /path/to
path.parse("/path/to/file.txt");
// { root: '/', dir: '/path/to', base: 'file.txt', ext: '.txt', name: 'file' }
6. Async Programming
Node.js is built around asynchronous, non-blocking I/O. Understanding async patterns is essential.
The event loop (simplified)
Node.js is single-threaded but handles many concurrent operations via the event loop:
- JavaScript runs synchronously
- Async operations (file I/O, network) are offloaded to the OS
- When complete, callbacks are queued
- Event loop picks them up and runs them
This means Node.js can handle thousands of concurrent connections without threads.
Callbacks (old style)
const fs = require("fs");
fs.readFile("a.txt", "utf8", (err, dataA) => {
if (err) return console.error(err);
fs.readFile("b.txt", "utf8", (err, dataB) => {
if (err) return console.error(err);
console.log(dataA + dataB);
// This "callback hell" nesting is why Promises exist
});
});
Promises
const fs = require("fs/promises");
fs.readFile("a.txt", "utf8")
.then((dataA) => {
return fs.readFile("b.txt", "utf8").then((dataB) => dataA + dataB);
})
.then((combined) => console.log(combined))
.catch((err) => console.error(err));
// Parallel reads (both start at the same time)
Promise.all([
fs.readFile("a.txt", "utf8"),
fs.readFile("b.txt", "utf8"),
]).then(([dataA, dataB]) => {
console.log(dataA + dataB);
});
async/await (recommended)
const fs = require("fs/promises");
async function readBothFiles() {
try {
// Sequential
const dataA = await fs.readFile("a.txt", "utf8");
const dataB = await fs.readFile("b.txt", "utf8");
console.log(dataA + dataB);
// Parallel (faster)
const [fileA, fileB] = await Promise.all([
fs.readFile("a.txt", "utf8"),
fs.readFile("b.txt", "utf8"),
]);
console.log(fileA + fileB);
} catch (err) {
console.error("Error:", err.message);
}
}
readBothFiles();
util.promisify
Convert old callback-style functions to Promises:
const { promisify } = require("util");
const fs = require("fs");
const readFile = promisify(fs.readFile);
async function main() {
const content = await readFile("file.txt", "utf8");
console.log(content);
}
7. HTTP Module (raw server)
const http = require("http");
const server = http.createServer((req, res) => {
const { method, url } = req;
if (method === "GET" && url === "/") {
res.writeHead(200, { "Content-Type": "text/html" });
res.end("<h1>Hello, Node.js!</h1>");
} else if (method === "GET" && url === "/api/hello") {
res.writeHead(200, { "Content-Type": "application/json" });
res.end(JSON.stringify({ message: "Hello from Node.js API" }));
} else {
res.writeHead(404, { "Content-Type": "application/json" });
res.end(JSON.stringify({ error: "Not found" }));
}
});
server.listen(3000, () => {
console.log("Server running at http://localhost:3000");
});
The raw http module works, but for real apps you'll use Express.
8. Express.js (web framework)
Express is the most popular Node.js web framework — minimal, flexible, and widely used.
Install Express
npm install express
Hello World with Express
const express = require("express");
const app = express();
app.use(express.json()); // parse JSON request bodies
app.get("/", (req, res) => {
res.send("Hello, Express!");
});
app.listen(3000, () => {
console.log("Server running on http://localhost:3000");
});
Route methods
app.get("/users", (req, res) => { ... }); // GET
app.post("/users", (req, res) => { ... }); // POST
app.put("/users/:id", (req, res) => { ... }); // PUT
app.patch("/users/:id", (req, res) => { ... }); // PATCH
app.delete("/users/:id", (req, res) => { ... }); // DELETE
Route parameters and query strings
// Route parameter: /users/42
app.get("/users/:id", (req, res) => {
const { id } = req.params; // "42"
res.json({ id, name: "Alice" });
});
// Query string: /search?q=node&limit=10
app.get("/search", (req, res) => {
const { q, limit = 20 } = req.query;
res.json({ query: q, limit: Number(limit) });
});
Request body
// POST /users with body: { "name": "Alice", "email": "alice@example.com" }
app.post("/users", (req, res) => {
const { name, email } = req.body;
if (!name || !email) {
return res.status(400).json({ error: "name and email required" });
}
const user = { id: Date.now(), name, email };
res.status(201).json(user);
});
Middleware
Middleware functions run between the request and the response:
// Logging middleware
function logger(req, res, next) {
console.log(`${req.method} ${req.url} - ${new Date().toISOString()}`);
next(); // call next() to continue
}
// Error handling middleware (4 parameters)
function errorHandler(err, req, res, next) {
console.error(err.stack);
res.status(500).json({ error: "Internal server error" });
}
app.use(logger); // apply to all routes
app.get("/", (req, res) => {
res.send("Hello!");
});
app.use(errorHandler); // must be last
Common middleware packages
npm install cors helmet morgan
const cors = require("cors");
const helmet = require("helmet");
const morgan = require("morgan");
app.use(cors()); // enable CORS for all origins
app.use(helmet()); // set security HTTP headers
app.use(morgan("combined")); // HTTP request logger
9. Building a REST API
A complete CRUD REST API for a todo list (in-memory, no database):
const express = require("express");
const app = express();
app.use(express.json());
// In-memory "database"
let todos = [
{ id: 1, title: "Learn Node.js", done: false },
{ id: 2, title: "Build an API", done: false },
];
let nextId = 3;
// GET /todos — list all
app.get("/todos", (req, res) => {
res.json(todos);
});
// GET /todos/:id — get one
app.get("/todos/:id", (req, res) => {
const todo = todos.find((t) => t.id === Number(req.params.id));
if (!todo) return res.status(404).json({ error: "Not found" });
res.json(todo);
});
// POST /todos — create
app.post("/todos", (req, res) => {
const { title } = req.body;
if (!title) return res.status(400).json({ error: "title is required" });
const todo = { id: nextId++, title, done: false };
todos.push(todo);
res.status(201).json(todo);
});
// PUT /todos/:id — update
app.put("/todos/:id", (req, res) => {
const index = todos.findIndex((t) => t.id === Number(req.params.id));
if (index === -1) return res.status(404).json({ error: "Not found" });
const { title, done } = req.body;
todos[index] = { ...todos[index], ...(title && { title }), ...(done !== undefined && { done }) };
res.json(todos[index]);
});
// DELETE /todos/:id — delete
app.delete("/todos/:id", (req, res) => {
const index = todos.findIndex((t) => t.id === Number(req.params.id));
if (index === -1) return res.status(404).json({ error: "Not found" });
todos.splice(index, 1);
res.status(204).send();
});
app.listen(3000, () => console.log("API running on http://localhost:3000"));
Test with curl:
curl http://localhost:3000/todos
curl -X POST http://localhost:3000/todos -H "Content-Type: application/json" -d '{"title":"Buy groceries"}'
curl -X PUT http://localhost:3000/todos/3 -H "Content-Type: application/json" -d '{"done":true}'
curl -X DELETE http://localhost:3000/todos/3
10. Environment Variables
Never hardcode secrets. Use environment variables.
# .env file (never commit this)
PORT=3000
DATABASE_URL=postgres://localhost:5432/mydb
API_KEY=secret123
npm install dotenv
require("dotenv").config(); // load .env file
const port = process.env.PORT || 3000;
const dbUrl = process.env.DATABASE_URL;
console.log(`Listening on port ${port}`);
Access environment variables:
process.env.NODE_ENV // "development", "production", "test"
process.env.PORT // "3000" (always a string)
process.env.MY_SECRET // your secret value
11. Error Handling
Synchronous errors
try {
const data = JSON.parse("invalid json");
} catch (err) {
console.error("Parse error:", err.message);
}
Async errors with async/await
async function fetchUser(id) {
const res = await fetch(`https://api.example.com/users/${id}`);
if (!res.ok) {
throw new Error(`HTTP error: ${res.status}`);
}
return res.json();
}
// Always wrap await in try/catch
async function main() {
try {
const user = await fetchUser(1);
console.log(user);
} catch (err) {
console.error("Failed to fetch user:", err.message);
}
}
Express async error handling
Express doesn't catch async errors automatically. Use a wrapper or pass errors to next:
// Option 1: try/catch in every route
app.get("/users/:id", async (req, res) => {
try {
const user = await getUserById(req.params.id);
res.json(user);
} catch (err) {
res.status(500).json({ error: err.message });
}
});
// Option 2: async wrapper function
const asyncHandler = (fn) => (req, res, next) => {
Promise.resolve(fn(req, res, next)).catch(next);
};
app.get("/users/:id", asyncHandler(async (req, res) => {
const user = await getUserById(req.params.id);
res.json(user);
}));
// Global error handler (add at the end)
app.use((err, req, res, next) => {
console.error(err.stack);
res.status(500).json({ error: "Internal server error" });
});
Unhandled promise rejections
process.on("unhandledRejection", (reason, promise) => {
console.error("Unhandled rejection:", reason);
process.exit(1);
});
process.on("uncaughtException", (err) => {
console.error("Uncaught exception:", err);
process.exit(1);
});
12. Working with JSON
JSON is the primary data format for Node.js APIs.
// Parse JSON string → JavaScript object
const jsonString = '{"name":"Alice","age":30}';
const user = JSON.parse(jsonString);
console.log(user.name); // Alice
// Serialize JavaScript object → JSON string
const obj = { name: "Bob", age: 25, tags: ["dev", "node"] };
const json = JSON.stringify(obj); // compact
const pretty = JSON.stringify(obj, null, 2); // pretty-printed
// Read JSON file
const fs = require("fs");
const config = JSON.parse(fs.readFileSync("config.json", "utf8"));
// Write JSON file
fs.writeFileSync("output.json", JSON.stringify(data, null, 2));
13. Streams
Streams are for processing large data efficiently — without loading everything into memory.
const fs = require("fs");
const { Transform } = require("stream");
// Read a large file line by line
const readline = require("readline");
const rl = readline.createInterface({
input: fs.createReadStream("large-file.txt"),
crlfDelay: Infinity,
});
rl.on("line", (line) => {
console.log("Line:", line);
});
rl.on("close", () => {
console.log("Done reading");
});
// Pipe streams (read → transform → write)
const upperCase = new Transform({
transform(chunk, encoding, callback) {
callback(null, chunk.toString().toUpperCase());
},
});
fs.createReadStream("input.txt")
.pipe(upperCase)
.pipe(fs.createWriteStream("output.txt"));
14. Events (EventEmitter)
Node.js is built on the event-driven architecture using EventEmitter.
const { EventEmitter } = require("events");
const emitter = new EventEmitter();
// Listen for an event
emitter.on("data", (payload) => {
console.log("Received data:", payload);
});
emitter.once("connect", () => {
console.log("Connected! (fires only once)");
});
// Emit events
emitter.emit("connect");
emitter.emit("data", { user: "Alice", action: "login" });
emitter.emit("data", { user: "Bob", action: "logout" });
// Custom class extending EventEmitter
class FileWatcher extends EventEmitter {
constructor(filename) {
super();
this.filename = filename;
}
start() {
const fs = require("fs");
fs.watchFile(this.filename, (curr, prev) => {
if (curr.mtime !== prev.mtime) {
this.emit("change", this.filename);
}
});
}
}
const watcher = new FileWatcher("config.json");
watcher.on("change", (file) => console.log(`${file} changed`));
watcher.start();
15. Making HTTP Requests
Using built-in fetch (Node.js 18+)
// GET request
const res = await fetch("https://jsonplaceholder.typicode.com/posts/1");
const post = await res.json();
console.log(post.title);
// POST request
const response = await fetch("https://jsonplaceholder.typicode.com/posts", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ title: "Hello", body: "World", userId: 1 }),
});
const created = await response.json();
console.log(created.id);
Using axios (popular npm package)
npm install axios
const axios = require("axios");
// GET
const { data } = await axios.get("https://api.example.com/users");
console.log(data);
// POST
const response = await axios.post("https://api.example.com/users", {
name: "Alice",
email: "alice@example.com",
});
console.log(response.data);
// With headers and error handling
try {
const { data } = await axios.get("https://api.example.com/protected", {
headers: { Authorization: `Bearer ${process.env.API_TOKEN}` },
});
console.log(data);
} catch (err) {
if (err.response) {
console.error("HTTP error:", err.response.status);
} else {
console.error("Network error:", err.message);
}
}
16. Project Structure
A well-organized Node.js/Express project:
my-api/
├── src/
│ ├── routes/
│ │ ├── users.js # /users routes
│ │ └── todos.js # /todos routes
│ ├── controllers/
│ │ ├── users.js # business logic for users
│ │ └── todos.js # business logic for todos
│ ├── middleware/
│ │ ├── auth.js # authentication middleware
│ │ └── errorHandler.js
│ ├── models/
│ │ └── user.js # data models / schemas
│ └── app.js # express app setup (no listen)
├── index.js # entry point (app.listen)
├── .env # environment variables
├── .gitignore
└── package.json
// src/app.js
const express = require("express");
const cors = require("cors");
const userRoutes = require("./routes/users");
const todoRoutes = require("./routes/todos");
const errorHandler = require("./middleware/errorHandler");
const app = express();
app.use(cors());
app.use(express.json());
app.use("/api/users", userRoutes);
app.use("/api/todos", todoRoutes);
app.use(errorHandler);
module.exports = app;
// index.js
const app = require("./src/app");
const PORT = process.env.PORT || 3000;
app.listen(PORT, () => {
console.log(`Server running on http://localhost:${PORT}`);
});
17. Three Beginner Projects
Project 1: CLI Todo Manager
// todo-cli.js
const fs = require("fs");
const path = require("path");
const DB_FILE = path.join(__dirname, "todos.json");
function loadTodos() {
if (!fs.existsSync(DB_FILE)) return [];
return JSON.parse(fs.readFileSync(DB_FILE, "utf8"));
}
function saveTodos(todos) {
fs.writeFileSync(DB_FILE, JSON.stringify(todos, null, 2));
}
const [,, command, ...args] = process.argv;
const todos = loadTodos();
switch (command) {
case "add": {
const title = args.join(" ");
if (!title) { console.log("Usage: node todo-cli.js add <title>"); break; }
const todo = { id: Date.now(), title, done: false };
todos.push(todo);
saveTodos(todos);
console.log(`Added: ${title}`);
break;
}
case "list": {
if (todos.length === 0) { console.log("No todos yet!"); break; }
todos.forEach((t, i) => {
console.log(`${i + 1}. [${t.done ? "x" : " "}] ${t.title}`);
});
break;
}
case "done": {
const index = Number(args[0]) - 1;
if (!todos[index]) { console.log("Todo not found"); break; }
todos[index] = { ...todos[index], done: true };
saveTodos(todos);
console.log(`Done: ${todos[index].title}`);
break;
}
case "delete": {
const index = Number(args[0]) - 1;
if (!todos[index]) { console.log("Todo not found"); break; }
const [removed] = todos.splice(index, 1);
saveTodos(todos);
console.log(`Deleted: ${removed.title}`);
break;
}
default:
console.log("Commands: add <title> | list | done <n> | delete <n>");
}
Usage:
node todo-cli.js add "Learn Node.js"
node todo-cli.js add "Build an API"
node todo-cli.js list
node todo-cli.js done 1
node todo-cli.js delete 2
Project 2: REST API with Express and File Storage
// server.js
const express = require("express");
const fs = require("fs");
const app = express();
app.use(express.json());
const DB_FILE = "notes.json";
function readNotes() {
if (!fs.existsSync(DB_FILE)) return [];
return JSON.parse(fs.readFileSync(DB_FILE, "utf8"));
}
function writeNotes(notes) {
fs.writeFileSync(DB_FILE, JSON.stringify(notes, null, 2));
}
// List all notes
app.get("/notes", (req, res) => {
res.json(readNotes());
});
// Get one note
app.get("/notes/:id", (req, res) => {
const notes = readNotes();
const note = notes.find((n) => n.id === req.params.id);
if (!note) return res.status(404).json({ error: "Not found" });
res.json(note);
});
// Create note
app.post("/notes", (req, res) => {
const { title, content } = req.body;
if (!title) return res.status(400).json({ error: "title required" });
const notes = readNotes();
const note = {
id: Date.now().toString(),
title,
content: content || "",
createdAt: new Date().toISOString(),
};
notes.push(note);
writeNotes(notes);
res.status(201).json(note);
});
// Update note
app.put("/notes/:id", (req, res) => {
const notes = readNotes();
const index = notes.findIndex((n) => n.id === req.params.id);
if (index === -1) return res.status(404).json({ error: "Not found" });
notes[index] = { ...notes[index], ...req.body, id: notes[index].id };
writeNotes(notes);
res.json(notes[index]);
});
// Delete note
app.delete("/notes/:id", (req, res) => {
const notes = readNotes();
const index = notes.findIndex((n) => n.id === req.params.id);
if (index === -1) return res.status(404).json({ error: "Not found" });
notes.splice(index, 1);
writeNotes(notes);
res.status(204).send();
});
app.listen(3000, () => console.log("Notes API on http://localhost:3000"));
Project 3: File Processing Script
Process a CSV file and output statistics:
// analyze-csv.js
const fs = require("fs");
const readline = require("readline");
async function analyzeCsv(filename) {
const rl = readline.createInterface({
input: fs.createReadStream(filename),
crlfDelay: Infinity,
});
const rows = [];
let headers = null;
for await (const line of rl) {
if (!headers) {
headers = line.split(",").map((h) => h.trim());
} else {
const values = line.split(",").map((v) => v.trim());
const row = {};
headers.forEach((h, i) => {
row[h] = values[i] || "";
});
rows.push(row);
}
}
console.log(`Rows: ${rows.length}`);
console.log(`Columns: ${headers.join(", ")}`);
// Numeric column stats
headers.forEach((header) => {
const values = rows
.map((r) => parseFloat(r[header]))
.filter((v) => !isNaN(v));
if (values.length > 0) {
const sum = values.reduce((a, b) => a + b, 0);
const avg = sum / values.length;
const min = Math.min(...values);
const max = Math.max(...values);
console.log(`${header}: avg=${avg.toFixed(2)}, min=${min}, max=${max}`);
}
});
}
const file = process.argv[2];
if (!file) {
console.log("Usage: node analyze-csv.js <file.csv>");
process.exit(1);
}
analyzeCsv(file).catch(console.error);
Learning path
| Stage | Topics | Time |
|---|---|---|
| 1. Foundation | JavaScript basics, Node install, REPL | Week 1 |
| 2. Core Node | Modules, fs, path, environment vars | Week 2 |
| 3. Async | Callbacks → Promises → async/await | Week 2-3 |
| 4. Express | Routing, middleware, REST API | Week 3-4 |
| 5. Data | JSON, file storage, then a real DB (SQLite/PostgreSQL) | Month 2 |
| 6. Production | Error handling, logging, env config, Docker | Month 2-3 |
| 7. Advanced | Streams, EventEmitter, testing, auth (JWT) | Month 3-4 |
Common mistakes
| Mistake | Problem | Fix |
|---|---|---|
Forgetting await |
Promise returned instead of value | Add await before async calls |
| No error handling in async routes | Unhandled rejection crashes server | Use try/catch or asyncHandler wrapper |
Committing node_modules/ |
Huge repo, unnecessary | Add to .gitignore |
Committing .env |
Exposes secrets | Add to .gitignore, use .env.example |
require path without ./ |
Looks for npm package, not local file | Always require("./myfile") for local |
Calling res.json() twice |
"Cannot set headers after they are sent" | return res.json(...) to stop execution |
| Synchronous fs in production | Blocks event loop | Use async fs/promises or callbacks |
| No process.env validation | Silent missing config | Check required env vars at startup |
Node.js vs related technologies
| Term | What it is |
|---|---|
| Node.js | JavaScript runtime (V8 engine + I/O APIs) |
| npm | Default package manager bundled with Node |
| pnpm | Faster alternative package manager |
| Bun | Faster JS runtime, drop-in Node alternative |
| Deno | Secure JS runtime by Node's creator, TypeScript first |
| Express | Minimal Node.js web framework |
| Fastify | Fast, schema-based Node.js web framework |
| NestJS | Opinionated Node.js framework (Angular-style) |
| Next.js | React framework with Node.js backend (App Router) |
| V8 | JavaScript engine (from Chrome) that powers Node.js |
Frequently asked questions
Do I need to know JavaScript before learning Node.js?
Yes — Node.js is JavaScript. Learn JS basics first (variables, functions, arrays, objects, async/await). Then Node.js becomes straightforward.
Node.js vs Python for backend — which should I learn?
If you already know JavaScript, Node.js is the obvious choice. Python is better if you're interested in data science or ML alongside backend. Both are excellent for web APIs.
Is Node.js good for production?
Yes. Node.js powers Netflix, PayPal, LinkedIn, Uber, and millions of other production apps. It excels at I/O-heavy workloads (APIs, real-time, microservices).
When should I use Express vs NestJS vs Fastify?
Express: learning, small projects, maximum flexibility. Fastify: performance-critical APIs with schema validation. NestJS: large teams, enterprise projects, Angular-like structure with TypeScript.
What database works best with Node.js?
All major databases have Node.js drivers. Common choices: PostgreSQL (pg / Prisma / Drizzle), MongoDB (mongoose), SQLite (better-sqlite3 for scripts), Redis (ioredis for caching).
How do I deploy a Node.js app?
Options: Railway, Render, or Fly.io (easiest), Heroku, DigitalOcean App Platform, AWS EC2/ECS/Lambda, Google Cloud Run, or any VPS with PM2 as a process manager.