Node.js Comprehensive Guide

πŸ”„ Why Node.js?

πŸ”„ When to Choose Node.js:

πŸ”„ How Node.js Handles 10,000 Concurrent Requests Efficiently?

βš™οΈ LIBUV

🎯 Reactor Pattern

πŸ’» Code Example


const net = require('net');

const server = net.createServer((socket) => {
    console.log('Client connected');

    socket.on('data', (data) => {
        console.log('Received:', data.toString());
        socket.write('Echo: ' + data);
    });

    socket.on('end', () => {
        console.log('Client disconnected');
    });
});

server.listen(3000, () => {
    console.log('Server listening on port 3000');
});
    

πŸ”„ How to Implement Security in Node.js?

πŸ”„ Node.js Features

πŸ”„ How to Increase Node.js Performance

πŸ”„ How to track memory leaks?

Memory leaks occur when an application allocates memory but fails to release it when it's no longer needed, leading to a gradual increase in memory usage and potentially crashing the application.

Common Causes:

Tools for Detection

How to avoid Leakage:

πŸ”„ How to handle Back Pressure of stream in event loop?

Back pressure occurs when the producer (e.g., a readable stream) sends data faster than the consumer (e.g., a writable stream)

❌ Problem:

A Readable Stream (fast producer)
A Writable Stream (slow consumer)

The producer is pushing data every 10ms, but the consumer is only able to consume every 100ms. This will:

Eventually crash your app if the stream buffer overflows.

βœ… How to handle?

1. In Node JS stream, if you use pipe() method, it will automatically automatically manages back pressure β€” it pauses the readable stream when the writable stream is not ready to receive data.

2. If you're not using pipe(), you should manually do pause when the consumer is full and resume when it's ready again. write() method will return true / false. On drain event - resume when the writable stream is drained.

readableStream.on('data', (chunk) => {
const canContinue = writableStream.write(chunk);
if (!canContinue) {
readableStream.pause(); // pause the readable stream
}
});

writableStream.on('drain', () => {
readableStream.resume(); // resume when the writable stream is drained
});

πŸ”„ Explain Event Loop Phases:

1. Timers Phase

Executes callbacks scheduled by setTimeout() and setInterval().
Timers are not guaranteed to run at the exact delayβ€”only after the delay has passed.

2. Pending Callbacks Phase

Executes certain I/O callbacks deferred to the next loop iteration.
Examples include errors like ECONNREFUSED for TCP sockets on some Unix systems.

3. Idle, Prepare Phase

Internal use only by Node.js and libuv.
Used to prepare the system for the poll phase.

4. Poll Phase

Retrieves new I/O events and executes their callbacks.

If there are:

5. Check Phase

Executes callbacks scheduled via setImmediate().
Always comes after poll.
Useful when you want to run something immediately after I/O.

6. Close Callbacks Phase

Executes cleanup callbacks for closed resources.
This includes sockets, streams, servers, or any event emitters that emit a 'close' event.

Example: socket.on('close', ...) or server.close().

🧰 Node.js util Package

The util module in Node.js is a built-in core module that provides utility functions helpful for working with asynchronous code, debugging, and formatting.

You don’t need to install it β€” just require it:

const util = require('util');

πŸ› οΈ Common use cases include:

πŸ“Œ Example: Using util.promisify with fs.readFile

const fs = require('fs');
	const util = require('util');
	const readFile = util.promisify(fs.readFile);

	readFile('./file.txt', 'utf8')
	  .then(data => console.log(data))
	  .catch(err => console.error(err));

πŸ“Œ Example: Using util.callbackify to convert an async function

const util = require('util');

	async function fetchData() {
		return 'Fetched Data';
	}

	const callbackStyle = util.callbackify(fetchData);

	callbackStyle((err, result) => {
		if (err) throw err;
		console.log(result); // Fetched Data
	});

πŸ“Œ Example: Creating a reusable utility function with util.format

// utils/logger.js
	const util = require('util');

	function logInfo(name, value) {
		const message = util.format('INFO: %s has a value of %d', name, value);
		console.log(message);
	}

	module.exports = { logInfo };

	// usage in another file
	const { logInfo } = require('./utils/logger');
	logInfo('Speed', 80);

πŸš€ Use the util package when:

πŸ”„ Microtasks (nextTick, Promises) Are Prioritized Between Phases

The event loop in Node.js runs in multiple phases to handle asynchronous operations. Within these phases, microtasks (such as `process.nextTick()` and Promises) have higher priority over macrotasks (like `setTimeout()` and `setImmediate()`). This means microtasks will be executed before timers and I/O callbacks, even if they were queued after them.

Code Example:

setTimeout(() => console.log('timeout'), 0);
	setImmediate(() => console.log('immediate'));
	Promise.resolve().then(() => console.log('promise'));
	process.nextTick(() => console.log('nextTick'));

Execution Order:

nextTick
	promise
	timeout
	immediate

Explanation:

βœ… Diagram Summary:


	 JS Code
		↓
	β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
	β”‚ Call Stack  β”‚ ← Synchronous code runs here
	β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
		↓
	Async functions offloaded to:
	β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
	β”‚  Node APIs (libuv) β”‚ ← I/O, Timers, etc.
	β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
		↓
	Callback queues:
	β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
	β”‚ Microtasks    β”‚ Timers        β”‚ I/O Callbacks β”‚
	β”‚ (Promises,    β”‚ (setTimeout)  β”‚ (fs, net)     β”‚
	β”‚ nextTick)     β”‚               β”‚               β”‚
	β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
		↓
	β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
	β”‚ Event Loop  β”‚ ← Picks & executes tasks
	β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
		

πŸ” Key Points:

πŸš€ How is Node.js Most Commonly Used?

Node.js is a powerful JavaScript runtime built on Chrome's V8 engine. It's widely used for building fast, scalable, and real-time applications β€” especially those that are I/O-intensive.

πŸ’Ό Common use cases for Node.js include:

πŸ’‘ Why Node.js?

πŸ”š In short: Node.js is best suited for building fast, scalable network applications β€” especially those that rely heavily on asynchronous I/O operations.

πŸ”„ What is I/O in Node.js?

In Node.js, I/O (Input/Output) refers to any operation that involves reading from or writing to external resources.

Common I/O operations in Node.js include:

πŸ’‘ Node.js is designed around non-blocking asynchronous I/O, which means it can perform I/O operations in the background and continue executing other code without waiting for the I/O to finish.

πŸš€ This is made possible by the libuv library and the event loop, allowing Node.js to be highly performant and scalable, especially for I/O-heavy applications.

πŸ“Œ Example: Reading a file asynchronously

const fs = require('fs');

	fs.readFile('example.txt', 'utf8', (err, data) => {
		if (err) throw err;
		console.log(data);
	});

	console.log('File read initiated...');

πŸ” The above code starts reading the file, moves on immediately, and logs the content once it's ready β€” this is non-blocking I/O in action.

🧼 How to Write Clean and Maintainable Node.js Code

Writing clean and maintainable code in Node.js helps teams scale, reduces bugs, and makes your codebase easier to understand and extend.

Here are some best practices to follow:

πŸ“ 1. Follow a Clean Project Structure

🧠 2. Use Meaningful Naming

πŸ“¦ 3. Modularize Your Code

πŸ›‘οΈ 4. Add Error Handling

βœ… 5. Write Unit & Integration Tests

πŸ“ 6. Follow Linting and Formatting Rules

πŸ“– 7. Document Your Code

🚦 8. Use Environment Variables

πŸ“Š 9. Monitor & Log Properly

πŸ§ͺ 10. Stick to Consistent Code Style

βœ… Bonus Tips:

🧠 Clean Node.js code = easier to debug, faster to extend, and loved by your future self (or your team)!

πŸ”— Inter-Service Communication in Microservices

βš–οΈ Load Balancing & πŸš€ Caching in Distributed Node.js Systems

πŸ›’ Scalable Microservices-based eCommerce Backend (Node.js)

πŸ§ͺ Testing with Mocha & Chai

πŸ“‘ Understanding HTTP Status Codes

πŸš€ How to Handle Performance Bottlenecks in Node.js

πŸ› οΈ How to Design Scalable RESTful APIs in Node.js

🌐 What is a REST API?

βš™οΈ What is CI/CD?

☁️ What Cloud Services Have You Used?

πŸš€ How Do You Deploy and Monitor Node.js Apps in the Cloud?

Deployment

Monitoring

βš™οΈ What CI/CD Tools Have You Used?

πŸ” What is SAML?

SAML (Security Assertion Markup Language) is a standard that allows users to log in once and access multiple websites or services without needing to re-enter credentials.

It works by using an identity provider (IdP) to authenticate the user and then sending a secure "SAML assertion" to the service provider (SP) to confirm the user's identity.

This enables Single Sign-On (SSO), improving security and user convenience.

The process involves the IdP verifying the user, creating a SAML token, and passing it to the SP for access.

🧡 Node.js Cluster Module

πŸ’‘ Advantages of Using Cluster Module


	const cluster = require('cluster');
	const http = require('http');
	const os = require('os');

	const numCPUs = os.cpus().length;

	if (cluster.isMaster) {
	  console.log(`Master process PID: ${process.pid}`);
	  
	  for (let i = 0; i < numCPUs; i++) {
		cluster.fork();
	  }

	  cluster.on('exit', (worker, code, signal) => {
		console.log(\`Worker \${worker.process.pid} died\`);
		console.log('Starting a new worker...');
		cluster.fork(); // Restart the worker
	  });

	} else {
	  http.createServer((req, res) => {
		res.writeHead(200);
		res.end(\`Handled by worker \${process.pid}\`);
	  }).listen(3000);

	  console.log(\`Worker process PID: \${process.pid} is running\`);
	}
    

πŸ”€ Child Process vs Worker Thread

Feature Child Process Worker Thread
Purpose Runs another Node.js process (separate memory) Runs JS code in a separate thread (shared memory)
Use Case Heavy CPU or I/O tasks, different scripts Heavy CPU tasks within the same app
Communication Via messages (uses IPC – Inter-Process Communication) Via messages (but faster – shares memory)
Performance Slower due to process creation overhead Faster because it's in the same process
Memory Usage Higher (each child has its own memory) Lower (shared memory with main thread)
Crash Isolation Crash won’t affect main app Crash may affect main app
Module child_process module worker_threads module
Can run different code? Yes (you can run any Node.js file) Yes, but usually used for functions/tasks
Example fork(), spawn(), exec() new Worker()
Good for Running scripts, external tools, separate jobs Parallel tasks like calculations or data parsing

πŸ“‘ Node.js net Module

The net library in Node.js is a code Node JS module used to create TCP servers and clients.

🌐 API Gateway

An API gateway is a software intermediary that manages interactions between clients and applications.

Key Functions of an API Gateway:

Example Tools for API Gateway:

🚧 Challenges Faced in Our Last eCommerce Project & Solutions

πŸ” 1. Authentication & Security

Challenge: We need to verify the user data is secure or not and properly preventing unauthorized access

Reason behind, team faced some malicious activity related to token misuse.

Solution:

Implementation:

πŸ’³ 2. Order Processing & Payment Integration

Challenge: Faced issue in correct order handling even when a payment fails or is delayed.

Solution:

🚧 What are difficult situations you faced ?

βœ… 1. Scaling a Node.js App That Couldn’t Handle Traffic

Challenge: We had a large Node.js monolithic app which is handling login, product details, and payments. As traffic increased, during peak times, the system began to slow down significantly.

Solution:

Implementation:

Result: The app performance improved. Traffic spikes no longer caused downtime, teams could work independently, and new features were released faster with reduced risk.

βœ… 2. Fixing a Crash During a Flash Sale

Challenge: During a high-traffic flash sale, the app crashed. CPU usage hit 100% and key features like login and checkout stopped working due to a memory leak in async operations.

Solution:

Implementation:

Result: System restored within 30 minutes. Future sales ran smoothly, and the engineering team became more resilient and knowledgeable.

🀝 How do you resolve conflicts in a team setting?

πŸ‘¨β€πŸ« Give an example of a time you mentored a junior developer.

πŸ•’ How do you ensure on-time delivery and code quality in a sprint cycle?

🀝 What is your approach to code reviews and collaboration?

πŸ”₯ How do you manage pressure from stakeholders and tight deadlines?

🌟 How do you keep your team motivated during low-morale phases?

🧠 What is a Generator?

A Generator is a special type of function in JavaScript that you can pause and resume. Unlike normal functions that run from start to end immediately, generators let you produce values one at a time β€” perfect for handling sequences or large data in chunks.

πŸ“¦ How to Use It?


	// Define a generator function
	function* greetings() {
	  yield "Hi";
	  yield "How are you?";
	  yield "Bye";
	}

	// Create generator object
	const gen = greetings();

	console.log(gen.next()); // { value: 'Hi', done: false }
	console.log(gen.next()); // { value: 'How are you?', done: false }
	console.log(gen.next()); // { value: 'Bye', done: false }
	console.log(gen.next()); // { value: undefined, done: true }
		

Each time you call gen.next(), it resumes the generator and runs until the next yield.

When there are no more values to yield, done: true is returned.

⏱️ Debouncing & Throttling

🧘 Debouncing


function debounce(fn, delay) {
  let timer;
  return function (...args) {
    clearTimeout(timer);
    timer = setTimeout(() => fn.apply(this, args), delay);
  };
}

// Usage
window.addEventListener('resize', debounce(() => {
  console.log('Resized!');
}, 300));
  

πŸƒ Throttling


function throttle(fn, limit) {
  let inThrottle;
  return function (...args) {
    if (!inThrottle) {
      fn.apply(this, args);
      inThrottle = true;
      setTimeout(() => inThrottle = false, limit);
    }
  };
}

// Usage
window.addEventListener('scroll', throttle(() => {
  console.log('Scrolled!');
}, 200));
  

βš–οΈ Difference: some() vs every()

Feature some() every()
Definition Returns true if at least one element satisfies the condition. Returns true if all elements satisfy the condition.
Stops when It finds the first match. It finds the first failure.
Return value true / false true / false
Common use Checking if any items meet a condition (e.g., "Is there at least one error?") Checking if all items meet a condition (e.g., "Are all fields valid?")
Example

[1, 2, 3].some(n => n > 2)
// true
          

[1, 2, 3].every(n => n > 0)
// true
          

βœ… Summary

πŸ” Array.find() & Array.findIndex()

βœ… Array.prototype.find()


const numbers = [4, 9, 16, 25];
const found = numbers.find(num => num > 10);
console.log(found); // 16
  

πŸ“ Array.prototype.findIndex()


const items = ['apple', 'banana', 'cherry'];
const index = items.findIndex(fruit => fruit.startsWith('b'));
console.log(index); // 1
  

🧠 Use Cases

πŸ—οΈ express-session Overview

βœ… What express-session Does:

πŸ”„ Typical Flow in a Login System:

  1. Client sends a login request to /login.
  2. Server validates user credentials.
  3. If login is successful:

// Server-side code
req.session.user = { id: user._id, name: user.name };
  

🧾 Server sends back a response with a cookie:


Set-Cookie: connect.sid=somerandomsessionid
  

	const express = require('express');
	const session = require('express-session');

	const app = express();
	const PORT = 3000;

	// Setup session middleware
	app.use(session({
	  secret: 'your_secret_key',            // πŸ—οΈ Secret for signing the session ID cookie
	  resave: false,                         // Don't save session if unmodified
	  saveUninitialized: true,              // Save new sessions
	  cookie: { maxAge: 60000 }             // Session expires in 1 minute
	}));

	// Set a value in session
	app.get('/set', (req, res) => {
	  req.session.username = 'john_doe';
	  res.send('Session value set!');
	});

	// Get the value from session
	app.get('/get', (req, res) => {
	  const username = req.session.username;
	  res.send(`Username in session is: ${username}`);
	});

	// Destroy the session
	app.get('/logout', (req, res) => {
	  req.session.destroy(err => {
		if (err) return res.send('Error logging out.');
		res.send('Logged out and session destroyed!');
	  });
	});

	app.listen(PORT, () => {
	  console.log(`Server is running at http://localhost:${PORT}`);
	});


πŸ’‘ Bonus Tip:

Always use app.use(session({...})) early in your middleware stack to ensure req.session is available for all routes.

πŸ”„ Control Flow in Node.js

Control flow in Node.js means the order in which your code runs. Because Node.js uses non-blocking and event-based programming, things don’t always run one after the other. So, it's important to manage the flow of your code to make sure things happen in the right order and any errors are handled properly.

🎯 Why It Matters

🧰 Control Flow Mechanisms

βš™οΈ Synchronous vs Asynchronous Flow


// Synchronous Example
console.log('1');
console.log('2');
console.log('3');
// Output: 1, 2, 3

// Asynchronous Example
console.log('Start');

setTimeout(() => {
  console.log('Timeout Finished');
}, 1000);

console.log('End');
// Output: Start, End, Timeout Finished
  

πŸ“š Example of All Three Styles


// Callback style
fs.readFile('file.txt', (err, data) => {
  if (err) throw err;
  console.log(data.toString());
});

// Promise style
fs.promises.readFile('file.txt')
  .then(data => console.log(data.toString()))
  .catch(err => console.error(err));

// Async/Await style
async function readFileAsync() {
  try {
    const data = await fs.promises.readFile('file.txt');
    console.log(data.toString());
  } catch (err) {
    console.error(err);
  }
}
readFileAsync();
  

πŸ” Summary

🌐 Ajax vs Node.js

πŸ”Ή What is Ajax?

Ajax stands for Asynchronous JavaScript and XML. It is a technique used in web development to make requests to the server without reloading the page. This allows for a smoother, more interactive user experience.

Common uses of Ajax include:

πŸ”Ή What is Node.js?

Node.js is a runtime environment that allows you to run JavaScript on the server side. It’s built on Chrome’s V8 JavaScript engine and is non-blocking, meaning it can handle multiple operations at once without waiting for one to finish before starting another.

Common uses of Node.js include:

βš–οΈ Key Differences

Aspect Ajax Node.js
Type Client-side technique Server-side runtime
Purpose Fetch and send data asynchronously between client and server Run JavaScript on the server and build backend applications
Role Improves frontend user experience Handles server-side logic and requests
Use Case Dynamic web pages, live data updates, non-refreshing web apps Backend services, APIs, real-time apps

πŸ”‘ Summary

🌐 SAFE

SAFe, or the Scaled Agile Framework, is a methodology designed to help large organizations apply Agile practices across multiple teams and departments in a coordinated way. While Agile works great for small teams, SAFe scales those principles so that many teams can work together efficiently toward common business goals."

How to summarize Agile and SAFe experience on a resume or interview?

"I have experience working in Agile environments following SAFe methodology, where I participated in cross-team PI planning, collaborated within Agile Release Trains, and contributed to incremental delivery of features aligned with business goals. I’m familiar with SAFe ceremonies such as PI planning, system demos, and Inspect & Adapt workshops, and have worked closely with Product Owners and Scrum Masters to ensure continuous delivery and alignment with stakeholders."

DUAL Commit in Node.js

In Node.js, Dual Commit refers to the pattern of performing a single logical operation that needs to be persisted in two different systems at the same time, such as writing to a database and sending an event to a message queue, or updating two separate databases.

Why It’s Challenging

Common Solutions

Example


// Example: Save order in DB + send message to Kafka
async function dualCommit(orderData) {
    try {
        await db.collection('orders').insertOne(orderData);
        await kafkaProducer.send({
            topic: 'orders',
            messages: [{ value: JSON.stringify(orderData) }]
        });
    } catch (err) {
        console.error('Dual commit failed:', err);
        // Retry logic or rollback here
    }
}