Node.js Fundamentals
Without Event Loop. Understand the runtime, V8, Libuv, and the core philosophy of Node.js.
What is Node.js?
Node.js is a runtime environment that allows JavaScript to run outside the browser.
JavaScript → Browser only
JavaScript → Server + Backend
Use Cases
- REST APIs
- Backend services
- Real-time apps (chat, notifications)
- CLI tools
Interview Line
Node.js is a runtime environment built on the V8 engine that allows JavaScript to run outside the browser.
Node.js Architecture
Node.js is built on two core components:
1. V8 Engine (Execution Layer)
- • Developed by Google
- • Converts JavaScript → Machine Code
- • Runs your JS code
2. Libuv (Async Layer)
- Handles:
- • File system
- • Network calls
- • Thread pool
- • Delegates heavy work to OS
Architecture Flow
Single-Threaded Concept (Very Important)
Is Node.js single-threaded?
JavaScript execution → Single-threaded
Node.js internally → Multi-threaded
Clear Understanding
| Component | Behavior |
|---|---|
| JS Code | Single thread |
| I/O Tasks | Multi-threaded (Libuv) |
Key Idea
You write single-threaded code, but Node executes efficiently using background threads.
Blocking vs Non-Blocking
Blocking Code
import fs from 'fs';
const data = fs.readFileSync('file.txt');
console.log("Done");- • Execution stops
- • Server cannot handle other requests
Non-Blocking Code
import fs from 'fs';
fs.readFile('file.txt', () => {
console.log("Done");
});
console.log("Next");Done
Core Concept
Node.js uses non-blocking I/O.
Why Node.js is Fast
Reasons
- Non-blocking I/O
- Efficient async handling
- No thread per request
CPU-Heavy Task Problem
while(true) {}- • Blocks main thread
- • Server becomes unresponsive
Rule
Node.js is best for I/O-heavy, not CPU-heavy tasks.
Core Modules
import { promises as fs } from 'fs';
const data = await fs.readFile('data.json', 'utf-8');import path from 'path';
const fullPath = path.join(__dirname, 'public', 'index.html');import { EventEmitter } from 'events';
const emitter = new EventEmitter();
emitter.on('login', (user) => {
console.log(`${user} logged in`);
});
emitter.emit('login', 'Shivam');Global process Object
console.log(process.env);
process.exit();Uses
- Environment variables
- Process control
Interview Questions (Fundamentals)
Theory Questions
What is Node.js?▼
Is Node.js single-threaded?▼
What is non-blocking I/O?▼
Why Node.js is fast?▼
Why avoid CPU-heavy tasks?▼
What is the difference between V8 and Libuv?▼
| V8 | Libuv |
|---|---|
| Executes JS (Execution Layer) | Handles async work (I/O Layer) |
| Machine code conversion | Thread pool & Event system |
Coding Questions
Create an event emitter that logs a timestamp every time a 'data' event is fired. (Demonstrates understanding of core events module).
import { EventEmitter } from 'events';
const logger = new EventEmitter();
logger.on('data', (msg) => {
console.log(`[${new Date().toISOString()}] ${msg}`);
});
logger.emit('data', 'User connected');
// Output: [2026-04-16T12:00:00.000Z] User connectedWhat will be printed to the console and why?
import fs from 'fs';
console.log("A");
fs.readFile('large.txt', () => {
console.log("B");
});
console.log("C");
/*
Output:
A
C
B
Why? fs.readFile is non-blocking. It delegates the reading to Libuv and
immediately moves to the next line. 'B' prints later when the callback
is pushed back to the Call Stack.
*/