A working reference for Node.js interviews. Answers are kept short on purpose — enough to show you understand, not a lecture. If you want to practise these out loud with feedback, that is what interview preparation is for.
Core concepts
What is the event loop and why does it matter?
Node runs JavaScript on a single thread but delegates I/O (file system, network, timers) to the system/libuv, which notifies the event loop when work completes. The loop then runs the associated callback. This is why Node handles thousands of concurrent connections cheaply — it isn't blocked waiting on I/O — but a CPU-heavy synchronous task still blocks everything else.
What are the phases of the event loop?
Roughly: timers (setTimeout/setInterval), pending callbacks, poll (fetching new I/O events, executing their callbacks), check (setImmediate), and close callbacks. process.nextTick() and resolved promise callbacks (microtasks) run between phases, before the loop continues — which is why they fire before a setTimeout(fn, 0).
Callbacks vs Promises vs async/await — what's actually different?
They're the same underlying mechanism with different ergonomics. Callbacks are prone to nesting ("callback hell") and inconsistent error handling. Promises standardise success/error paths and chain cleanly. async/await is syntax sugar over promises that reads like synchronous code — use it by default, but remember await still needs try/catch for errors and doesn't make anything actually synchronous.
What happens if you have an unhandled promise rejection?
Node emits an unhandledRejection event and, depending on version/configuration, can crash the process. Always attach .catch() or wrap await calls in try/catch; in Express-style apps, funnel errors to a single error-handling middleware rather than leaving stray unhandled rejections.
Modules and packaging
CommonJS (require) vs ES Modules (import) — what's the real difference?
CommonJS resolves and loads modules synchronously at runtime and supports dynamic require() calls anywhere. ES Modules are statically analysed (imports are known at parse time, enabling tree-shaking) and loaded asynchronously; top-level await is only available there. Interop between the two has sharp edges — a default export from CJS doesn't always map cleanly into ESM import.
What does package.json's "type": "module" actually change?
It tells Node to treat .js files as ES Modules instead of the CommonJS default, enabling import/export syntax and top-level await without needing a .mjs extension.
What's the difference between dependencies and devDependencies?
dependencies are needed at runtime in production; devDependencies are only needed for building/testing (linters, test runners, bundlers). A package-lock.json (or equivalent) pins exact resolved versions so installs are reproducible — always commit it.
Async patterns and concurrency
How do you run multiple async operations in parallel?
Promise.all([...]) runs them concurrently and rejects as soon as any one fails. Promise.allSettled([...]) runs them concurrently but waits for all to finish regardless of failure, giving you each result's status individually — use it when partial failure is acceptable.
Node is single-threaded — how does it use multiple CPU cores?
Two main ways: the built-in cluster module forks multiple processes that share a port (good for stateless HTTP servers), and worker_threads run genuine parallel JS threads with message-passing (good for CPU-bound work like image processing or parsing) without forking a whole new process.
How would you handle a CPU-intensive task without blocking the event loop?
Offload it — either to a worker_thread, a separate microservice/queue consumer, or by breaking it into chunks processed across multiple event-loop ticks. Never run a heavy synchronous loop directly in a request handler; it stalls every other in-flight request.
Streams and I/O
Why use streams instead of reading a whole file into memory?
Streams process data in chunks as it arrives, so memory use stays flat regardless of file size, and processing can start before the whole file is available. For large files or proxying HTTP payloads, this is the difference between a service that scales and one that runs out of memory under load.
What are the four stream types?
Readable (a source, e.g. reading a file), Writable (a destination, e.g. an HTTP response), Duplex (both, e.g. a TCP socket), and Transform (a duplex stream that modifies data as it passes through, e.g. gzip compression).
What does .pipe() do, and why not just read-then-write manually?
.pipe() connects a readable stream to a writable one and automatically manages backpressure — pausing the source if the destination can't keep up. Reimplementing that manually is easy to get wrong; .pipe() (or the pipeline() utility, which also handles errors and cleanup correctly) is the standard approach.
Building services
What does Express middleware actually do?
A middleware function receives (req, res, next) and can inspect/modify the request, end the response, or call next() to pass control along the chain. Order matters — body parsers, auth checks, and logging typically run before route handlers; error-handling middleware (four arguments) runs last.
How do you handle errors consistently across an API?
Centralise it: throw or pass errors to next(err) inside route handlers, and let one error-handling middleware format the response consistently (status code, message shape) rather than duplicating try/catch logic in every route.
What are common security basics for a Node/Express API?
Set security headers (helmet), configure CORS explicitly rather than allowing all origins, validate and sanitise input, use parameterised queries (never string-concatenate SQL), rate-limit auth endpoints, and keep dependencies patched — npm audit regularly, not just once.
Performance and debugging
How do you find a memory leak in a running Node service?
Watch heap usage over time (via process.memoryUsage() or a monitoring tool) — if it climbs and never comes back down after GC, take a heap snapshot (--inspect + Chrome DevTools, or clinic.js/heapdump) and look for objects that shouldn't still be retained, commonly an ever-growing cache, array, or event listeners that were never removed.
An endpoint is slow — how do you find out why?
Confirm where time is actually going: add timing around the database call, downstream HTTP calls, and any synchronous processing. Check for missing indexes, N+1 query patterns, connection-pool exhaustion, or a synchronous block stalling the event loop. Profile with --prof/clinic.js under realistic load rather than guessing.
What's the difference between process.nextTick() and setImmediate()?
process.nextTick() callbacks run immediately after the current operation, before the event loop continues to the next phase — even before promise microtasks in some Node versions' ordering nuances. setImmediate() callbacks run in the check phase, after I/O events for the current loop iteration. In practice: use setImmediate for "run after I/O this tick," and use nextTick sparingly since overusing it can starve the event loop.
Practising these
Knowing the answer and delivering it calmly under interview pressure are different skills. If you have a Node.js interview coming up, a couple of mock interview sessions with a senior engineer will show you where you actually stand. If you're already in a Node/JavaScript role and struggling with the real thing rather than the interview, see JavaScript job support.