0% found this document useful (0 votes)
9 views9 pages

Node

Advance Nod 1

Uploaded by

Aditya
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
9 views9 pages

Node

Advance Nod 1

Uploaded by

Aditya
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
You are on page 1/ 9

Node.

js Interview Handbook (5+ Years) — Deep


Dive
A practical, interview‑focused handbook with explanations, trade‑offs, and copy‑pasteable code for each
topic. Use the TL;DR boxes for quick answers; use the Deep Dive and Code sections when the
interviewer probes.

1) Core Node.js Concepts

1.1 Event Loop & Async Handling

TL;DR: Node.js is single‑threaded at the JS level but uses libuv for I/O. The event loop cycles through
phases (timers → pending callbacks → idle/prepare → poll → check → close). Microtasks (promises,
process.nextTick ) run between ticks.

Deep Dive:

• Non‑blocking I/O: disk/network operations are delegated to OS/libuv threads.


• CPU‑bound code blocks the loop → use Worker Threads or offload.
• Microtasks queue runs after the current call stack and before the next macrotask phase.

Code: Ordering demo

console.log('A');
setTimeout(() => console.log('B: setTimeout 0'), 0);
setImmediate(() => console.log('C: setImmediate'));
Promise.resolve().then(() => console.log('D: microtask (promise)'));
process.nextTick(() => console.log('E: nextTick (highest microtask)'));
console.log('F');
// Likely order: A, F, E, D, (B/C may swap depending on I/O state)

Interview Tip: Mention microtasks vs macrotasks and when the poll phase empties before check.

1.2 process.nextTick , setImmediate , setTimeout

TL;DR: nextTick runs before any other queued work; setImmediate runs in check phase;
setTimeout(…,0) runs in timers phase ≥ 0ms later.

Gotcha: Overusing nextTick can starve the event loop; prefer promises.

1
1.3 CJS vs ESM

TL;DR: CJS uses require/module.exports (sync). ESM uses import/export (async, static analysis,
tree‑shaking). In Node, enable with "type":"module" or .mjs .

// CommonJS
const fs = require('fs');
module.exports = { read: fs.readFileSync };

// ESM
import fs from 'fs';
export const read = fs.readFileSync;

Migration Tip: Avoid mixing; if you must, use dynamic import() in CJS or createRequire in ESM.

1.4 Cluster & Multi‑Core

TL;DR: Cluster forks multiple Node processes to utilize all cores; each has its own event loop and
memory. Use a load balancer (Node’s master or external like NGINX/PM2).

// cluster-demo.js
import cluster from 'cluster';
import http from 'http';
import os from 'os';

if (cluster.isPrimary) {
os.cpus().forEach(() => cluster.fork());
cluster.on('exit', (w) => cluster.fork()); // self-heal
} else {
http.createServer((_, res) => res.end(`pid ${process.pid}`)).listen(3000);
}

When to use: High concurrency I/O workloads. Not for sharing in‑memory state (use Redis/DB).

1.5 Child Processes

TL;DR: Use child_process for shell commands ( exec , spawn ) or to isolate tasks.

import { spawn, exec } from 'child_process';


exec('ls -la', (err, out) => { if (!err) console.log(out); });

const child = spawn('node', ['worker.js']);


child.stdout.on('data', d => process.stdout.write(d));

Use cases: Image processing, ffmpeg, calling legacy tools.

2
2) Asynchronous Programming

2.1 Callbacks vs Promises vs Async/Await

TL;DR: Callbacks are simple but nest; Promises chain; async/await is readable on top of promises.

// Promise -> async/await wrapper


const getJson = (url) => fetch(url).then(r => r.json());
(async () => {
try { console.log(await getJson('https://api.github.com')); }
catch (e) { console.error(e); }
})();

Pitfall: Don’t forget await on promises; handle rejections.

2.2 Microtasks vs Macrotasks

TL;DR: Microtasks (promises, nextTick ) run before macrotasks (timeouts, I/O). Use microtasks for
follow‑up work that must run ASAP.

2.3 Single Thread Concurrency

TL;DR: Concurrency comes from non‑blocking I/O. Use Worker Threads for CPU work.

2.4 Worker Threads vs Cluster

TL;DR: Workers share memory (transfer/SharedArrayBuffer) → CPU jobs; Cluster = processes → scale
HTTP.

// worker-demo.js
import { Worker } from 'worker_threads';
const worker = new Worker(new URL('./fib-worker.mjs', import.meta.url));
worker.postMessage(40);
worker.on('message', (n) => console.log('fib(40)=', n));

2.5 Streams

TL;DR: Process data in chunks: less memory, backpressure support.

import fs from 'fs';


fs.createReadStream('big.csv')

3
.pipe(fs.createWriteStream('copy.csv'))
.on('finish', () => console.log('done'));

Use cases: File copy, gzip, HTTP proxying.

3) Error Handling & Debugging

3.1 Async/Await Best Practices

• Wrap with try/catch near boundaries (controller layer).


• Centralize Express error handling middleware.

// express-error.js
app.use(async (req, res, next) => {
try { await next(); }
catch (err) { next(err); }
});

app.use((err, req, res, _next) => {


console.error(err);
res.status(500).json({ message: 'Internal error' });
});

3.2 Operational vs Programmer Errors

• Operational: timeouts, ECONNREFUSED → retry, circuit break.


• Programmer: undefined var, bad logic → crash fast, fix code.

3.3 Debugging Memory Leaks

• Use node --inspect → Chrome DevTools → Heap snapshots.


• Tools: clinic.js, 0x, heapdump.

3.4 uncaughtException & unhandledRejection

process.on('unhandledRejection', (r) => { console.error(r); /* alert/metrics


*/ });
process.on('uncaughtException', (e) => {
console.error(e);
// attempt graceful shutdown; let process manager (PM2/K8s) restart
process.exit(1);
});

4
4) Performance & Scaling

4.1 Multi‑CPU Scaling

• Use Cluster/PM2, or run multiple containers/pods behind a load balancer.

4.2 Load Balancing Strategies

• Round‑robin, least‑connections (NGINX/ELB), sticky sessions (if using in‑memory sessions →


better: Redis store).

4.3 Detect & Fix Event Loop Blocking

• Symptoms: high latency under load with low CPU usage per worker.
• Tools: clinic doctor , 0x , —prof .
• Fix: move CPU work to Workers, ensure async DB calls, paginate.

4.4 High Throughput API Techniques

• HTTP keep‑alive, gzip/br, caching (CDN/Redis), batching, idempotency keys, pagination,


connection pooling.

Code: Basic Redis cache

import express from 'express';


import Redis from 'ioredis';
const app = express();
const redis = new Redis();

app.get('/users/:id', async (req, res) => {


const key = `user:${req.params.id}`;
const cached = await redis.get(key);
if (cached) return res.json(JSON.parse(cached));
const user = await db.users.findById(req.params.id);
await redis.setex(key, 60, JSON.stringify(user));
res.json(user);
});

5) Security

5.1 Common Risks & Mitigations

• XSS: Escape output, CSP, sanitize input.


• SQLi/NoSQLi: Use parameterized queries; validate types.
• CSRF: SameSite cookies, CSRF tokens.
• DOS: Rate limit, timeouts, body size limits.

Express hardening

5
import helmet from 'helmet';
import rateLimit from 'express-rate-limit';
app.use(helmet());
app.use(rateLimit({ windowMs: 60_000, max: 100 }));
app.use(express.json({ limit: '1mb' }));

5.2 Secrets Management

• .env for local; production: AWS Secrets Manager, Vault, GCP Secret Manager. Never commit
secrets.

6) Databases

6.1 Connection Pooling

Postgres

import { Pool } from 'pg';


export const pool = new Pool({ max: 10, idleTimeoutMillis: 30_000 });

MongoDB

import { MongoClient } from 'mongodb';


const client = new MongoClient(process.env.MONGO_URL); // driver manages pool
await client.connect();

6.2 ORM/ODM vs Raw

• ORM/ODM (Prisma/Sequelize/Mongoose): faster dev, migrations, validation.


• Raw: maximum control/perf, less abstraction.

6.3 Transactions

Postgres

const client = await pool.connect();


try {
await client.query('BEGIN');
await client.query('INSERT INTO accounts ...');
await client.query('COMMIT');
} catch (e) {
await client.query('ROLLBACK');
throw e;
} finally { client.release(); }

6
MongoDB (replica set/Atlas required)

const session = client.startSession();


try {
await session.withTransaction(async () => {
await users.insertOne({ name: 'A' }, { session });
await orders.insertOne({ user: 'A' }, { session });
});
} finally { await session.endSession(); }

7) Testing & CI/CD

7.1 Unit/Integration Testing

// Jest unit test example


import { sum } from './math.js';
test('sum', () => expect(sum(2,3)).toBe(5));

// Integration with supertest


import request from 'supertest';
import app from '../app.js';

test('GET /health', async () => {


await request(app).get('/health').expect(200);
});

7.2 Async Testing Patterns

test('await pattern', async () => {


const data = await fetchData();
expect(data.ok).toBe(true);
});

7.3 CI/CD Outline

• Pipeline: lint → test → build → scan → deploy.


• Tools: GitHub Actions, GitLab CI, Jenkins; deployments via Docker/K8s.

8) Architecture & Best Practices

8.1 SOLID in Node

• SRP: small modules (authService, userRepo).

7
• OCP: middleware/plugins; extend without modify.
• LSP: repository interfaces interchangeable (Mongo/Postgres).
• ISP: split large interfaces; focused services.
• DIP: depend on abstractions; inject implementations.

DIP Example (logger)

class Service {
constructor(logger) { this.logger = logger; }
run() { this.logger.info('running'); }
}
// main.js decides which logger to use

8.2 Singleton

• Good: DB pool, config loader.


• Risks: global state, testing pain, multi‑process inconsistency. Prefer DI; if used, keep stateless or
idempotent.

8.3 Monolith vs Microservices

• Monolith: simpler deploy, easier transactions; great to start.


• Microservices: independent scaling, team boundaries; added ops complexity.

8.4 Stateless API

• No in‑memory sessions; JWT or session store (Redis). Store files in S3/GCS.

8.5 Queues (Kafka/RabbitMQ/BullMQ)

• Use for async tasks: email, billing, heavy processing. Improves latency and resilience.

9) Real‑World Scenarios

9.1 API Latency Spike

Checklist:

• Check event loop lag ( event-loop-lag ), DB slow queries, GC pauses, hot code paths.
• Add caching, indexes, pagination; move CPU tasks to workers.

9.2 Random Crashes

• Inspect logs/traces; ensure proper error boundaries; use PM2/K8s restart policies; add health
checks.

8
9.3 Rate Limiter Middleware (Redis)

import Redis from 'ioredis';


const redis = new Redis();
export const rateLimit = (limit=100, windowSec=60) => async (req,res,next)
=>

You might also like