Q1. How does the Node.js event loop work?
Short answer: A single-threaded loop offloads I/O and runs callbacks in phases, enabling non-blocking concurrency.
Strong answer: Node runs JavaScript on a single thread but offloads I/O to the libuv thread pool/OS, then processes completed callbacks through event-loop phases (timers, pending, poll, check, close). This non-blocking model handles many concurrent I/O operations efficiently — current to Node.js 24 LTS. CPU-bound work blocks the loop, so offload it.
Likely follow-up: What happens to the event loop during a heavy synchronous computation?
What the interviewer is checking: Core runtime understanding.
Common mistake: Thinking Node is multi-threaded for your JS code by default.
Q2. What is the difference between process.nextTick, microtasks and setImmediate?
Short answer: nextTick and promise microtasks run before the loop continues; setImmediate runs in the check phase.
Strong answer: process.nextTick callbacks and Promise microtasks drain before the event loop proceeds to the next phase (nextTick even before other microtasks). setImmediate runs in the check phase after poll. Misusing nextTick can starve the loop. Understanding the ordering matters for predictable async behavior.
Likely follow-up: How can overusing process.nextTick starve I/O?
What the interviewer is checking: Deep async ordering knowledge.
Common mistake: Assuming setImmediate and nextTick run at the same time.
Q3. How do you handle CPU-bound work in Node.js?
Short answer: Offload to worker threads, child processes, or an external service — never block the loop.
Strong answer: Since a long synchronous task blocks the single event loop and stalls all requests, offload CPU-bound work to worker_threads, a child process/cluster, or a separate service/queue. Keep the main loop for I/O orchestration. This preserves responsiveness under load.
Likely follow-up: Worker threads vs cluster — when do you use each?
What the interviewer is checking: Scaling Node correctly.
Common mistake: Running heavy computation on the main thread.
Q4. How do you handle errors in async Node.js code?
Short answer: try/catch with async/await, handle promise rejections, and centralize error middleware.
Strong answer: Use try/catch around await, always handle promise rejections (unhandledRejection can crash the process), and in Express centralize errors with error-handling middleware. Distinguish operational errors (retry/respond) from programmer errors (fail fast). Validate inputs and never swallow errors silently.
Likely follow-up: What happens on an unhandled promise rejection in modern Node?
What the interviewer is checking: Robust error handling.
Common mistake: Forgetting to catch rejected promises.
Q5. How do you build a scalable REST API in Node.js?
Short answer: Async handlers, input validation, middleware, clustering/horizontal scaling, and caching.
Strong answer: Use a framework (Express/Fastify) with async handlers, validate inputs, centralize auth and errors in middleware, keep handlers non-blocking, and scale horizontally (cluster/PM2 or containers behind a load balancer) since one process uses one core. Add caching, rate limiting and observability.
Likely follow-up: Why does a single Node process only use one CPU core?
What the interviewer is checking: API and scaling understanding.
Common mistake: Running one process on a multi-core box with no clustering.
Q6. What are streams and why are they useful?
Short answer: Streams process data in chunks, enabling low-memory handling of large data.
Strong answer: Streams (readable, writable, duplex, transform) process data piece by piece instead of loading it all into memory — ideal for large files, network data and pipelines via pipe(). They improve memory efficiency and time-to-first-byte. Backpressure handling keeps fast producers from overwhelming slow consumers.
Likely follow-up: What problem does backpressure solve?
What the interviewer is checking: Efficient data handling.
Common mistake: Buffering huge payloads fully in memory.
Q7. How do you manage configuration, secrets and security in Node apps?
Short answer: Env-based config, a secrets manager, input validation, and dependency scanning.
Strong answer: Keep config in environment variables (12-factor), store secrets in a manager (not .env in git), validate and sanitize all input, use parameterized queries, set security headers (helmet), keep dependencies patched (npm audit/SCA), and avoid running as root. Supply-chain risk via npm packages is real — vet dependencies.
Likely follow-up: How do you reduce npm supply-chain risk?
What the interviewer is checking: Security awareness.
Common mistake: Committing secrets and ignoring dependency vulnerabilities.