Expert level Node.js Interview Questions
A curated list of advanced Node.js interview questions with expert-level answers, designed to help you identify candidates with real technical depth and production experience.
Explain in detail how the Node.js event loop interacts with the thread pool and OS kernel for asynchronous I/O.
The event loop delegates file system and network I/O to the OS when possible. If not supported natively, libuv's thread pool handles it (e.g., DNS resolution, file reads). The event loop coordinates callbacks, polling for readiness, and processes completed events in specific phases. Thread pool usage impacts concurrency and needs careful tuning (UV_THREADPOOL_SIZE).
What are the exact phases of the Node.js event loop, and how can you hook into each?
Phases: Timers, Pending Callbacks, Idle/Prepare, Poll, Check, Close Callbacks. You can hook into phases using setTimeout
(Timers), I/O callbacks (Poll), setImmediate
(Check), and event listeners (Close). Tools like async_hooks
can give visibility into async resources across phases.
Compare setImmediate
, process.nextTick
, and Promises. In what order are they executed?
Order:
process.nextTick
(next tick queue, before microtasks)- Promises (
then/catch/finally
, microtask queue) setImmediate
(Check phase, macrotask)setTimeout
/setInterval
(Timers phase)
process.nextTick
always runs before other microtasks and can starve the event loop if abused.
What is the internal state flow of a Promise in Node.js? How does it transition between pending, fulfilled, and rejected?
A Promise starts as pending. It transitions to fulfilled with a value via resolve
, or rejected with a reason via reject
. These state changes are one-way and final. Resolution queues microtasks tied to .then
, .catch
, and .finally
.
Explain how you would detect and analyze a memory leak in a Node.js production app running in Kubernetes.
Use heap snapshots and tools like heapdump
, V8 Inspector, or clinic.js
. Monitor RSS, heapUsed, and event loop lag. Kubernetes probes can detect unresponsive containers. Combine with tracing (e.g., Jaeger) to pinpoint long-lived requests. Mitigate via object lifecycle audits, cleaning up event listeners, and avoiding unbounded data structures.
What are the limitations of Node.js's single-threaded model, and how would you overcome them in CPU-bound scenarios?
Limitations: one thread handles all JS execution—CPU-bound tasks block the event loop. Solutions: offload heavy tasks to worker threads, child processes, or external services. Cluster to use multiple cores, but for per-request CPU work, worker threads are essential.
Describe how libuv works under the hood to handle I/O operations in Node.js.
Libuv is a multi-platform library providing an event loop and asynchronous I/O. It uses epoll, kqueue, IOCP, or event ports depending on OS. It handles sockets, files, DNS, and more. Libuv’s thread pool (default 4 threads) handles blocking operations like fs I/O and DNS lookups.
How would you design a Node.js API that can handle 100k concurrent connections without overloading the server?
Use a non-blocking framework (Fastify, raw HTTP2), avoid per-request heavy computation, implement connection limits, and leverage reverse proxies for load balancing (NGINX, HAProxy). Use efficient data structures, avoid event loop blocking, and distribute load via clusters or distributed instances behind a load balancer. Consider WebSockets for persistent connections and offload state to Redis.
Explain how you would implement connection pooling in Node.js for a PostgreSQL or MySQL database. What are the caveats?
Use libraries like pg-pool
or mysql2-promise
. Pooling reduces connection overhead and limits concurrent DB connections. Caveats: proper pool sizing to prevent exhausting DB, handle dropped connections gracefully, and release connections after use. Monitor pool health to avoid leaks.
How do you ensure that a Node.js application is resilient to event loop blocking? Provide concrete strategies.
Monitor event loop lag (event-loop-lag
, perf_hooks
). Offload heavy work to worker threads or services. Use streaming APIs to handle large data. Avoid synchronous code in request handlers. Split large tasks into smaller chunks (e.g., setImmediate
-based chunking).
Describe how you would secure JWT authentication in a Node.js backend beyond basic signing.
Use short-lived JWTs with rotating refresh tokens, store refresh tokens securely (HttpOnly cookies). Use asymmetric keys (RS256) for signing. Validate JWT claims (exp, iss, aud). Implement token revocation lists if necessary. Secure transport (HTTPS) is mandatory.
Explain how V8's garbage collector works and how it impacts Node.js performance under high load.
V8 uses generational GC (young and old spaces). Minor GCs clean short-lived objects; major GCs for long-lived. GC pauses can block the event loop. High allocation rates increase GC frequency, affecting latency. Optimize by reducing object churn, using object pools, and preallocating buffers.
How would you design a fault-tolerant Node.js microservice that interacts with external APIs?
Use retry with exponential backoff and circuit breakers (libraries like opossum
). Graceful degradation when services are unavailable. Timeouts on HTTP requests. Bulkheads to isolate failure impact. Structured error handling, metrics for monitoring.
Explain the difference between HTTP/1.1 and HTTP/2 in Node.js, and how you would implement HTTP/2 in production.
HTTP/2 uses a single connection with multiplexed streams, reducing overhead and improving latency. Supports server push (though often disabled now). In Node.js, use http2
module. Use TLS (mandatory for HTTP/2). Ensure reverse proxies (NGINX) are configured to support HTTP/2 upstream.
How would you prevent prototype pollution in a Node.js API?
Validate and sanitize all input, especially when merging objects (avoid Object.assign
or _.merge
on user input without checks). Use Object.create(null)
for dictionaries without prototypes. Use libraries like secure-json-parse
to handle JSON safely.
What are the implications of using async_hooks in Node.js? How do you use them without degrading performance?
async_hooks
provide introspection into async operations, useful for debugging or APM. High overhead if used naively. Mitigate by sampling traces, limiting to critical code paths, and avoiding excessive tracking. Consider alternatives like CLS (Continuable Local Storage) when possible.
Describe the lifecycle of an HTTPS request in Node.js, including TLS handshake, routing, and response.
TLS handshake establishes secure connection. Node.js decodes incoming request, parses headers/body, routes via HTTP server/router. Response is composed and streamed back. Handshake latency and key size impact connection setup time; HTTP/2 can mitigate via connection reuse.
Explain what "backpressure" is in Node.js streams and how to handle it correctly in a pipeline.
Backpressure occurs when writable stream cannot process data as fast as it's read. Node.js handles this via .write()
returning false and 'drain' event. Handle by pausing readable streams and resuming on 'drain'. Use pipeline()
to automate error handling and backpressure management.
How do you prevent event emitter memory leaks in Node.js, and why do they occur?
Memory leaks occur when event listeners are added and never removed. Prevent by using once()
for one-time listeners, removing listeners (removeListener
, off
), setting proper maxListeners
, and auditing listener lifecycles.