Node.js Backend Interview Questions
Node interviews revolve around one architectural fact and its consequences: a single thread runs your JavaScript, so anything that occupies it stops everything. Almost every serious Node question is a corollary — what blocks the loop, how backpressure prevents unbounded buffering, why an unhandled rejection can take the process down, why errors in callbacks do not propagate the way you expect.
The common weakness is a candidate fluent in async/await syntax who has never thought about what the runtime is doing underneath. They can write a clean `await` chain and cannot say why one CPU-heavy request degrades every other request on the instance, or why their stream pipeline uses unbounded memory on a large upload. The interview is built to find that boundary.
Written and reviewed by Sahil Srivastav
What the bar actually is
You should be able to explain the event loop concretely enough to predict behaviour: that `await` yields rather than parallelises, that `Promise.all` starts work concurrently but does not use more threads, and that a tight synchronous loop or a large synchronous JSON parse stalls every pending request.
On reliability you are expected to handle errors at the boundaries that actually exist: rejected promises, stream errors, and the difference between a recoverable operational error and a programmer error that should crash the process. Candidates who catch everything and continue are a reliability problem, and interviewers probe for it.
How the rounds are structured
Machine coding in TypeScript or JavaScript
A small HTTP service. Expect questions about concurrent requests, duplicate submissions, and what happens when the downstream call hangs.
Event loop and async semantics
What blocks it, microtasks versus macrotasks at a working level, how to measure loop lag. The round that most reliably separates candidates.
Streams and memory
Piping, backpressure, and why buffering a whole upload or response is a memory and availability problem rather than a style issue.
Error handling and process lifecycle
Unhandled rejections, graceful shutdown, draining in-flight requests on SIGTERM. Operational questions that come up constantly in container deployments.
What this interview bar tests
What actually blocks the loop
Synchronous CPU work, large `JSON.parse`, synchronous filesystem calls, regex backtracking. The effect is collateral: unrelated requests queue behind it.
Backpressure as correctness, not tuning
A fast producer and a slow consumer without backpressure buffers in memory without bound. `pipeline` propagates it and handles errors; manual `pipe` chains frequently do neither.
Rejections and crash semantics
An unhandled rejection terminates the process by default in modern Node. Knowing which errors should crash — programmer errors — and which should be handled is the senior distinction.
Connection reuse under load
Keep-alive sockets, agent pool limits, and the `ECONNRESET` that appears when a server closes an idle socket your client still believes is usable.
A preparation plan that works
- 1Measure event loop lag on a small service, then add a synchronous CPU burn to one endpoint and watch latency rise on an unrelated one. That demonstration is the whole mental model.
- 2Build an upload or export pipeline twice: once buffering, once streaming with `pipeline`. Compare memory. The difference makes the backpressure answer concrete rather than theoretical.
- 3Practise graceful shutdown properly: stop accepting connections, drain in-flight requests, close the pool, exit. It comes up in every containerised role and is usually implemented wrongly.
- 4Reproduce an `ECONNRESET` with keep-alive by letting a connection idle past the server timeout. Understanding this removes a whole class of flaky-retry confusion.
Questions you should expect
One endpoint does heavy CPU work. What happens to the others?
They wait. The event loop is single-threaded for your JavaScript, so synchronous CPU work blocks every pending callback and request on that instance. The fix is to move it off the loop — a worker thread, a separate service, or a queue — not to add more application instances, which merely spreads the same stall.
What does `await` do to concurrency?
It yields the loop so other work can proceed; it does not create parallelism. Two awaits in sequence are sequential. To overlap, start both promises and then `await Promise.all`. Candidates who describe `await` as parallel are signalling they have not reasoned about the runtime.
Why does your service crash on an unhandled promise rejection?
Because that is the default behaviour in current Node — an unhandled rejection terminates the process. The right response is to handle rejections where they can occur rather than installing a global handler that swallows them, because a process continuing in an unknown state is worse than a restart.
A large file upload exhausts memory. What is wrong?
The body is being buffered rather than streamed, so memory scales with file size multiplied by concurrency. Stream it through `pipeline`, which also propagates errors and destroys the streams on failure — the thing manual `pipe` chains routinely miss, leaving sockets and file handles open.
You see intermittent ECONNRESET to a downstream service. Why?
Commonly a keep-alive mismatch: the server closes an idle socket before your client does, and your client then writes to a socket that is already gone. Align the client's idle timeout below the server's, and make the request retryable since the write was never processed.
What gets candidates rejected
- Describing `await` as parallelism
- Adding instances to fix a blocked event loop instead of removing the blocking work
- Buffering whole request or response bodies and calling it simpler
- Installing a global unhandled-rejection handler that logs and continues
- Using manual `pipe` chains with no error propagation or stream destruction
- No graceful shutdown, so a container restart drops in-flight requests
- Treating `ECONNRESET` as random flakiness rather than a keep-alive lifecycle issue
What to practise, in order
Message queue consumer idempotency and retry
Duplicate and out-of-order deliveries with assertions on final state — the shape of most Node backend reliability work.
File upload deduplication and multipart finalization
Streaming, partial failure and finalisation under retry. Where buffering assumptions break.
API client pagination and retry
A client that must page through an upstream API and retry safely without duplicating or skipping records.
Webhook event idempotency and ordering
The canonical Node backend problem: at-least-once webhooks that must not double-charge.
FAQ
Do I need to explain microtasks versus macrotasks?
At a working level — that promise callbacks drain before the next timer phase — yes. Reciting the full phase order is rarely needed, and candidates who recite it but cannot say what blocks the loop are answering the easier question.
Is TypeScript expected?
For most Node backend roles now, yes. What is probed is whether types reflect real boundaries — parsed input, API contracts — rather than whether you know advanced type gymnastics. `any` at an input boundary is the thing reviewers notice.
How much should I know about worker threads?
Enough to say when they are the right answer: genuine CPU-bound work that must stay in-process. Not for I/O concurrency, which the loop already handles, and not as a general-purpose parallelism tool.
Will I be asked about a specific framework?
Often Express or NestJS. The questions that matter are middleware ordering, where errors are caught, and how request-scoped state is managed — not API surface recall.