Node.js Interview Questions — Practice Real Interview Problems

Reviewed by Mark Dickie · Last updated

Node.js is a JavaScript runtime built on Chrome's V8 engine that lets you run JavaScript outside the browser, most often on servers. For interviews, the event loop is the spine of almost every question: you need to know how Node processes asynchronous work across its phases, when microtasks run versus macrotasks, and why blocking the main thread is the cardinal sin. Beyond that, expect questions on streams, the CommonJS/ESM module split, error-first callbacks and Promises, memory leaks, and how to profile a slow process.

What does a Node.js interview typically test?

TopicWhat gets asked
Event loopPhase order, microtask vs macrotask scheduling, process.nextTick vs setImmediate
StreamsPiping, backpressure, transform streams, error propagation
ModulesCommonJS require vs ESM import, circular dependencies
Async patternsPromises, async/await, error-first callbacks, Promise.all vs allSettled
Performance & memoryMemory leaks, --max-old-space-size, CPU profiling, clustering
Built-in modulesfs, http, path, child_process, worker_threads

How should I prepare for a Node.js interview?

  1. Write out the event loop phases from memory and explain what runs in each one.
  2. Build a small HTTP server that serves files through streams and add backpressure handling.
  3. Convert a callback-based function into a Promise, then into async/await.
  4. Create a memory leak on purpose, then track it down with --inspect and a heap snapshot.
  5. Set up a cluster or use worker_threads to run CPU-bound work without blocking the event loop.

Senior-level interviews also dig into runtime internals: V8's garbage collector, libuv's thread pool (default size 4), and how worker_threads differ from child processes. Knowing where the built-in http module ends and where a framework like Express or Fastify picks up shows the kind of practical judgment interviewers look for. If you can explain why Node is single-threaded yet still handles thousands of concurrent connections, you have the mental model most candidates are missing.

Key facts

  • Tarmac has 101 Node.js interview questions on this topic, 10 of them on this page, at difficulty 1–5 of 5.
  • Tarmac last reviewed these Node.js interview questions on 18 August 2026.

At a glance

Questions10 shown · 101 in the bank
Difficulty1–5 of 5
FormatsCoding exercise, Multiple choice, True / false, Fill in the blank, Multiple answer, Find the bug, Flashcard, Ordering, Code output, Short answer
Interactive1 run your code against tests, in the app

What you'll review

  1. middleware
  2. commonjs
  3. file system
  4. streams
  5. error first callbacks
  6. clustering
  7. node event loop
  8. async context

Practice questions

Node.js/node-http/middleware

In Node.js HTTP servers, middleware functions typically receive (req, res, next) and modify the request or response before passing control along. Implement a simple logging middleware function loggingMiddleware(req, res) that:#

Starter code

function loggingMiddleware(req, res) {
  // TODO: Set res.headers['X-Request-ID'] to req.id or 'unknown'
  // TODO: Set res.statusCode to 200
  // TODO: Return res

  return res;
}

Your solution must pass

  • sets header from req.id

This one is written and run, not read. Solve it in the app and your code is executed against these tests and the hidden ones.

Node.js/node-modules/commonjs

In a CommonJS module (a .js file loaded with require), which of these is a real, module-scoped variable Node injects automatically?#

Options

Show answer

__dirname is the real module-scoped variable Node injects automatically. Node wraps every CommonJS module in a function that receives exports, require, module, __filename, and __dirname as parameters, so it is available without any import. import.meta only exists in ES modules, and window is a browser global.

Why:

Node wraps every CommonJS module in a function that receives exports, require, module, __filename, and __dirname as parameters, so __dirname is available without importing anything. import.meta only exists in ES modules; window is a browser global.

Node.js/node-modules/commonjs

In CommonJS, calling require("./config") twice in the same process returns the same cached module object the second time.#

Options

Show answer

True. require caches a module's exports in require.cache keyed by the resolved filename, and the module's top-level code runs only on the first call. Every later require of the same path returns that same cached object, making the module effectively a singleton per process.

Why:

require caches a module's exports in require.cache keyed by its resolved filename. The module's top-level code runs only on the first require; subsequent calls return the same exported object, so the module is effectively a singleton per process.

Node.js/fs-io/file-system

To read a file with async/await using the promise-based API, you import from fs/_____ and call await _____(path, "utf8").#

Show answer

To read a file with async/await using the promise-based API, you import from fs/**promises** and call await **readFile**(path, "utf8").

Why:

The promise-based file API lives at fs/promises (also reachable as fs.promises). Its readFile(path, encoding) returns a Promise of the contents, so it can be awaited directly — unlike the callback-style fs.readFile.

Node.js/runtime/streams

Which of the following are among the four fundamental stream types in Node's stream module?#

Options

Pick every one that applies.

Show answer

The four fundamental stream types are Readable, Writable, Duplex, and Transform — not Mutable, which does not exist. A Duplex is both readable and writable, and a Transform is a Duplex whose output is computed from its input, such as zlib.createGzip().

Why:

The four base stream types are Readable, Writable, Duplex (both readable and writable), and Transform (a Duplex whose output is computed from its input, e.g. zlib.createGzip()). There is no "Mutable" stream type.

Node.js/async-patterns/error-first-callbacks

This reads a file but will crash on the happy path instead of handling failures. Which line is the bug?#

const fs = require('fs');
fs.readFile('./data.json', 'utf8', (data) => {
  console.log(JSON.parse(data));
});
Show answer

The bug is on line 2.

Why:

Node's error-first callbacks receive (err, data) — the first argument is the error. The callback here binds the first argument to data, so data is actually the error (usually null on success), and the real file contents are dropped. It must be (err, data) => { if (err) ...; }.

Node.js/scaling/clustering

When would you reach for the cluster module versus worker_threads?#

Show answer

Use cluster to fork multiple processes of the same server to use all CPU cores for I/O-bound, request-serving workloads (each worker has its own memory and event loop, sharing a listening port). Use worker_threads for CPU-bound work you want to run in parallel within one process (shared-nothing by default, but able to share memory via SharedArrayBuffer and transfer buffers cheaply).

Why:

cluster scales an I/O server horizontally across cores using separate processes; worker_threads offloads CPU-heavy computation to threads in the same process. They solve different problems — concurrency of requests vs parallelism of computation.

Node.js/runtime/node-event-loop

This runs top to bottom:#

Put these in order

Show answer

The logs appear in this order:

  1. A (synchronous)
  2. D (process.nextTick)
  3. C (promise microtask)
  4. B (setTimeout)

Synchronous code runs first, then the process.nextTick queue drains before the promise microtask queue, and the setTimeout callback is a timers-phase macrotask that runs last.

Why:

Synchronous code runs first (A). Before the event loop moves on, the process.nextTick queue drains (D), then the promise microtask queue (C). The setTimeout callback (B) is a timers-phase macrotask, so it runs last.

Node.js/runtime/node-event-loop

In what order does Node log these four lines?#

console.log('sync');
setTimeout(() => console.log('timeout'), 0);
Promise.resolve().then(() => console.log('promise'));
process.nextTick(() => console.log('nextTick'));
Show answer
sync
nextTick
promise
timeout
Why:

Synchronous code runs first (sync). Before the event loop advances to any phase, Node drains the process.nextTick queue, then the promise microtask queue — so nextTick precedes promise. The setTimeout callback is a timers-phase macrotask and runs last.

Node.js/async-patterns/async-context

What problem does AsyncLocalStorage solve, and how does it work at a high level?#

Show answer

AsyncLocalStorage (from node:async_hooks) provides per-request context that survives across asynchronous boundaries — callbacks, promises, and awaits — without threading a value through every function argument. You call store.run(context, callback) to establish a context, and any code executing within that asynchronous call chain can retrieve it with store.getStore(). It is commonly used to carry a request id or trace id through a whole request lifecycle for logging.

Why:

AsyncLocalStorage ties a store to an asynchronous execution context so values set with run() are readable via getStore() anywhere downstream in that chain, even after await. It is the idiomatic way to propagate request-scoped data (request/trace ids, user) without passing it explicitly everywhere.

Sources

The official documentation these questions are checked against:

Related interview questions

The other 91 questions

This page shows 10. A free account opens the rest and marks your answers, which a page of answers can't do. Miss one and it returns a few days later, then a week after that, until you stop missing it.

Start free

Free · the whole bank · 100 graded answers per 30 days · written feedback and job-ad quizzes on the paid plan

What moved, monthly

One email a month when the bulletin comes out: what moved in the markets we track, and the new question topics we published. Confirm your address to join. Unsubscribe any time.