Launch offer
Business website $150 USD Custom plugin $200 USD Ready in 5 days
Get a quote
Node.js

Node.js Complete Tutorial: Beginner to Advanced (55 Topics with Code)

Complete Node.js course notes covering core modules, Express.js, MongoDB, JWT, Multer, CORS, email, and projects — with practical code examples. Companion to the YouTube Node.js course.

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)

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.

  1. Learn Node.js fundamentals and core modules first.
  2. Build small APIs with the HTTP module, then move to Express.
  3. 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.

  1. Remember: Node.js is a runtime, not a framework.
  2. It uses the V8 engine to execute JavaScript on the server.
  3. 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.

  1. Client sends a request to your Node server.
  2. Server runs business logic and database queries.
  3. 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.

  1. Install Node.js LTS from the official site or nvm.
  2. Open a terminal and verify `node` and `npm`.
  3. 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.

  1. Create `app.js`.
  2. Add a simple console message.
  3. 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.

  1. Avoid blocking the event loop with heavy sync work.
  2. Use modules to organize code.
  3. 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.

  1. JS code executes in V8.
  2. Async work is handled via libuv/thread pool where needed.
  3. 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.

  1. Create Express app with dotenv, cors, and JSON parsing.
  2. Add User + Todo Mongoose models.
  3. Implement register/login with JWT.
  4. 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.

Leave a reply

Your email address will not be published. Required fields are marked *