Advanced Node.js Interview Questions
12 Node.js interview questions for senior and lead developers (5+ years): event-loop lag, worker threads versus cluster, memory leaks, graceful shutdown, idempotency, rate limiting and caching. Each answer explains why the other options are wrong.
CoversApi designCachingDatabasesEvent loopEventsGraceful shutdownLibuvMemoryNode.jsOperationsPaymentsPerformanceRate limitingScalingWorker threads
By Deepak KumarPublished Answers checked by Deepak
Take it as a timed test
- 12 questions
- 18 min
- Pass mark 60%
- Answers shown at the end
No sign-up. Your answers and scores stay in this browser.
This pro paper has 12 Node.js questions for senior and lead developers with roughly 5+ years of experience. It covers event-loop lag, worker threads versus cluster, memory leaks, graceful shutdown, idempotency, rate limiting and caching. Ten questions are on this page with full explanations; the timed test also includes two questions that aren't shown here, so practise for understanding rather than memory.
How should you use this paper?
Answer each practice question before opening its explanation, and say your reasoning out loud the way you would in an interview. Then take the timed test: 18 minutes, a pass mark of 60%, with answers shown at the end. Your report breaks your score down by topic and difficulty, and you can retry only the questions you missed.
Practice questions with answers
Try each one before opening the answer. The explanation covers why the other options are wrong. The timed test also uses 2 questions that aren't shown here, so practise for understanding, not memory.
Response times spike under load and CPU is high on one core. How do you confirm the event loop is being blocked?
Show answer and explanation
Answer: A. Measure event-loop delay (perf_hooks.monitorEventLoopDelay) and profile with --cpu-prof to find the synchronous hot path
Event-loop delay shows how late timers fire: if p99 delay climbs with latency, something synchronous is hogging the thread. A CPU profile (--cpu-prof, Chrome DevTools or clinic.js) shows which functions take the time, often JSON parsing of huge payloads, sync crypto or a heavy regex. Making functions async doesn't move CPU work off the thread.
What is the key difference between worker_threads and cluster?
Show answer and explanation
Answer: A. Workers are threads inside one process and can share memory; cluster runs separate processes, each with its own memory
worker_threads run JavaScript in parallel within one process. They communicate by messages and can share memory through SharedArrayBuffer, which suits CPU-heavy jobs like image processing. cluster forks whole processes to spread incoming connections across cores. Workers are lighter to start; processes isolate crashes and memory better.
Memory in a long-running API climbs steadily until the process restarts. What's the usual culprit, and how do you find it?
Show answer and explanation
Answer: A. An unbounded in-memory cache or array that only grows; compare heap snapshots over time to see what is retained
Classic leaks are a module-level Map used as a cache with no size limit or expiry, listeners added per request and never removed, and closures holding large objects. Take heap snapshots a few minutes apart under load and diff them: whatever keeps growing points to the code. Fix it with an LRU cache with a maximum size, or by removing listeners.
On SIGTERM during a deploy, what should a Node.js API do? Select all that apply.
Show answer and explanation
Answer: A. Stop accepting new connections with server.close() + B. Let in-flight requests finish, with a time limit + C. Close database pools and other connections
A graceful shutdown stops taking new work, finishes what is running, releases resources, then exits. Calling process.exit(0) straight away cuts off in-flight requests and can leave transactions half-done. Add a hard timeout (say 10 to 30 seconds) so a stuck request can't block the deploy, and fail health checks first so the load balancer stops sending traffic.
What does prependListener change about the order listeners run in?
const { EventEmitter } = require('events');
const e = new EventEmitter();
e.on('x', () => console.log('a'));
e.prependListener('x', () => console.log('b'));
e.emit('x');Show answer and explanation
Answer: A. b, then a
Listeners normally run synchronously in the order they were added. prependListener puts the new listener at the front of the list, so b runs before a. Also note that emit is synchronous: all listeners finish before emit returns, so a slow listener blocks whoever emitted the event.
A client retries a 'create payment' request after a timeout. How do you make sure the customer isn't charged twice?
Show answer and explanation
Answer: A. Require an idempotency key; store the result per key and return the same result for repeats
Timeouts don't tell the client whether the first request succeeded, so retries are unavoidable. With an idempotency key, the server records the first outcome under that key, ideally in the same transaction as the charge, and returns it for any repeat instead of acting again. Stripe and most payment APIs work this way. Keys usually expire after a day or so.
Why is a token bucket a popular rate-limiting algorithm?
Show answer and explanation
Answer: A. It allows short bursts up to the bucket size while keeping the average rate fixed
Tokens refill at a steady rate up to a maximum, and each request spends one. Clients that were quiet can burst, which feels natural for real users, while the long-run rate stays capped. With several API instances, keep the bucket in a shared store such as Redis, using an atomic operation, so limits hold across servers.
UV_THREADPOOL_SIZE affects how many fs and crypto.pbkdf2 operations run in parallel.
TrueFalse
Show answer and explanation
Answer: True
True. libuv runs file system work, DNS lookups with dns.lookup, and some crypto and zlib calls on a thread pool with 4 threads by default. If you hash many passwords with pbkdf2 or scrypt, they queue behind each other. Raising UV_THREADPOOL_SIZE before startup helps, but network sockets don't use the pool at all.
Match each caching layer to what it is best at.
- CDN
- Redis
- In-process LRU cache
- Cache-Control header
- The fastest lookups, but separate in each instance
- Serving static files and public pages from servers near the user
- A shared cache that every API instance can read
- Telling browsers and proxies how long to reuse a response
Show answer and explanation
Answer: CDN → Serving static files and public pages from servers near the userRedis → A shared cache that every API instance can readIn-process LRU cache → The fastest lookups, but separate in each instanceCache-Control header → Telling browsers and proxies how long to reuse a response
Layers stack. A CDN absorbs public traffic before it reaches you. Redis gives every instance the same view, such as sessions and computed results. An in-process LRU avoids even a network hop, but each instance holds its own copy and may be stale. Cache-Control controls reuse downstream. Most bugs come from invalidation, so decide expiry rules up front.
An endpoint returns 50 orders and runs 51 database queries. What is the problem and the usual fix?
Show answer and explanation
Answer: A. The N+1 query problem; fetch the related rows in one batched query or a join
One query loads the list, then one more runs for each item to load its related data: N+1. Latency grows with the result size. Batch the lookups with a single WHERE id IN (...) or a join, use your ORM's eager loading, or a DataLoader in GraphQL. Logging the query count per request catches this early.
Answered them all? Take the same questions against the clock and get a topic-by-topic report.
Start the timed test