Node.js Basics

What is Node.js?

Node.js is a JavaScript runtime built on Chrome's V8 engine. It allows you to run JavaScript outside the browser, making it perfect for building scalable backend applications.

Event-Driven Architecture

Node.js uses an event-driven, non-blocking I/O model. Callbacks are executed when events occur (like file reads, HTTP requests, database queries).

const EventEmitter = require('events');
const emitter = new EventEmitter();
emitter.on('event', () => {
    console.log('Event triggered!');
});
emitter.emit('event'); // Output: Event triggered!

Single-Threaded Model

Node.js runs on a single thread, but it handles concurrent requests through the event loop and callbacks. Heavy CPU tasks should be offloaded to worker threads.

// Single-threaded but non-blocking 
console.log('1');
setTimeout(() => console.log('2'), 0);
console.log('3');
// Output: 1 → 3 → 2

Installation & Setup

  • Download from nodejs.org
  • Check version: node --version
  • Check npm: npm --version
  • Create project: npm init -y

Running Node.js

// Run a file 
node app.js 
// Run Node REPL (interactive mode) 
node 
// Debug with inspect 
node --inspect app.js

Node.js REPL

The REPL (Read-Eval-Print Loop) is an interactive environment for testing JavaScript code.

> 2 + 3 5 > const x = 'Node.js'; undefined > console.log(x); Node.js > .help > .exit

Core Modules

File System (fs)

const fs = require('fs');
// Read file asynchronously 
fs.readFile('file.txt', 'utf8', (err, data) => {
    if (err) throw err;
    console.log(data);
});
// Write file 
fs.writeFile('file.txt', 'Hello World', (err) => {
    if (err) throw err;
    console.log('File written!');
});
// Sync versions (blocking) 
const data = fs.readFileSync('file.txt', 'utf8');
fs.writeFileSync('file.txt', 'Content');

Path Module

const path = require('path');
path.join('/folder', 'subfolder', 'file.txt');
// → /folder/subfolder/file.txt path.resolve('file.txt'); 
// → /absolute/path/to/file.txt path.basename('/path/to/file.txt'); 
// → file.txt path.dirname('/path/to/file.txt'); 
// → /path/to path.extname('file.txt'); // → .txt

OS Module

const os = require('os');
os.platform();
// 'linux', 'win32', 'darwin' os.arch(); 
// 'x64', 'arm64' os.cpus(); 
// CPU information 
os.freemem();
// Free memory in bytes 
os.totalmem();
// Total memory in bytes 
os.homedir();
// User's home directory 
os.userInfo();
// Current user info

HTTP Module

const http = require('http');
const server = http.createServer((req, res) => {
    res.statusCode = 200;
    res.setHeader('Content-Type', 'text/plain');
    res.end('Hello World!\n');
});
server.listen(3000, () => {
    console.log('Server running on http://localhost:3000');
});

Events Module

const EventEmitter = require('events');
class MyEmitter extends EventEmitter {}
const myEmitter = new MyEmitter();
myEmitter.on('event', () => {
    console.log('Event fired!');
});
myEmitter.once('single', () => {
    console.log('This runs only once');
});
myEmitter.emit('event');
myEmitter.emit('single');

URL Module

const url = require('url');
const myUrl = new URL('https://user:pass@example.com:8080/path?key=value#hash');
myUrl.protocol; // 'https:' myUrl.hostname; // 'example.com' myUrl.port; // '8080' myUrl.pathname; // '/path' myUrl.search; // '?key=value' myUrl.hash; // '#hash'

Crypto Module

const crypto = require('crypto');
// Generate hash 
const hash = crypto.createHash('sha256').update('password').digest('hex');
// Generate random bytes 
const token = crypto.randomBytes(32).toString('hex');
// Encrypt/Decrypt const cipher = crypto.createCipher('aes192', 'password'); let encrypted = cipher.update('data', 'utf8', 'hex'); encrypted += cipher.final('hex');

Streams & Buffers

// Streams - memory efficient for large data 
const fs = require('fs');
const readStream = fs.createReadStream('large-file.txt');
const writeStream = fs.createWriteStream('output.txt');
// Pipe - efficient data transfer 
readStream.pipe(writeStream);
// Buffer - temporary data storage 
const buffer = Buffer.alloc(10);
buffer.write('Hello');
console.log(buffer.toString());

Event Loop & Async Programming

Event Loop Phases

The event loop handles different types of callbacks in specific phases:
Phase Purpose
Timers Execute setTimeout/setInterval callbacks
Pending Callbacks Execute deferred callbacks (OS level)
Idle/Prepare Internal use only
Poll Handle I/O events (fs, network)
Check Execute setImmediate callbacks
Close Close callbacks (socket, etc)

process.nextTick() vs setImmediate()

// Execution order 
console.log('1');
process.nextTick(() => console.log('2'));
// Microtask - runs before check phase setImmediate(() => console.log('3')); 
// Macrotask - runs in check phase setTimeout(() => console.log('4'), 0); 
// Runs in timer phase console.log('5'); // Output: 1 → 5 → 2 → 4 → 3

Callbacks

// Traditional callback (callback hell) fs.readFile('file.txt', (err, data) => { if (err) { console.error(err); } else { console.log(data); } }); // Callback best practices function myAsync(callback) { setTimeout(() => { const error = null; const result = 'Success'; if (error) { callback(error, null); } else { callback(null, result); } }, 1000); }

Promises

// Create a promise const myPromise = new Promise((resolve, reject) => { setTimeout(() => { resolve('Success!'); }, 1000); }); // Consume promises myPromise .then(result => console.log(result)) .catch(error => console.error(error)) .finally(() => console.log('Done')); // Promise.all - wait for all Promise.all([promise1, promise2, promise3]) .then(results => console.log(results)); // Promise.race - first settled Promise.race([promise1, promise2]) .then(result => console.log(result));

Async/Await

// Modern async/await async function fetchData() { try { const response = await fetch('https://api.example.com'); const data = await response.json(); console.log(data); } catch (error) { console.error('Error:', error); } finally { console.log('Finished'); } } // Parallel execution async function parallel() { const [result1, result2] = await Promise.all([ asyncFunc1(), asyncFunc2() ]); return result1 + result2; } fetchData();

Microtasks vs Macrotasks

Key Difference: All microtasks execute before the next macrotask. This is why process.nextTick() and Promise callbacks run before setImmediate().

File System & Streams

Reading Files

const fs = require('fs');
// Asynchronous read (recommended) 
fs.readFile('file.txt', 'utf8', (err, data) => {
    if (err) throw err;
    console.log(data);
});
// Promise-based (modern) 
fs.promises.readFile('file.txt', 'utf8').then(data => console.log(data)).catch(err => console.error(err));
// Sync read (blocking - avoid in production) const data = fs.readFileSync('file.txt', 'utf8'); console.log(data);

Writing Files

// Write file (overwrites) 
fs.writeFile('file.txt', 'Hello World', (err) => {
    if (err) throw err;
    console.log('File written!');
});
// Append to file 
fs.appendFile('file.txt', '\nNew line', (err) => {
    if (err) throw err;
    console.log('Appended!');
});
// Promise-based 
await fs.promises.writeFile('file.txt', 'Content');
// Check file stats 
fs.stat('file.txt', (err, stats) => {
            console.log(stats.size);
            // file size in bytes console.log(stats.isFile()); // is it a file? });

Streams

const fs = require('fs');
// Readable Stream 
const readStream = fs.createReadStream('large-file.txt', {
    highWaterMark: 16 * 1024 // 16KB chunks 
});
readStream.on('data', (chunk) => {
    console.log('Received:', chunk.length, 'bytes');
});
readStream.on('end', () => {
    console.log('Stream ended');
});
readStream.on('error', (err) => {
    console.error(err);
});
// Writable Stream 
const writeStream = fs.createWriteStream('output.txt');
writeStream.write('Hello World\n');
writeStream.end();
// Pipe - efficient data transfer 
fs.createReadStream('source.txt').pipe(fs.createWriteStream('dest.txt'));

Directory Operations

const fs = require('fs');
// Read directory 
fs.readdir('./', (err, files) => {
    console.log(files);
});
// Create directory 
fs.mkdir('new-folder', (err) => {
    if (err) throw err;
    console.log('Folder created');
});
// Delete directory 
fs.rmdir('folder', (err) => {
    if (err) throw err;
    console.log('Folder removed');
});
// Delete file 
fs.unlink('file.txt', (err) => {
    if (err) throw err;
    console.log('File deleted');
});

Modules & NPM

CommonJS vs ES Modules

// CommonJS (default in Node.js) 
const express = require('express');
module.exports = myFunction;
// ES Modules (add "type": "module" in package.json) 
import express from 'express';
export default myFunction;
export const namedExport = () => {};

Creating Custom Modules

// math.js 
function add(a, b) {
    return a + b;
}

function subtract(a, b) {
    return a - b;
}
module.exports = {
    add,
    subtract
};
// app.js 
const {
    add,
    subtract
} = require('./math');
console.log(add(5, 3));
// 8 console.log(subtract(10, 3)); // 7

NPM Commands

# Initialize project
npm init - y
# Install dependencies
npm install express
# local
npm install - g nodemon # global
# Install from package.json npm install
# Update packages npm update
# Remove package
npm uninstall express
# List installed packages
npm list
# Run scripts
npm start npm run dev
# Check outdated packages
npm outdated

package.json Structure

{
    "name": "my-app",
    "version": "1.0.0",
    "description": "My Node.js app",
    "main": "index.js",
    "type": "module",
    "scripts": {
        "start": "node index.js",
        "dev": "nodemon index.js",
        "test": "jest"
    },
    "dependencies": {
        "express": "^4.18.0"
    },
    "devDependencies": {
        "nodemon": "^2.0.0",
        "jest": "^29.0.0"
    }
}

Semantic Versioning

Version format: MAJOR.MINOR.PATCH

  • ^1.2.3 - Allow changes that don't modify the left-most non-zero digit
  • ~1.2.3 - Approximately equivalent to version (allows patch changes)
  • 1.2.3 - Exact version
  • * - Any version
  • >=1.0.0 - Greater than or equal

Express.js Essentials

Basic Server Setup

const express = require('express');
const app = express();
// Middleware 
app.use(express.json());
app.use(express.urlencoded({
    extended: true
}));
// Routes 
app.get('/', (req, res) => {
    res.send('Hello World');
});
// Start server 
const PORT = process.env.PORT || 3000;
app.listen(PORT, () => {
    console.log(`Server running on port ${PORT}`);
});

Routing

// HTTP Methods 
app.get('/users', (req, res) => {
    /* read */
});
app.post('/users', (req, res) => {
    /* create */
});
app.put('/users/:id', (req, res) => {
    /* update */
});
app.delete('/users/:id', (req, res) => {
    /* delete */
});
app.patch('/users/:id', (req, res) => {
    /* partial update */
});
// URL Parameters 
app.get('/users/:id', (req, res) => {
    console.log(req.params.id);
    res.json({
        id: req.params.id
    });
});
// Query Parameters 
app.get('/search', (req, res) => {
            console.log(req.query.q); // ?q=nodejs res.json({ query: req.query.q }); }); 
            // Router grouping 
            const router = express.Router();
            router.get('/', (req, res) => res.send('Router home'));
            app.use('/api', router);

Middleware

// Custom middleware 
const logger = (req, res, next) => {
    console.log(`${req.method} ${req.url}`);
    next();
};
app.use(logger);
// Middleware for specific routes 
app.get('/admin', authenticate, (req, res) => {
    res.send('Admin panel');
});
// Error handling middleware (4 parameters) 
app.use((err, req, res, next) => {
    console.error(err.stack);
    res.status(500).send('Something went wrong!');
});

Request/Response Objects

app.post('/data', (req, res) => {
            // Request object 
            console.log(req.method);
            // HTTP method 
            console.log(req.url);
            // URL path 
            console.log(req.headers);
            // HTTP headers 
            console.log(req.body);
            // Request body 
            console.log(req.params);
            // URL parameters 
            console.log(req.query);
            // Query parameters 
            console.log(req.cookies);
            // Cookies // Response methods 
            res.status(200);
            res.json({
                message: 'Success'
            });
            // res.send('Text response'); 
            // res.redirect('/home'); 
            // res.render('template', { data }); 
            // res.download('file.pdf'); });

Static Files & Template Engines

// Serve static files 
app.use(express.static('public'));
// Set template engine (EJS) 
app.set('view engine', 'ejs');
app.set('views', './views');
// Render template 
app.get('/home', (req, res) => {
    res.render('index', {
        title: 'Home',
        user: 'John'
    });
});
// Using Pug (formerly Jade) 
app.set('view engine', 'pug');

Database Integration

MongoDB with Mongoose

// Connect to MongoDB 
const mongoose = require('mongoose');
mongoose.connect('mongodb://localhost:27017/myapp', {
    useNewUrlParser: true,
    useUnifiedTopology: true
});
// Define schema 
const userSchema = new mongoose.Schema({
    name: String,
    email: {
        type: String,
        unique: true
    },
    age: Number,
    createdAt: {
        type: Date,
        default: Date.now
    }
});
// Create model 
const User = mongoose.model('User', userSchema);
// CRUD operations 
const user = new User({
    name: 'John',
    email: 'john@example.com'
});
await user.save();
const users = await User.find();
await User.findByIdAndUpdate(id, {
    name: 'Jane'
});
await User.findByIdAndDelete(id);

MySQL Integration

const mysql = require('mysql2/promise');
// Create pool for better performance 
const pool = mysql.createPool({
    host: 'localhost',
    user: 'root',
    password: 'password',
    database: 'myapp'
});
// Query 
const connection = await pool.getConnection();
const [rows] = await connection.query('SELECT * FROM users');
console.log(rows);
// With parameters (prevent SQL injection) 
const [result] = await connection.query('SELECT * FROM users WHERE id = ?', [id]);
connection.release();

Connection Pooling & Best Practices

  • Always use connection pools for multiple queries
  • Use prepared statements to prevent SQL injection
  • Handle connection errors gracefully
  • Set connection timeouts
  • Use parameterized queries: SELECT * FROM users WHERE id = ?
  • Close connections properly: connection.release()

Security & Best Practices

Helmet - Security Headers

const helmet = require('helmet');
// Use helmet for security headers 
app.use(helmet());
// Specific security headers 
app.use(helmet.contentSecurityPolicy({
    directives: {
        defaultSrc: ["'self'"],
        scriptSrc: ["'self'", "'unsafe-inline'"]
    }
}));
app.use(helmet.hsts({
    maxAge: 31536000
}));

CORS Setup

const cors = require('cors');
// Allow all origins 
app.use(cors());
// Specific origins 
app.use(cors({
    origin: 'https://example.com',
    credentials: true,
    optionsSuccessStatus: 200
}));

Environment Variables

// .env file 
DATABASE_URL = mongodb: //localhost:27017 JWT_SECRET=your_secret_key NODE_ENV=development 
    // app.js 
    require('dotenv').config();
const dbUrl = process.env.DATABASE_URL;
const secret = process.env.JWT_SECRET;
console.log(process.env.NODE_ENV);

Input Validation

// Joi validation 
const schema = Joi.object({
    username: Joi.string().alphanum().min(3).max(30).required(),
    email: Joi.string().email().required(),
    password: Joi.string().pattern(/[a-zA-Z0-9]{3,30}/).required(),
    age: Joi.number().integer().min(18).max(100)
});
app.post('/register', (req, res) => {
            const {
                error,
                value
            } = schema.validate(req.body);
            if (error) {
                return res.status(400).json({
                    error: error.details
                });
            } // Process valid data });

Prevent Common Attacks

// XSS Prevention - sanitize input 
const xss = require('xss');
const clean = xss(userInput);
// CSRF Protection 
const csrf = require('csurf');
app.use(csrf({
    cookie: true
}));
// NoSQL Injection Prevention 
// ❌ Vulnerable 
db.users.find({
    username: req.body.username
});
// ✅ Safe 
db.users.find({
    username: String(req.body.username)
});
// SQL Injection Prevention - Use parameterized queries 
// ✅ Safe 
db.query('SELECT * FROM users WHERE id = ?', [userId]);

Authentication

JWT Authentication

const jwt = require('jsonwebtoken');
const bcrypt = require('bcrypt');
// Generate JWT 
function generateToken(user) {
    return jwt.sign({
        id: user.id,
        email: user.email
    }, process.env.JWT_SECRET, {
        expiresIn: '7d'
    });
}
// Verify JWT 
function verifyToken(token) {
    try {
        return jwt.verify(token, process.env.JWT_SECRET);
    } catch (error) {
        return null;
    }
}
// Middleware 
const authenticate = (req, res, next) => {
    const token = req.headers.authorization?.split(' ')[1];
    if (!token) return res.status(401).send('No token');
    const decoded = verifyToken(token);
    if (!decoded) return res.status(403).send('Invalid token');
    req.user = decoded;
    next();
};
// Hash password 
const hashedPassword = await bcrypt.hash(password, 10);
// Verify password 
const isValid = await bcrypt.compare(password, hashedPassword);

Session vs Token

OAuth Basics

OAuth 2.0 allows users to authenticate using third-party services (Google, GitHub, Facebook).

// Using Passport.js with Google OAuth 
const passport = require('passport');
const GoogleStrategy = require('passport-google-oauth20');
passport.use(new GoogleStrategy({
    clientID: process.env.GOOGLE_CLIENT_ID,
    clientSecret: process.env.GOOGLE_CLIENT_SECRET,
    callbackURL: '/auth/google/callback'
}, (accessToken, refreshToken, profile, done) => {
    // User authenticated, create/find user in DB 
    User.findOrCreate({
        googleId: profile.id
    }, done);
}));
app.get('/auth/google', passport.authenticate('google', {
    scope: ['profile', 'email']
}));
app.get('/auth/google/callback', passport.authenticate('google', {
    failureRedirect: '/login'
}), (req, res) => {
    res.redirect('/dashboard');
});

Error Handling & Logging

Centralized Error Handler

// Custom error class 
class AppError extends Error {
    constructor(message, statusCode) {
        super(message);
        this.statusCode = statusCode;
    }
}
// Express error handler middleware (4 params) 
app.use((err, req, res, next) => {
    const status = err.statusCode || 500;
    const message = err.message || 'Internal Server Error';
    res.status(status).json({
        status,
        message,
        ...(process.env.NODE_ENV === 'development' && {
            stack: err.stack
        })
    });
});
// Async error wrapper 
const asyncHandler = (fn) => (req, res, next) => {
    Promise.resolve(fn(req, res, next)).catch(next);
};
// Usage 
app.post('/data', asyncHandler(async (req, res) => {
    const data = await fetchData();
    res.json(data);
}));

Winston Logger Setup

const winston = require('winston');
const logger = winston.createLogger({
    level: 'info',
    format: winston.format.json(),
    defaultMeta: {
        service: 'api'
    },
    transports: [new winston.transports.File({
        filename: 'error.log',
        level: 'error'
    }), new winston.transports.File({
        filename: 'combined.log'
    })]
});
if (process.env.NODE_ENV !== 'production') {
    logger.add(new winston.transports.Console({
        format: winston.format.simple()
    }));
}
logger.info('Application started');
logger.error('An error occurred');

Morgan HTTP Logger

const morgan = require('morgan');
// Log HTTP requests 
app.use(morgan('combined'));
// Combined format 
app.use(morgan('dev'));
// Development format 
// Custom format 
app.use(morgan(':method :url :status :res[content-length] :response-time ms'));

Performance Optimization

Clustering

const cluster = require('cluster');
const os = require('os');
const numWorkers = os.cpus().length;
if (cluster.isMaster) {
    console.log(`Master process running (PID: ${process.pid})`);
    // Fork workers for (let i = 0; i < numWorkers; i++) { cluster.fork(); } cluster.on('exit', (worker) => { console.log(`Worker ${worker.process.pid} died`); cluster.fork(); // Restart dead worker }); } else { // Worker process const express = require('express'); const app = express(); app.listen(3000, () => { console.log(`Worker process running (PID: ${process.pid})`); }); }

Redis Caching

const redis = require('redis');
const client = redis.createClient();
client.connect(); // Cache middleware const cacheMiddleware = async (req, res, next) => { const cached = await client.get(req.url); if (cached) { return res.json(JSON.parse(cached)); } next(); }; app.get('/users', cacheMiddleware, async (req, res) => { const users = await User.find(); await client.setEx(req.url, 3600, JSON.stringify(users)); res.json(users); });

Compression & Gzip

const compression = require('compression');
// Enable gzip compression app.use(compression()); 
// Optimize images and assets 
// Use CDN for static files 
// Minimize CSS/JS

Async Patterns

// Process multiple items in parallel with limit 
async function processWithLimit(items, limit, processor) {
    const queue = [];
    for (const item of items) {
        if (queue.length >= limit) {
            await Promise.race(queue);
        }
        queue.push(processor(item));
    }
    await Promise.all(queue);
}
// Batch processing 
async function batchProcess(items, batchSize, processor) {
    for (let i = 0; i < items.length; i += batchSize) {
        const batch = items.slice(i, i + batchSize);
        await Promise.all(batch.map(processor));
    }
}

Worker Threads

const {
    Worker
} = require('worker_threads');

function runWorker(data) {
    return new Promise((resolve, reject) => {
        const worker = new Worker('./worker.js');
        worker.on('message', resolve);
        worker.on('error', reject);
        worker.on('exit', (code) => {
            if (code !== 0) reject(new Error(`Worker stopped with exit code ${code}`));
        });
        worker.postMessage(data);
    });
}
// Heavy computation const result = await runWorker({ number: 1000000 });

Testing

Jest Testing

// __tests__/math.test.js 
const {
    add,
    subtract
} = require('../math');
describe('Math functions', () => {
    test('add should return sum', () => {
        expect(add(2, 3)).toBe(5);
    });
    test('subtract should return difference', () => {
        expect(subtract(10, 3)).toBe(7);
    });
});

API Testing with Supertest

const request = require('supertest');
const app = require('../app');
describe('GET /users', () => {
    test('should return users array', async () => {
        const res = await request(app).get('/users').expect(200);
        expect(Array.isArray(res.body)).toBe(true);
    });
    test('should create user', async () => {
        const res = await request(app).post('/users').send({
            name: 'John',
            email: 'john@example.com'
        }).expect(201);
        expect(res.body.id).toBeDefined();
    });
});

Mocha & Chai

const chai = require('chai');
const expect = chai.expect;
describe('User', () => {
    it('should have name property', () => {
        const user = {
            name: 'John',
            age: 30
        };
        expect(user).to.have.property('name');
        expect(user.name).to.equal('John');
        expect(user.age).to.be.above(18);
    });
});

DevOps & Deployment

PM2 Process Management

npm install - g pm2 # Start app pm2 start app.js--name "my-app"
# Start with cluster mode(multiple instances) pm2 start app.js - i max--name "my-app"
# Monitor pm2 monit # Restart pm2 restart my - app # Stop all pm2 stop all # Delete process pm2 delete my - app # Startup hook pm2 startup pm2 save

Environment Configuration

# .env.development 
NODE_ENV=development DATABASE_URL=mongodb://localhost:27017 API_PORT=3000 
# .env.production 
NODE_ENV=production DATABASE_URL=mongodb://prod-server:27017 API_PORT=3000

Docker Setup

# Dockerfile 
FROM node:18-alpine WORKDIR /app COPY package.json . RUN npm install --production COPY . . EXPOSE 3000 CMD ["node", "app.js"] 
# Build image docker build -t my-app:latest . 
# Run container docker run -p 3000:3000 my-app:latest

GitHub Actions CI/CD

# .github/workflows/test.yml name: Tests on: [push, pull_request] jobs: test: runs-on: ubuntu-latest steps: - uses: actions/checkout@v2 - uses: actions/setup-node@v2 with: node-version: '18' - run: npm install - run: npm test - run: npm run build

Advanced Concepts

Custom Event Emitters

const EventEmitter = require('events');
class CustomEmitter extends EventEmitter {
    constructor() {
        super();
    }
    someMethod() {
        this.emit('event', {
            data: 'important'
        });
    }
    async asyncMethod() {
        this.on('event', (data) => {
            console.log('Received:', data);
        });
    }
}
const emitter = new CustomEmitter();
emitter.on('event', (data) => console.log(data));
emitter.someMethod();

Child Processes

const {
    spawn,
    exec
} = require('child_process');
// Execute shell command 
exec('ls -la', (error, stdout, stderr) => {
    if (error) throw error;
    console.log(stdout);
});
// Spawn process (better for large output) 
const child = spawn('node', ['script.js']);
child.stdout.on('data', (data) => {
    console.log(`stdout: ${data}`);
});
child.stderr.on('data', (data) => {
    console.error(`stderr: ${data}`);
});
child.on('close', (code) => {
    console.log(`Process exited with code ${code}`);
});

Streams Advanced

const {
    Transform
} = require('stream');
// Create custom transform stream 
const uppercase = new Transform({
    transform(chunk, encoding, callback) {
        this.push(chunk.toString().toUpperCase());
        callback();
    }
});
fs.createReadStream('input.txt').pipe(uppercase).pipe(fs.createWriteStream('output.txt'));

Rate Limiting

const rateLimit = require('express-rate-limit');
const limiter = rateLimit({
            windowMs: 15 * 60 * 1000,
            // 15 minutes max: 100, 
            // Limit each IP to 100 requests message: 'Too many requests, please try again later.', standardHeaders: true, legacyHeaders: false, }); app.use(limiter); // API limit - stricter const apiLimiter = rateLimit({ windowMs: 1 * 60 * 1000, max: 30 }); app.use('/api/', apiLimiter);

Interview Questions (50+)

Q&A Collection

What is the event loop?

The event loop is the core of Node.js. It continuously checks if there are any callbacks to execute. Node.js runs single-threaded, so the event loop ensures non-blocking I/O operations. It processes timers, I/O callbacks, setImmediate, and other async operations in phases.

Explain the difference between process.nextTick() and setImmediate().

process.nextTick() executes callbacks in the microtask queue before the next phase of the event loop. setImmediate() executes in the check phase of the event loop. Microtasks always run before macrotasks, so process.nextTick() always runs before setImmediate(). Example: console.log(1), process.nextTick(() => console.log(2)), setImmediate(() => console.log(3)), console.log(4) outputs 1→4→2→3.

What is middleware in Express.js?

Middleware are functions that have access to the request and response objects. They execute during the lifecycle of a request. Middleware can modify request/response, end the request, or call the next middleware. Express passes control to the next middleware using the next() function. Common middleware: logging, authentication, compression, body parsing.

What are callbacks and callback hell?

Callbacks are functions passed to other functions to be executed later. Callback hell (pyramid of doom) occurs when callbacks are nested too deeply, making code hard to read and maintain. Modern solutions: Promises and async/await. Example of callback hell: fs.readFile(..., (err, data) => { fs.writeFile(..., (err) => { ... }); });

What is the difference between require() and import?

require() is CommonJS (default in Node.js), imports modules synchronously. import is ES Modules, imports asynchronously. require() can be used conditionally, import must be at the top level. CommonJS uses module.exports, ES Modules use export. Node.js supports both through "type": "module" in package.json.

Explain Promises and their states.

Promises have three states: pending (initial), fulfilled (resolved successfully), rejected (failed). Once a promise settles (fulfilled or rejected), it cannot change states. Use .then() for fulfilled, .catch() for rejected, .finally() for both. Promises help avoid callback hell and enable better error handling.

What is async/await and how does it work?

async/await is syntactic sugar for Promises. async makes a function return a Promise. await pauses execution until a Promise resolves, making async code look synchronous. Always use try/catch for error handling. Example: async function getData() { try { const data = await fetchData(); } catch(err) { console.log(err); } }

What is a stream in Node.js?

Streams are objects used to read/write data in chunks rather than loading everything into memory. Four types: Readable, Writable, Duplex, Transform. Streams have methods like .pipe(), .on('data'), .on('end'). Benefits: memory efficiency for large files, improved performance. Example: fs.createReadStream().pipe(fs.createWriteStream()).

What is JWT and how does it work?

JWT (JSON Web Token) is a compact token format for authentication. Structure: Header.Payload.Signature. Header contains algorithm, Payload contains claims/data, Signature verifies integrity. Stateless - server doesn't store tokens. Sent via Authorization header. Advantages: scalable, works well with microservices, supports cross-domain requests.

How does clustering improve performance?

Clustering creates multiple worker processes, one for each CPU core. Master process manages workers. If a worker dies, master can spawn a new one. Distributes load across workers. Improves server reliability and performance. Node.js runs single-threaded, so clustering enables true multi-threading/multi-processing for CPU-intensive tasks.

What is the difference between synchronous and asynchronous code?

Synchronous code blocks execution until complete. Asynchronous code doesn't block, execution continues immediately. Sync: fs.readFileSync() - blocks until file is read. Async: fs.readFile() - continues execution, callback fires when complete. Node.js is designed for async operations. Mixing sync/async can cause performance issues.

How do you handle errors in Node.js?

Methods: try/catch for sync errors, .catch() for Promises, error event listeners, error middleware in Express (4 params). Always handle errors to prevent crashes. Log errors for debugging. Send appropriate HTTP status codes. Use custom error classes. Never ignore errors.

What is CORS and why is it needed?

CORS (Cross-Origin Resource Sharing) allows requests from different origins. By default, browsers block cross-origin requests for security. Configure with Access-Control-Allow-Origin headers. Use middleware: cors() package. Essential for APIs serving multiple domains. Development: allow all. Production: whitelist specific origins.

Explain dependency injection and why it's important.

DI is passing dependencies as arguments rather than creating them inside functions. Benefits: easier testing (mock dependencies), decouples code, improves maintainability. Example: instead of creating DB inside function, pass it as parameter: function getUser(db, id) { return db.find(id); }.

What are memory leaks and how do you prevent them?

Memory leaks occur when memory is no longer used but not freed. Common causes: unclosed timers, unreleased event listeners, unresolved promises, large data retained in closures. Prevention: properly remove listeners, use node --inspect to profile, clear timers with clearTimeout(), use tools like clinic.js to detect leaks.

What is npm and what does package-lock.json do?

npm is Node Package Manager for managing dependencies. package-lock.json locks specific versions for reproducible installs. Ensures all developers use same dependency versions. Prevents "works on my machine" issues. Always commit package-lock.json to version control. package.json specifies version ranges, package-lock.json specifies exact versions.

How do you optimize Node.js applications?

Strategies: clustering, compression (gzip), caching (Redis), load balancing, database query optimization, use streams for large data, minify code, lazy load modules, profile with clinic.js, use CDN, implement rate limiting, connection pooling, avoid synchronous operations, use async/await properly.

What is the buffer in Node.js?

Buffer is a temporary storage for data being moved from one place to another. Fixed-size chunk of memory. Used for reading binary files, network data, crypto operations. Create: Buffer.alloc(size), Buffer.from(string). Convert to string: buffer.toString(). Handle large data efficiently without loading into memory.

Explain the V8 engine and its role in Node.js.

V8 is Chrome's JavaScript engine used by Node.js. Compiles JavaScript to machine code for faster execution. Features: JIT compilation, garbage collection, optimization. Node.js provides bindings between JavaScript and C++ for low-level operations. Understanding V8 helps optimize performance.

What is the purpose of package.json "scripts" section?

Defines custom commands executable via npm run. Example: "start": "node index.js", "dev": "nodemon index.js", "test": "jest", "build": "webpack". Run with npm start or npm run scriptName. Automates common tasks, improves development workflow, standardizes commands across projects.

How do you secure Node.js applications?

Use helmet for security headers, validate/sanitize inputs, use HTTPS, implement authentication/authorization, use environment variables for secrets, keep dependencies updated, implement rate limiting, use prepared statements for DB queries, implement CSRF protection, use strong password hashing (bcrypt), implement logging/monitoring.

What is WebSockets and when would you use it?

WebSockets provide two-way communication between client and server. Unlike HTTP (request-response), WebSockets maintain persistent connection. Use cases: real-time chat, notifications, live updates, collaborative tools, gaming. Libraries: socket.io, ws. Advantages: low latency, persistent connection, supports binary data.