Skip to content
tezvyn:

Top 30 Node.js & Express Interview Questions and Answers

30 multiple-choice questions on Node.js & Express, of the kind that come up in a technical interview, drawn from 30 bites in the Node.js & Express library. Answer them here or read straight down. Every question carries the correct option, why it is correct, and a link to the bite it came from.

Node.js, Express, Fastify, NestJS, Bun, Deno

30 questions. Pick an answer, or open “Show the answer” to read it.

Answers are graded in your browser. Nothing is saved, and no XP or streak is earned here. The app keeps score.

  1. Question 1 of 30

    Which statement best describes how Node.js handles I/O for thousands of concurrent connections?

    Show the answer

    Answer: a · Network sockets are watched by the OS, while blocking tasks like DNS and file reads use a small internal thread pool.

    Node.js relies on the OS kernel to monitor network sockets and notify the event loop when data arrives, while delegating blocking operations like DNS and some file system work to a limited internal thread pool. Distractor D is tempting because Node is famous for non-blocking I/O, but it wrongly assumes every I/O operation can run without thread assistance.

    Read the full bite: How does Node.js handle thousands of connections on one thread?

  2. Question 2 of 30

    In which event loop phase do the majority of completed I/O callbacks, such as a finished network read, actually execute?

    Show the answer

    Answer: d · The poll phase, which retrieves and runs I/O completions

    The poll phase retrieves new I/O events and executes most of their callbacks. The timers phase only handles elapsed setTimeout/setInterval, not I/O completion.

    Read the full bite: Order of the Node.js event loop phases

  3. Question 3 of 30

    When both are scheduled from inside a completed fs I/O callback, which runs first and why?

    Show the answer

    Answer: c · setImmediate, because the check phase follows poll in the same iteration

    From within an I/O callback the loop is in poll, so the next phase is check, making setImmediate fire before the timers phase comes around again. setTimeout(0) waits for the next iteration.

    Read the full bite: nextTick vs setImmediate vs setTimeout(fn, 0)

  4. Question 4 of 30

    Why does wrapping a heavy synchronous computation in an async function fail to keep a Node server responsive?

    Show the answer

    Answer: a · The computation is still synchronous and never yields the single JS thread

    async/await only helps when there is an awaited asynchronous boundary; a synchronous CPU loop still occupies the single thread, blocking the event loop. Worker Threads provide real parallelism.

    Read the full bite: Offloading CPU-bound work with Worker Threads

  5. Question 5 of 30

    Why is it a problem to list a library your source code imports at runtime under devDependencies?

    Show the answer

    Answer: d · A production install that skips devDependencies leaves it missing, causing runtime errors

    Production installs omit devDependencies, so a runtime import placed there will be absent and throw module-not-found. Runtime libraries must live under dependencies.

    Read the full bite: dependencies vs devDependencies in package.json

  6. Question 6 of 30

    What does Node do first when it encounters require('fs')?

    Show the answer

    Answer: a · Matches it against built-in core modules, which take precedence

    Bare specifiers are checked against built-in core modules first; fs is compiled into the binary and resolves without touching node_modules. The package search only runs for non-core bare names.

    Read the full bite: Resolving core vs relative module specifiers

  7. Question 7 of 30

    What core problem does committing package-lock.json solve that package.json alone cannot?

    Show the answer

    Answer: c · It guarantees every install resolves to the exact same dependency tree, including transitive packages

    package.json uses version ranges, so installs can drift; the lockfile pins exact versions and tree shape for all transitive deps, making installs deterministic. It does not store tarballs or block additions.

    Read the full bite: Why package-lock.json must be committed

  8. Question 8 of 30

    Which statement about using ES Modules instead of CommonJS in Node is accurate?

    Show the answer

    Answer: d · ESM lacks __dirname by default and is enabled via type module or .mjs

    ESM omits __dirname and require, and is enabled by type module or the .mjs extension. import is not an alias for require, and only ESM supports top-level await.

    Read the full bite: Choosing between CommonJS and ES Modules

  9. Question 9 of 30

    Why should the service layer in a layered Express API avoid referencing the request and response objects?

    Show the answer

    Answer: d · It keeps business logic framework-agnostic and unit-testable without HTTP

    Keeping req and res out of services makes the business logic reusable and testable without spinning up HTTP. Express does not forbid it and services are not on a separate thread.

    Read the full bite: Layered structure for a scalable Express API

  10. Question 10 of 30

    Given a dependency written as ^1.4.2, which upgrade would npm refuse to install on its own?

    Show the answer

    Answer: b · 2.0.0, a major release

    The caret allows updates below the next major, so anything under 2.0.0 is permitted, but 2.0.0 itself is excluded. The tilde would be the operator that also blocks 1.7.0.

    Read the full bite: SemVer and the caret vs tilde range operators

  11. Question 11 of 30

    When module B requires module A in the middle of A's own loading, what does B receive?

    Show the answer

    Answer: c · A's exports object as populated so far, possibly incomplete

    CommonJS caches the exports object at load start and returns it as-is, so B gets whatever A has assigned up to that point. It is not re-executed, does not throw, and the reference is not retroactively backfilled.

    Read the full bite: Circular dependencies in CommonJS modules

  12. Question 12 of 30

    Why can two installed major versions of the same library cause instanceof checks to fail across packages?

    Show the answer

    Answer: b · Each copy is a distinct module instance with its own classes, so cross-copy instanceof returns false

    Two physical copies are separate module instances with separate class identities, so an object from one copy is not an instance of the other copy's class. npm does not strip prototypes.

    Read the full bite: Diamond dependencies and nested node_modules

  13. Question 13 of 30

    What is a distinctive advantage of monorepo workspaces over consuming shared code as published private packages?

    Show the answer

    Answer: d · A breaking change and all consumer updates can land in one atomic commit without a publish step

    Workspaces link internal packages locally, so changes to shared code and its consumers ship in a single atomic commit. Published packages instead require a publish-and-bump cycle and risk version drift.

    Read the full bite: Monorepo workspaces vs private npm packages

  14. Question 14 of 30

    Which statement about a Promise's state transitions is correct?

    Show the answer

    Answer: a · Once settled as fulfilled or rejected, the state is permanent and cannot change

    Settling is one-way and final; a Promise transitions from pending to exactly one of fulfilled or rejected and stays there. then callbacks run later as microtasks, not synchronously.

    Read the full bite: The three states of a JavaScript Promise

  15. Question 15 of 30

    Why does the Promise callback print before the setTimeout(0) callback despite both being scheduled in the same tick?

    Show the answer

    Answer: d · The microtask queue is fully drained before the next macrotask runs

    After the synchronous stack clears, all microtasks (Promise reactions) drain before any macrotask (setTimeout) runs. The delay value is not the deciding factor here; queue priority is.

    Read the full bite: Output order of sync, microtask, and macrotask

  16. Question 16 of 30

    What happens to the remaining requests when one input to Promise.all rejects?

    Show the answer

    Answer: b · The combined Promise rejects immediately, but the other requests still run to completion

    Promise.all rejects as soon as the first input rejects, but JavaScript Promises are not cancellable, so the other in-flight requests continue running. They simply have no remaining handler.

    Read the full bite: Running independent requests with Promise.all and race

  17. Question 17 of 30

    In classic Express 4, how should an error from an awaited database call inside async middleware reach the error handler?

    Show the answer

    Answer: c · Catch it and pass it to next(err) so the error-handling middleware runs

    Express 4 does not auto-catch async throws, so you must catch and forward with next(err) to trigger the four-argument error handler. Throwing alone leaves the request hanging.

    Read the full bite: Handling async errors in Express middleware

  18. Question 18 of 30

    Why might a try/catch fail to catch an error from an async call inside it?

    Show the answer

    Answer: b · If the returned Promise is not awaited, control leaves the block before it rejects

    Without await, the async call returns a pending Promise and execution exits the try block immediately, so a later rejection is not caught there. Awaiting the call keeps it within the try/catch scope.

    Read the full bite: Comparing the three async error-handling styles

  19. Question 19 of 30

    Why does a worker-pool design usually finish faster than processing fixed chunks of ten sequentially?

    Show the answer

    Answer: a · It keeps ten requests always in flight instead of waiting for each chunk's slowest item

    Fixed chunks must wait for the slowest request in each batch before starting the next, leaving slots idle. A worker pool immediately refills a freed slot, maintaining full concurrency throughout.

    Read the full bite: Bounded concurrency for many async requests

  20. Question 20 of 30

    When is Promise.allSettled the better choice over Promise.all?

    Show the answer

    Answer: c · When each operation is independent and you need every outcome, including failures, reported

    allSettled waits for every input and reports each outcome, ideal when partial success is acceptable and you must see all failures. all aborts on the first rejection, hiding other results.

    Read the full bite: Promise.all vs Promise.allSettled

  21. Question 21 of 30

    Why is for await...of preferable to Promise.all for processing a multi-gigabyte file line by line?

    Show the answer

    Answer: c · It consumes lines lazily one at a time, keeping memory bounded with backpressure

    for await...of awaits each item before requesting the next, so only one line is held at a time and memory stays flat. Promise.all would require buffering every line in memory at once.

    Read the full bite: Async iterators and for await...of for streaming

  22. Question 22 of 30

    Why is calling fs.readFileSync inside an Express request handler a problem under concurrent load?

    Show the answer

    Answer: c · It blocks the single event loop thread, stalling all other pending requests

    The sync read blocks the one event loop thread until it finishes, so every other request waits. It is not forbidden and does not spawn threads; the async version uses the libuv pool instead.

    Read the full bite: fs.readFileSync vs fs.readFile

  23. Question 23 of 30

    What does path.join provide that naive string concatenation of path segments does not?

    Show the answer

    Answer: c · Platform-correct separators plus normalization of redundant slashes and segments

    path.join inserts the right separator per OS and normalizes the path. It does not encrypt, speed up reads, or by itself stop traversal attacks, which still require explicit validation.

    Read the full bite: Why use path.join over string concatenation

  24. Question 24 of 30

    In a bare http module server, what is the consequence of never calling res.end inside the request listener?

    Show the answer

    Answer: a · The client connection hangs because the response is never finalized

    res.end finalizes and flushes the response; without it the client waits indefinitely. Node does not auto-complete the response, crash, or retry the request on its own.

    Read the full bite: Minimal HTTP server with the http module

  25. Question 25 of 30

    Why does fs.createReadStream with readline scale to a 5GB log while fs.readFile does not?

    Show the answer

    Answer: d · Streaming keeps memory roughly constant by processing small chunks instead of buffering all 5GB

    Streaming processes the file in small chunks so memory stays flat; readFile allocates the whole file at once. It does not compress, double disk speed, and the issue is memory, not a hard 2GB open limit.

    Read the full bite: Counting lines in a 5GB log file efficiently

  26. Question 26 of 30

    Why must you wait for the request's end event before calling JSON.parse on a POST body?

    Show the answer

    Answer: d · The body arrives as multiple chunks and is only complete once end fires

    The request is a stream delivering chunks via data events; the full body exists only after end. The body is not encrypted, JSON.parse works anywhere, and req.body is not natively populated.

    Read the full bite: Reading a POST body from the request stream

  27. Question 27 of 30

    Given path.resolve('/foo', 'bar', '/baz', 'qux'), what is returned and why?

    Show the answer

    Answer: d · /baz/qux because an absolute segment discards everything to its left

    resolve processes right to left and a leading-slash segment resets the path, so /baz/qux results. join would keep /foo/bar/baz/qux; resolve does not simply concatenate or use only the last segment.

    Read the full bite: path.resolve vs path.join

  28. Question 28 of 30

    What is the core mechanism difference between fs.watch and fs.watchFile?

    Show the answer

    Answer: a · fs.watch uses OS-native change notifications; fs.watchFile polls with stat on an interval

    fs.watch is event-driven via OS APIs and efficient but inconsistent; fs.watchFile polls stat, making it portable but slower. The roles are not reversed and neither relies on encryption.

    Read the full bite: fs.watch vs fs.watchFile

  29. Question 29 of 30

    In a cluster setup, what is the primary (master) process responsible for?

    Show the answer

    Answer: a · Forking and supervising worker processes while workers serve traffic

    The primary forks workers and restarts them on exit; the workers handle requests across cores. It does not serve traffic itself, share a heap, or run the heavy compute.

    Read the full bite: Scaling across cores with cluster and os

  30. Question 30 of 30

    What advantage does stream.pipeline have over the .pipe() method when streaming a file to an HTTP response?

    Show the answer

    Answer: a · pipeline propagates errors and destroys all streams on failure, preventing leaks

    pipeline adds unified error handling and resource cleanup that pipe lacks, avoiding leaked descriptors. Both honor backpressure; pipeline does not skip buffering, drop backpressure, or encrypt data.

    Read the full bite: Backpressure in Node.js streams

Could you explain these out loud?

That is what an interview actually tests. Tezvyn gives you questions like these with what the interviewer is really checking, the answer that lands, and the mistake that ends the conversation, in the four minutes before your next meeting.

The iPhone app is on the way

We are building it. Until it lands, nothing here is held back from you: every interview card, your saved cards, streaks and the job board all work in Safari, plus hundreds of free practice quizzes of thirty questions each. Sign in and it all carries over to the app the day it arrives.

Want it as an icon? Tap Share at the bottom of Safari, then Add to Home Screen. It opens full screen and the cards you have read stay available offline.

Get it on Google PlayiPhone app coming soon