Node.js and Express course menu
Advanced Node.js Interview Questions and Answers
Advanced Node.js and back-end interview questions with answers: libuv and the thread pool, worker threads vs cluster, memory leaks and profiling, scaling Node across cores and machines, graceful shutdown, caching, rate limiting, message queues, security, observability, and designing a scalable API.
On this page
- Internals and performance
- 1. What is libuv, and what is the thread pool used for?
- 2. Worker threads vs the cluster module vs child processes?
- 3. How would you find and fix a memory leak?
- 4. How do you find what is slowing an API?
- Scaling and reliability
- 5. How do you scale a Node.js API?
- 6. Explain graceful shutdown.
- 7. How do you make operations safe to retry?
- Security and operations
- 8. What are the main security risks for a Node.js API, and how do you address them?
- 9. What does good observability look like for a Node service?
- System design
- 10. Design a notification service that sends emails and SMS for an e-commerce site.
- Key takeaway
Senior interviews go beyond syntax: how Node works internally, how it behaves under load, and how you design, operate and secure services.
Internals and performance#
1. What is libuv, and what is the thread pool used for?#
Show solution
libuv is the C library underneath Node that implements the event loop and asynchronous I/O across operating systems. Network I/O uses the OS's non-blocking mechanisms (epoll, kqueue, IOCP) on the main thread. Operations without a good non-blocking OS API (most file system calls, DNS lookups via dns.lookup, some crypto such as pbkdf2 and scrypt, and zlib) run on libuv's thread pool, which has 4 threads by default (UV_THREADPOOL_SIZE changes it). Many slow thread-pool tasks at once can queue up behind each other.
2. Worker threads vs the cluster module vs child processes?#
Show solution
- Worker threads (
node:worker_threads) run JavaScript in parallel threads within one process, with their own event loops; ideal for CPU-heavy work (image processing, parsing, encryption) without blocking the main thread. They communicate by messages and can share memory withSharedArrayBuffer. - Cluster forks several processes of the same server that share a port, using all CPU cores for request handling. In containers and PaaS setups, running several instances behind a load balancer is more common.
- Child processes (
spawn,exec) run other programs or scripts entirely separately.
3. How would you find and fix a memory leak?#
Show solution
Symptoms: memory (heap) grows steadily until the process slows down or crashes with "heap out of memory". Typical causes: unbounded caches or arrays, listeners added repeatedly and never removed, timers that are never cleared, and closures holding large objects. Diagnose by monitoring process.memoryUsage(), taking heap snapshots (Chrome DevTools via --inspect, or v8.writeHeapSnapshot()) at intervals and comparing which objects grow. Fix the retention, bound caches (LRU with a maximum size), and remove listeners and timers in cleanup.
4. How do you find what is slowing an API?#
Show solution
Measure first: request latency percentiles (p50, p95, p99), event-loop delay (perf_hooks.monitorEventLoopDelay), CPU profiles (--cpu-prof, --inspect, flame graphs), and database query timings. Common culprits are synchronous work in handlers, N+1 database queries, missing indexes, large JSON serialisation, and sequential awaits that could run in parallel. Then fix the biggest bottleneck and measure again.
Scaling and reliability#
5. How do you scale a Node.js API?#
Show solution
Keep servers stateless (sessions and caches in Redis or the database, not in process memory), then run multiple instances across cores and machines behind a load balancer. Add caching (HTTP caching, CDN, Redis), database read replicas and connection pooling, and move slow or bursty work (emails, image processing, reports) to background jobs with a queue (BullMQ, RabbitMQ, SQS). Rate-limit clients and use timeouts and retries with backoff for calls to other services.
6. Explain graceful shutdown.#
Show solution
On SIGTERM (sent by orchestrators during deploys and scale-downs), stop accepting new connections (server.close()), let in-flight requests finish, stop background consumers, close database connections, then exit, with a hard timeout as a fallback. Combined with health checks, this enables zero-downtime deploys.
7. How do you make operations safe to retry?#
Show solution
Make them idempotent: repeating them has the same effect as doing them once. GET, PUT and DELETE are idempotent by design; for POST operations such as payments, clients send an idempotency key, and the server stores the result per key and returns it for repeats instead of charging twice. This matters because networks fail after the server acted but before the client got the response.
Security and operations#
8. What are the main security risks for a Node.js API, and how do you address them?#
Show solution
Broken access control (check ownership on every request), injection (parameterised queries, never eval user input), broken authentication (hashed passwords, rate-limited logins, short-lived tokens), sensitive data exposure (HTTPS, no secrets in code or logs), security misconfiguration (Helmet headers, strict CORS, generic error messages), vulnerable dependencies (npm audit, updates, lock files, minimal dependencies), and denial of service (body size limits, rate limiting, timeouts, avoiding catastrophic regular expressions).
9. What does good observability look like for a Node service?#
Show solution
Structured JSON logs with levels and a request id on every line; metrics such as request rate, error rate, latency percentiles, event-loop delay and memory; distributed tracing (OpenTelemetry) across services; error tracking (for example Sentry); health and readiness endpoints; and alerts on user-facing symptoms such as error rate and latency rather than on every internal blip.
System design#
10. Design a notification service that sends emails and SMS for an e-commerce site.#
Show solution
- API: other services publish events (
order.shipped) or callPOST /notifications; respond quickly with 202 Accepted. - Queue: put each notification job on a durable queue so bursts are absorbed and nothing is lost if a worker crashes.
- Workers: separate Node processes consume jobs, render templates, and call email/SMS providers with timeouts, retries with exponential backoff, and a dead-letter queue for repeated failures.
- Idempotency: a job id prevents duplicate sends on retries.
- Preferences and rate limits: respect user opt-outs and quiet hours; limit per user and per provider.
- Storage and observability: record delivery status, expose metrics (sent, failed, latency), and alert on failure rates.
Key takeaway#
More practice: the Node.js practice sets and interview questions.
Finished this lesson?