Node.js Complete Tutorial: Beginner to Advanced (55 Topics with Code) — a practical guide to Node.js complete tutorial with clear examples you can reuse in real projects.
This complete Node.js tutorial covers 55 topics — from installation and core modules to Express, MongoDB, JWT auth, and real projects. It is written as a beginner-friendly companion to the video course:
Watch the full Node.js course on YouTube
Course roadmap (all topics)
- Node.js Course Introduction and Roadmap
- What is Node.js?
- How Node.js is Used for Backend
- Node.js Installation
- First Node.js Program
- Important Points in Node.js
- Node.js Architecture
- JavaScript Fundamentals in Node.js
- Node.js Modules
- Core Module FS (File System)
- Core Module OS in Node.js
- FS Module Synchronous Methods
- FS Module Asynchronous Methods
- FS Promises with Then and Catch
- FS Promises with Async and Await
- Events Module and EventEmitter in Node.js
- JavaScript vs Node.js
- package.json and npm Commands
- External Packages in Node.js
- __dirname, __filename, require, and process
- HTTP Module in Node.js
- Nodemon Package in Node.js
- Understanding Server Response in Node.js
- Create API and Testing in Node.js
- Callbacks and Callback Hell in Node.js
- HTTP and HTTPS Modules in Detail
- Fetching Data from APIs in Node.js
- CommonJS vs ES Modules in Node.js
- Weather App Using External API in Node.js
- Todo App with Node.js
- CLI Quiz App Using Node.js
- Currency Converter Project in Node.js
- URL Shortener Project in Node.js
- Express.js Introduction
- Render HTML Login Form with POST in Express
- 404 Page Implementation in Express.js
- Using CSS in Express.js
- GET and POST API in Express.js
- PUT and DELETE API in Express.js
- Middleware in Express.js
- Simple Notes API Project with Express
- Node.js and Express Interview Questions
- EJS Template Engine in Express
- Blog Management Project with Node.js and Express
- MongoDB with Node.js
- Mongoose in Node.js
- MongoDB Atlas with Node.js
- File Upload Using Multer in Node.js
- Environment Variables in Node.js
- Cookies in Node.js and Express
- Sessions in Node.js and Express
- Authentication with JWT in Node.js
- Fix CORS Issue in Node.js
- Send Email with Node.js
- Final Todo Project with Node.js and Express
1. Node.js Course Introduction and Roadmap
This series follows a practical Node.js path: core modules, HTTP APIs, Express, MongoDB, auth, and mini projects. Use it as a written companion to the video course.
- Learn Node.js fundamentals and core modules first.
- Build small APIs with the HTTP module, then move to Express.
- Add MongoDB, auth (JWT), file uploads, and finish with projects.
Suggested learning order
1. Node basics + modules
2. FS / OS / Events / HTTP
3. npm + Express APIs
4. MongoDB + Mongoose
5. JWT auth + real projects
2. What is Node.js?
Node.js lets you run JavaScript outside the browser. It is event-driven and non-blocking, which makes it great for APIs, real-time apps, CLIs, and microservices.
- Remember: Node.js is a runtime, not a framework.
- It uses the V8 engine to execute JavaScript on the server.
- Use it for backend APIs, tooling, and streaming apps.
Quick mental model
// Browser JS -> DOM, window, document
// Node.js JS -> filesystem, network, process, modules
console.log('Node runs JavaScript on the server');
3. How Node.js is Used for Backend
On the backend, Node.js receives HTTP requests, talks to databases, validates input, and returns JSON or HTML. Frameworks like Express make this workflow cleaner.
- Client sends a request to your Node server.
- Server runs business logic and database queries.
- Server returns a response (JSON, HTML, file, etc.).
Typical backend flow
// Request -> Middleware -> Controller/Route -> DB -> Response
app.get('/api/users', async (req, res) => {
const users = await User.find();
res.json(users);
});
4. Node.js Installation
Download the LTS build from nodejs.org (or use nvm). Installation includes npm, which manages packages for your projects.
- Install Node.js LTS from the official site or nvm.
- Open a terminal and verify `node` and `npm`.
- Create a project folder and initialize npm.
Verify installation
node -v
npm -v
mkdir my-node-app && cd my-node-app
npm init -y
5. First Node.js Program
Create a `.js` file, write JavaScript, and run it with `node filename.js`. No browser is required.
- Create `app.js`.
- Add a simple console message.
- Run it with Node from the terminal.
app.js
const name = 'Imtiyaj';
console.log(`Hello, ${name}! Welcome to Node.js`);
// Run: node app.js
6. Important Points in Node.js
Node.js is single-threaded for your JS code, but handles many concurrent I/O operations through the event loop and libuv. Prefer non-blocking APIs for scalable servers.
- Avoid blocking the event loop with heavy sync work.
- Use modules to organize code.
- Rely on npm packages instead of reinventing common tools.
Non-blocking vs blocking mindset
// Prefer async I/O
const fs = require('fs');
fs.readFile('data.txt', 'utf8', (err, data) => {
if (err) throw err;
console.log(data);
});
7. Node.js Architecture
Node.js architecture combines Google’s V8 engine with libuv. Your JavaScript runs on V8; async tasks (files, network) are offloaded and completed through the event loop.
- JS code executes in V8.
- Async work is handled via libuv/thread pool where needed.
- Callbacks/promises resume when the event loop picks them up.
Event loop demo
console.log('1 sync');
setTimeout(() => console.log('2 timeout'), 0);
Promise.resolve().then(() => console.log('3 promise'));
console.log('4 sync');
8. JavaScript Fundamentals in Node.js
Strong JavaScript basics make Node.js easier. Focus on modern syntax: `const`/`let`, arrow functions, destructuring, promises, and async/await.
Modern JS used in Node
const add = (a, b) => a + b;
const user = { name: 'Asha', role: 'admin' };
const { name, role } = user;
async function load() {
const value = await Promise.resolve(42);
return value;
}
load().then(console.log);
9. Node.js Modules
Modules keep projects maintainable. Export functions/objects from one file and import them in another with `require` (CommonJS) or `import` (ESM).
math.js + app.js
// math.js
function sum(a, b) {
return a + b;
}
module.exports = { sum };
// app.js
const { sum } = require('./math');
console.log(sum(2, 3));
10. Core Module FS (File System)
`fs` is a core Node.js module for filesystem operations. You can use callback, sync, or promise-based APIs.
Write and read a file (callback)
const fs = require('fs');
fs.writeFile('hello.txt', 'Hello Node', (err) => {
if (err) throw err;
fs.readFile('hello.txt', 'utf8', (err, data) => {
if (err) throw err;
console.log(data);
});
});
11. Core Module OS in Node.js
The `os` module helps with logging, health checks, and environment-aware configuration by exposing host machine details.
OS info example
const os = require('os');
console.log({
platform: os.platform(),
arch: os.arch(),
cpus: os.cpus().length,
freeMem: os.freemem(),
totalMem: os.totalmem(),
hostname: os.hostname(),
uptime: os.uptime(),
});
12. FS Module Synchronous Methods
Sync methods like `readFileSync` block the event loop until finished. Fine for small CLI scripts; avoid them in high-traffic servers.
Synchronous FS
const fs = require('fs');
fs.writeFileSync('notes.txt', 'Learn Node.js');
const data = fs.readFileSync('notes.txt', 'utf8');
console.log(data);
13. FS Module Asynchronous Methods
Async fs methods accept a callback `(err, data)`. Errors come first — always check `err` before using the result.
Async FS with callback
const fs = require('fs');
fs.appendFile('log.txt', 'New linen', (err) => {
if (err) return console.error(err);
console.log('Appended');
});
14. FS Promises with Then and Catch
`fs.promises` returns Promises instead of using callbacks, which reads more linearly when chained with then/catch.
fs.promises + then/catch
const fs = require('fs').promises;
fs.writeFile('demo.txt', 'Promise style')
.then(() => fs.readFile('demo.txt', 'utf8'))
.then((data) => console.log(data))
.catch((err) => console.error(err));
15. FS Promises with Async and Await
Async/await is the preferred modern style for promise-based fs work. Wrap calls in try/catch for errors.
async/await FS
const fs = require('fs').promises;
async function main() {
try {
await fs.writeFile('demo.txt', 'Async await');
const data = await fs.readFile('demo.txt', 'utf8');
console.log(data);
} catch (err) {
console.error(err);
}
}
main();
16. Events Module and EventEmitter in Node.js
Many Node APIs are event-based. `EventEmitter` lets you emit named events and attach listeners with `.on()`.
Custom emitter
const EventEmitter = require('events');
const bus = new EventEmitter();
bus.on('user:created', (user) => {
console.log('Welcome', user.name);
});
bus.emit('user:created', { name: 'Ravi' });
17. JavaScript vs Node.js
JavaScript is the language. Node.js is a runtime that runs that language on the server with different global APIs than the browser.
Key differences
JavaScript (browser): DOM, fetch, window, localStorage
Node.js: fs, http, process, Buffer, path, no DOM
Same language: variables, functions, promises, classes
18. package.json and npm Commands
`package.json` stores project metadata, scripts, and dependency lists. npm installs packages into `node_modules` and locks versions with a lockfile.
Common npm commands
npm init -y
npm install express
npm install nodemon --save-dev
npm uninstall lodash
npm run start
package.json scripts
{
"name": "my-app",
"scripts": {
"start": "node server.js",
"dev": "nodemon server.js"
}
}
19. External Packages in Node.js
External packages extend Node.js quickly — HTTP frameworks, validators, date libraries, and more. Always review package quality and maintenance.
Using chalk (example package)
npm install chalk@4
Require and use
const chalk = require('chalk');
console.log(chalk.green('Installed package works!'));
20. __dirname, __filename, require, and process
In CommonJS, `__dirname` and `__filename` give the current file path. `require` loads modules. `process` exposes env vars, args, and exit controls.
Globals demo
const path = require('path');
console.log(__dirname);
console.log(__filename);
console.log(process.argv);
console.log(process.env.NODE_ENV);
console.log(path.join(__dirname, 'data', 'file.txt'));
21. HTTP Module in Node.js
The `http` core module can create servers without Express. It is the foundation for understanding how Node handles requests and responses.
Minimal HTTP server
const http = require('http');
const server = http.createServer((req, res) => {
res.writeHead(200, { 'Content-Type': 'text/plain' });
res.end('Hello from Node HTTP server');
});
server.listen(3000, () => console.log('http://localhost:3000'));
22. Nodemon Package in Node.js
Nodemon watches your project files and restarts the process when code changes — a big productivity boost while building APIs.
Install and run nodemon
npm install -D nodemon
npx nodemon server.js
# or in package.json: "dev": "nodemon server.js"
23. Understanding Server Response in Node.js
Every response has a status code (200, 404, 500…), headers (content type, cache), and a body (HTML/JSON/text). Set them intentionally.
JSON response with status
res.writeHead(201, { 'Content-Type': 'application/json' });
res.end(JSON.stringify({ ok: true, id: 12 }));
24. Create API and Testing in Node.js
A basic API inspects `req.url` and `req.method`, then returns JSON. Test endpoints with Postman, Thunder Client, curl, or the browser for GET.
Tiny JSON API
const http = require('http');
http.createServer((req, res) => {
if (req.url === '/api/health' && req.method === 'GET') {
res.writeHead(200, { 'Content-Type': 'application/json' });
return res.end(JSON.stringify({ status: 'ok' }));
}
res.writeHead(404, { 'Content-Type': 'application/json' });
res.end(JSON.stringify({ error: 'Not found' }));
}).listen(3000);
25. Callbacks and Callback Hell in Node.js
Callbacks are functions passed to async APIs. Deep nesting creates “callback hell.” Prefer promises or async/await for multi-step flows.
Callback hell vs async/await
// Hard to read
doA(() => {
doB(() => {
doC(() => console.log('done'));
});
});
// Better
async function run() {
await doA();
await doB();
await doC();
console.log('done');
}
26. HTTP and HTTPS Modules in Detail
`https` works like `http` but requires TLS certificates. In production, TLS is often terminated by a reverse proxy (Nginx), while Node still serves HTTP locally.
Read request URL and method
const http = require('http');
const url = require('url');
http.createServer((req, res) => {
const parsed = url.parse(req.url, true);
console.log(req.method, parsed.pathname, parsed.query);
res.end('ok');
}).listen(3000);
27. Fetching Data from APIs in Node.js
Modern Node versions include global `fetch`. Use it to consume third-party APIs, then process JSON for your app or CLI.
Fetch JSON
async function getPosts() {
const res = await fetch('https://jsonplaceholder.typicode.com/posts/1');
if (!res.ok) throw new Error('Request failed');
const data = await res.json();
console.log(data.title);
}
getPosts().catch(console.error);
28. CommonJS vs ES Modules in Node.js
CommonJS (`require`) is classic Node style. ES Modules (`import`/`export`) are the modern standard — enable with `"type": "module"` in package.json.
Both styles
// CommonJS
const fs = require('fs');
module.exports = { ok: true };
// ESM (package.json: "type": "module")
import fs from 'fs';
export const ok = true;
29. Weather App Using External API in Node.js
Practice fetch, env keys, and JSON parsing by building a weather lookup tool. Keep API keys out of source control.
Weather CLI sketch
const city = process.argv[2] || 'London';
const key = process.env.WEATHER_API_KEY;
async function weather(city) {
const url = `https://api.example.com/weather?q=${encodeURIComponent(city)}&appid=${key}`;
const res = await fetch(url);
const data = await res.json();
console.log(city, data);
}
weather(city);
30. Todo App with Node.js
A todo app is perfect for learning create/read/update/delete. Start with a JSON file store, then upgrade to MongoDB later.
JSON file todo helpers
const fs = require('fs').promises;
const FILE = './todos.json';
async function list() {
const raw = await fs.readFile(FILE, 'utf8').catch(() => '[]');
return JSON.parse(raw);
}
async function add(title) {
const todos = await list();
todos.push({ id: Date.now(), title, done: false });
await fs.writeFile(FILE, JSON.stringify(todos, null, 2));
}
31. CLI Quiz App Using Node.js
CLI apps teach input/output streams. Use `readline` to ask questions and compare answers.
Simple quiz
const readline = require('readline');
const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
rl.question('Capital of France? ', (answer) => {
console.log(answer.trim().toLowerCase() === 'paris' ? 'Correct!' : 'Wrong');
rl.close();
});
32. Currency Converter Project in Node.js
Combine CLI args, number parsing, and API/data lookup to build a practical converter utility.
Basic converter
const rates = { USD: 1, INR: 83, EUR: 0.92 };
const [,, amount, from = 'USD', to = 'INR'] = process.argv;
const value = Number(amount);
const result = (value / rates[from]) * rates[to];
console.log(`${value} ${from} = ${result.toFixed(2)} ${to}`);
33. URL Shortener Project in Node.js
Store mappings in memory/JSON/DB, generate short IDs, and redirect with HTTP 302 when a short code is visited.
In-memory shortener sketch
const http = require('http');
const map = new Map();
http.createServer((req, res) => {
if (req.method === 'POST' && req.url === '/shorten') {
let body = '';
req.on('data', (c) => (body += c));
req.on('end', () => {
const { url } = JSON.parse(body);
const code = Math.random().toString(36).slice(2, 8);
map.set(code, url);
res.end(JSON.stringify({ short: code }));
});
return;
}
const code = req.url.slice(1);
if (map.has(code)) {
res.writeHead(302, { Location: map.get(code) });
return res.end();
}
res.writeHead(404);
res.end('Not found');
}).listen(3000);
34. Express.js Introduction
Express is the most popular Node.js web framework. It simplifies routing, middleware, and JSON APIs compared to raw `http`.
Hello Express
const express = require('express');
const app = express();
app.get('/', (req, res) => res.send('Hello Express'));
app.listen(3000, () => console.log('http://localhost:3000'));
35. Render HTML Login Form with POST in Express
Use Express to render a login form and read submitted fields from `req.body` after enabling URL-encoded parsing.
Form + POST handler
const express = require('express');
const app = express();
app.use(express.urlencoded({ extended: true }));
app.get('/login', (req, res) => {
res.send(`<form method="POST" action="/login">
<input name="email" placeholder="Email" />
<input name="password" type="password" />
<button>Login</button>
</form>`);
});
app.post('/login', (req, res) => {
res.send(`Welcome ${req.body.email}`);
});
app.listen(3000);
36. 404 Page Implementation in Express.js
Place a final middleware after all routes. If nothing matched earlier, send a 404 response.
404 middleware
app.use((req, res) => {
res.status(404).send('<h1>404 - Page Not Found</h1>');
});
37. Using CSS in Express.js
Put CSS/JS/images in `public/` and mount `express.static` so browsers can load `/style.css` and similar assets.
Static files
const path = require('path');
app.use(express.static(path.join(__dirname, 'public')));
// public/style.css -> http://localhost:3000/style.css
38. GET and POST API in Express.js
GET retrieves data. POST creates data. Enable `express.json()` so JSON bodies are available on `req.body`.
GET + POST notes API
const express = require('express');
const app = express();
app.use(express.json());
const notes = [];
app.get('/api/notes', (req, res) => res.json(notes));
app.post('/api/notes', (req, res) => {
const note = { id: Date.now(), text: req.body.text };
notes.push(note);
res.status(201).json(note);
});
app.listen(3000);
39. PUT and DELETE API in Express.js
PUT/PATCH updates an existing resource. DELETE removes it. Use route params like `/api/notes/:id`.
PUT + DELETE
app.put('/api/notes/:id', (req, res) => {
const id = Number(req.params.id);
const note = notes.find((n) => n.id === id);
if (!note) return res.status(404).json({ error: 'Not found' });
note.text = req.body.text;
res.json(note);
});
app.delete('/api/notes/:id', (req, res) => {
const id = Number(req.params.id);
const index = notes.findIndex((n) => n.id === id);
if (index === -1) return res.status(404).json({ error: 'Not found' });
notes.splice(index, 1);
res.status(204).end();
});
40. Middleware in Express.js
Middleware functions run between the request and the final route handler. Call `next()` to continue, or send a response to stop the chain.
Logger middleware
function logger(req, res, next) {
console.log(req.method, req.url);
next();
}
app.use(logger);
41. Simple Notes API Project with Express
A Notes API ties together routing, middleware, validation, and status codes — a strong portfolio mini-project before databases.
Validation middleware
function requireText(req, res, next) {
if (!req.body?.text?.trim()) {
return res.status(400).json({ error: 'text is required' });
}
next();
}
app.post('/api/notes', requireText, (req, res) => {
/* create note */
});
42. Node.js and Express Interview Questions
Interview prep should cover the event loop, middleware, REST methods, error handling, security basics, and differences between Node and browsers.
Quick Q&A sheet
Q: Is Node single-threaded?
A: JS runs on one thread; libuv can use a thread pool for some I/O.
Q: What is middleware?
A: Functions that process req/res and call next().
Q: PUT vs PATCH?
A: PUT usually replaces; PATCH partially updates.
Q: How do you handle errors in Express?
A: next(err) + centralized error middleware.
43. EJS Template Engine in Express
EJS lets Express inject data into HTML with `<%= %>`. Useful for dashboards, blogs, and server-rendered pages.
Setup EJS
app.set('view engine', 'ejs');
app.set('views', path.join(__dirname, 'views'));
app.get('/profile', (req, res) => {
res.render('profile', { name: 'Imtiyaj' });
});
// views/profile.ejs
// <h1>Hello <%= name %></h1>
44. Blog Management Project with Node.js and Express
A blog project practices listing posts, create forms, edit/update, delete, and detail pages — ideal before adding MongoDB.
Blog routes sketch
app.get('/posts', (req, res) => res.render('posts/index', { posts }));
app.get('/posts/new', (req, res) => res.render('posts/new'));
app.post('/posts', (req, res) => { /* save */ res.redirect('/posts'); });
app.get('/posts/:id', (req, res) => { /* show */ });
45. MongoDB with Node.js
MongoDB is a document database. From Node, you can use the official driver or Mongoose ODM to store JSON-like documents.
Native driver connect
const { MongoClient } = require('mongodb');
async function main() {
const client = new MongoClient(process.env.MONGO_URL);
await client.connect();
const db = client.db('app');
await db.collection('users').insertOne({ name: 'Asha' });
const users = await db.collection('users').find().toArray();
console.log(users);
await client.close();
}
46. Mongoose in Node.js
Mongoose adds schemas, validation, and helpers on top of MongoDB. Models represent collections in a more application-friendly way.
User model
const mongoose = require('mongoose');
const userSchema = new mongoose.Schema({
name: { type: String, required: true },
email: { type: String, unique: true, required: true },
}, { timestamps: true });
const User = mongoose.model('User', userSchema);
module.exports = User;
47. MongoDB Atlas with Node.js
MongoDB Atlas provides managed cloud clusters. Create a cluster, whitelist IPs, create a DB user, then use the connection string in `.env`.
Connect with Mongoose
await mongoose.connect(process.env.MONGO_URL);
console.log('Connected to Atlas');
48. File Upload Using Multer in Node.js
Browsers send files as `multipart/form-data`. Multer middleware parses uploads and saves them to disk or memory.
Multer setup
const multer = require('multer');
const upload = multer({ dest: 'uploads/' });
app.post('/upload', upload.single('avatar'), (req, res) => {
res.json({ file: req.file });
});
49. Environment Variables in Node.js
Environment variables keep secrets out of source code. Use `.env` locally and real env vars in production.
dotenv example
// npm i dotenv
require('dotenv').config();
const port = process.env.PORT || 3000;
const mongoUrl = process.env.MONGO_URL;
console.log({ port, mongoUrl: Boolean(mongoUrl) });
50. Cookies in Node.js and Express
Cookies are small key/value pairs stored by the browser and sent on later requests. Prefer `httpOnly` cookies for tokens when appropriate.
cookie-parser
const cookieParser = require('cookie-parser');
app.use(cookieParser());
app.get('/set', (req, res) => {
res.cookie('theme', 'dark', { httpOnly: true, maxAge: 86400000 });
res.send('cookie set');
});
app.get('/get', (req, res) => {
res.json({ theme: req.cookies.theme });
});
51. Sessions in Node.js and Express
Sessions store data on the server and give the browser a session ID cookie. Useful for classic server-rendered login flows.
express-session
const session = require('express-session');
app.use(session({
secret: process.env.SESSION_SECRET,
resave: false,
saveUninitialized: false,
}));
app.post('/login', (req, res) => {
req.session.userId = 1;
res.send('logged in');
});
52. Authentication with JWT in Node.js
JWTs are signed tokens containing user claims. The client sends `Authorization: Bearer <token>`, and middleware verifies it on protected routes.
Sign and verify JWT
const jwt = require('jsonwebtoken');
function signToken(user) {
return jwt.sign({ sub: user.id }, process.env.JWT_SECRET, { expiresIn: '1d' });
}
function auth(req, res, next) {
const header = req.headers.authorization || '';
const token = header.startsWith('Bearer ') ? header.slice(7) : null;
try {
req.user = jwt.verify(token, process.env.JWT_SECRET);
next();
} catch {
res.status(401).json({ error: 'Unauthorized' });
}
}
app.get('/api/me', auth, (req, res) => res.json({ user: req.user }));
53. Fix CORS Issue in Node.js
Browsers block cross-origin API calls unless the server sends proper CORS headers. Use the `cors` package and restrict origins in production.
cors middleware
const cors = require('cors');
app.use(cors({
origin: ['http://localhost:5173'],
credentials: true,
}));
54. Send Email with Node.js
Use Nodemailer for welcome emails, password resets, and notifications. Keep SMTP credentials in environment variables.
Nodemailer example
const nodemailer = require('nodemailer');
const transporter = nodemailer.createTransport({
host: process.env.SMTP_HOST,
port: 587,
auth: {
user: process.env.SMTP_USER,
pass: process.env.SMTP_PASS,
},
});
await transporter.sendMail({
from: 'noreply@example.com',
to: 'user@example.com',
subject: 'Welcome',
text: 'Thanks for signing up!',
});
55. Final Todo Project with Node.js and Express
The final project combines everything: Express routes, Mongoose models, JWT-protected endpoints, validation, and clean project structure.
- Create Express app with dotenv, cors, and JSON parsing.
- Add User + Todo Mongoose models.
- Implement register/login with JWT.
- Protect todo CRUD routes with auth middleware.
Todo model + protected route sketch
const todoSchema = new mongoose.Schema({
title: { type: String, required: true },
done: { type: Boolean, default: false },
user: { type: mongoose.Schema.Types.ObjectId, ref: 'User', required: true },
}, { timestamps: true });
app.get('/api/todos', auth, async (req, res) => {
const todos = await Todo.find({ user: req.user.sub }).sort('-createdAt');
res.json(todos);
});
app.post('/api/todos', auth, async (req, res) => {
const todo = await Todo.create({ title: req.body.title, user: req.user.sub });
res.status(201).json(todo);
});
Conclusion
You now have a full Node.js path: core modules, HTTP APIs, Express CRUD, MongoDB/Mongoose, JWT auth, uploads, email, and project ideas. Practice each section in a small repo, then combine them into the final Todo API.