← All articles
13 min read

Node.js Interview Questions for Backend Engineers (2026)

Senior Node.js interviews in 2026 test whether you can keep a production service healthy, not whether you can define a callback. Here are the questions that come up, with answers and debugging walkthroughs.

Node.js interview questions in 2026 focus on production behavior: how the event loop schedules work, what blocks it, how streams apply backpressure, how errors and crashes are handled, and how you would debug a leaking or slow service. For experienced backend roles, expect fewer definitions and more scenarios like "p99 latency tripled after a deploy, walk me through it." Below are the questions that come up most, with the answers interviewers want and step-by-step debugging walkthroughs.

If you need a refresher on language fundamentals like closures, this, and the microtask queue first, start with our JavaScript interview questions guide. This article assumes you know the language and focuses on the runtime.

Key Takeaways

  • The event loop has six phases (timers, pending callbacks, idle/prepare, poll, check, close), and process.nextTick plus promise microtasks run between every callback, not once per phase.
  • "Node is single-threaded" is a trap answer. Your JavaScript is, but libuv runs a thread pool of 4 threads by default for fs, dns.lookup, crypto, and zlib.
  • Backpressure is the most common streams question: respect the false returned by write() or use pipeline() from node:stream/promises.
  • Worker threads are for CPU-bound JavaScript; cluster (or more containers) is for scaling request handling across cores. Neither fixes slow I/O.
  • Express 5 forwards rejected promises to error middleware automatically, which changes a classic Express 4 interview answer.
  • Senior loops grade your debugging method: measure event loop delay, take heap snapshots, profile CPU, then fix the root cause.

What Node.js Interviews Test in 2026

Node.js interviews now test runtime judgment more than API recall. Most backend loops include a coding round, a system design round, and at least one conversation where the interviewer probes how Node behaves under real load. Our backend engineer interview guide covers the full loop; this section maps the Node-specific part.

TopicJunior/mid questionSenior question
Event loopWhat order do these logs print?Why did one endpoint slow every other endpoint?
StreamsWhat is a Transform stream?Why does this CSV export run out of memory?
ErrorsHow do you catch an async error?What should the process do on an uncaught exception?
PerformanceWhat is the cluster module?Worker threads or more pods for this workload?
FrameworksWhat is middleware?How would you structure validation and errors across 40 routes?
SecurityWhat is SQL injection?How does prototype pollution reach your API?

Version context matters a little. Check the official Node.js release schedule for which line is current LTS, and expect most teams to run Node.js 22 or 24. Knowing modern built-ins such as fetch, node:test, AbortController, and stream/promises signals that your experience is current.

How Does the Node.js Event Loop Work?

The Node.js event loop is the libuv-driven cycle that picks up completed I/O and timers and runs their JavaScript callbacks on the main thread. Each iteration moves through fixed phases, as described in the official Node.js event loop guide:

  1. Timers: callbacks from setTimeout and setInterval whose threshold has passed.
  2. Pending callbacks: some system-level I/O callbacks deferred from the previous iteration, such as certain TCP errors.
  3. Idle, prepare: internal use only.
  4. Poll: retrieve new I/O events and run their callbacks. The loop may block here waiting for I/O when nothing else is scheduled.
  5. Check: setImmediate callbacks.
  6. Close callbacks: for example socket.on('close').

Between each individual callback, Node drains the process.nextTick queue and then the promise microtask queue. That is why a long chain of resolved promises can delay I/O just as effectively as a while loop.

The classic ordering question

const fs = require('node:fs');

setTimeout(() => console.log('timeout'), 0);
setImmediate(() => console.log('immediate'));
Promise.resolve().then(() => console.log('promise'));
process.nextTick(() => console.log('nextTick'));

fs.readFile(__filename, () => {
  setTimeout(() => console.log('io timeout'), 0);
  setImmediate(() => console.log('io immediate'));
});

console.log('sync');

The output starts with sync, nextTick, promise. After that, timeout versus immediate from the main module is not guaranteed, because it depends on how fast the process enters the first loop iteration. Inside the I/O callback, io immediate always prints before io timeout, because the check phase comes right after poll. Saying "it depends, and here is why" earns more credit than guessing a fixed order.

Where does the thread pool fit?

libuv keeps a thread pool, 4 threads by default and configurable with the UV_THREADPOOL_SIZE environment variable, for work the OS cannot do asynchronously. That includes most fs calls, dns.lookup, crypto.pbkdf2 and scrypt, and async zlib. Network sockets do not use the pool; they rely on epoll, kqueue, or IOCP.

The senior follow-up: "Why would 50 concurrent password hashes slow down file reads?" Because both compete for the same four pool threads. The fix is to raise the pool size within reason, cap hashing concurrency, or move the work to a dedicated service.

What Blocks the Event Loop? A Debugging Walkthrough

Anything synchronous and expensive on the main thread blocks every request in the process. The usual offenders are JSON.parse or JSON.stringify on large payloads, synchronous crypto or fs.*Sync calls in request paths, catastrophic regular expressions, and large in-memory sorts or loops.

Scenario: "After a release, p99 latency across all endpoints jumps from 80 ms to 2 seconds, but CPU is only at 40 percent overall. What do you do?"

A strong answer walks through steps in order:

  1. Confirm it is loop blocking, not a slow dependency. If every endpoint degrades together, including ones that touch no database, the process itself is stalling. Check event loop delay.
  2. Measure delay directly. monitorEventLoopDelay from node:perf_hooks gives a histogram you can export as a metric.
  3. Profile. Run with --cpu-prof, or attach Chrome DevTools via --inspect, reproduce under load, and look for one wide synchronous frame.
  4. Correlate with the diff. A new export endpoint calling JSON.stringify on 200,000 rows would explain it.
  5. Fix the root cause. Stream the response, paginate, or move the work to a worker thread. Then verify the delay histogram returns to baseline.
const { monitorEventLoopDelay } = require('node:perf_hooks');

const h = monitorEventLoopDelay({ resolution: 20 });
h.enable();

setInterval(() => {
  console.log({
    p50ms: h.percentile(50) / 1e6,
    p99ms: h.percentile(99) / 1e6,
    maxms: h.max / 1e6,
  });
  h.reset();
}, 10_000);

Note why 40 percent CPU is misleading: on a multi-core machine, one fully pinned Node process can look like low total CPU. Explaining that out loud is the kind of detail interviewers remember. Our debugging interview guide covers how to narrate this hypothesis-first approach in any language.

Streams and Backpressure Questions

A Node.js stream is an abstraction for processing data in chunks instead of loading it all into memory. There are four types: Readable, Writable, Duplex, and Transform. Interviewers rarely ask you to list them; they ask why a service ran out of memory.

Backpressure is the mechanism that slows a fast producer when the consumer cannot keep up. Every stream has an internal buffer limit called highWaterMark. Since Node.js 22, the default for byte streams is 64 KiB (it was 16 KiB before), and object-mode streams default to 16 objects. When writable.write() returns false, the buffer is over that limit and you should pause until 'drain'.

The memory leak that is really ignored backpressure

Scenario: "An endpoint exports a 3 GB table to CSV. Memory climbs until the pod is OOM-killed."

The buggy version usually looks like this:

cursor.on('data', (row) => {
  res.write(toCsvLine(row));
});
cursor.on('end', () => res.end());

The database cursor produces rows faster than a slow client can download them. res.write() returns false, the code ignores it, and Node buffers everything in memory. The fix is to let pipeline manage flow:

const { pipeline } = require('node:stream/promises');
const { Transform } = require('node:stream');

app.get('/export', async (req, res) => {
  res.setHeader('Content-Type', 'text/csv');
  const toCsv = new Transform({
    writableObjectMode: true,
    transform(row, _enc, cb) {
      cb(null, toCsvLine(row));
    },
  });
  await pipeline(db.streamRows('SELECT * FROM orders'), toCsv, res);
});

pipeline respects backpressure, propagates errors, and destroys all streams if the client disconnects. Mention that last point: with plain .pipe(), an error in one stream does not close the others, which leaks file descriptors and database connections.

Follow-up questions to expect: how async iterators (for await (const chunk of readable)) handle backpressure (they pull one chunk at a time), and when you would write a custom Transform versus using a library.

Error Handling and Process Management

Errors in Node.js travel through three channels: thrown exceptions, rejected promises, and 'error' events on emitters and streams. An unhandled 'error' event crashes the process, and since Node.js 15 an unhandled promise rejection also crashes it by default.

The question "What should happen on uncaughtException?" has one accepted answer: log it, stop accepting new work, and exit. The process state is unknown after an uncaught exception, so you let your process manager or orchestrator restart it. Trying to keep running is a red flag.

A production-grade shutdown handler shows you have operated real services:

const server = app.listen(3000);

function shutdown(signal) {
  console.log(`received ${signal}, draining`);
  server.close(async () => {
    await db.end();
    process.exit(0);
  });
  setTimeout(() => process.exit(1), 10_000).unref();
}

process.on('SIGTERM', shutdown);
process.on('SIGINT', shutdown);
process.on('unhandledRejection', (err) => {
  console.error('unhandledRejection', err);
  process.exit(1);
});

Explain why SIGTERM matters: Kubernetes sends it before killing a pod, and a service that ignores it drops in-flight requests on every deploy. If the role touches infrastructure, our Kubernetes and Docker interview questions cover the orchestration side, including readiness probes during shutdown.

Two more points interviewers like: distinguish operational errors (timeouts, bad input, a database that is down) from programmer errors (a TypeError from a bug), and use AbortController to cancel outbound requests when a client disconnects or a deadline passes.

Worker Threads vs Cluster: Scaling Across Cores

Worker threads and the cluster module solve different problems, and confusing them is one of the most common mistakes on Node.js interview questions for experienced engineers.

Worker threadsCluster moduleMore containers/pods
UnitThread in the same processSeparate OS processesSeparate processes on separate hosts
MemorySeparate V8 isolates; can share SharedArrayBufferFully separateFully separate
Best forCPU-heavy JS: image resizing, parsing, hashingUsing all cores for HTTP on one VMHorizontal scale and fault isolation
CommunicationpostMessage, transferable objectsIPC via the primary processNetwork, queues
Crash impactWorker error can be caught by the parentOne worker dies, primary restarts itOrchestrator restarts pod

The judgment call: in a containerized setup, most teams run one Node process per container and scale by adding pods, rather than using cluster inside a container. Worker threads are for when a single request needs heavy computation that would otherwise block the loop, ideally through a fixed-size pool so you do not spawn a thread per request.

Neither option speeds up I/O-bound work. If requests wait on a slow database, more threads only add more waiters. Say that explicitly. The broader theory of threads, locks, and shared memory shows up in our concurrency and multithreading interview questions.

Debugging questions like "why is p99 spiking?" are where candidates freeze, because the answer depends on recalling the right tool under pressure. TechScreen listens to the question and shows a structured diagnostic plan on your screen, invisible during screen shares on Zoom, Google Meet, and Teams. Try it on your next Node.js interview with 3 free tokens, no credit card required.

Get started free →

Memory Leak Debugging: The Heap Snapshot Walkthrough

A memory leak in Node.js is memory that stays reachable from a GC root even though your code no longer needs it, so the garbage collector cannot free it. Common causes are unbounded in-memory caches, event listeners added per request and never removed, closures held by long-lived timers, and global maps keyed by request or user ID.

Scenario: "RSS grows about 50 MB per hour and the pod restarts every day. Find the leak."

  1. Confirm the trend. Graph process.memoryUsage().heapUsed over hours. A sawtooth that returns to baseline is normal GC; a rising floor is a leak. If RSS grows but the heap does not, suspect native memory or Buffers.
  2. Capture snapshots. Start with --heapsnapshot-signal=SIGUSR2 or call v8.writeHeapSnapshot(). Take one snapshot, apply load for several minutes, take another.
  3. Compare. In Chrome DevTools Memory tab, use the Comparison view and sort by size delta. A growing count of one object type, such as closures or IncomingMessage, points at the source.
  4. Follow retainers. The retainers panel shows the reference chain keeping objects alive, often ending at a module-level Map or an emitter.
  5. Fix and verify. Add a bound (an LRU with a max size or TTL), remove listeners, or use a WeakMap when keys are objects. Rerun the load and confirm the floor is flat.

Snapshots pause the process and can briefly double memory use, so mention taking them on one instance pulled out of the load balancer. A common shortcut answer, "increase --max-old-space-size," only delays the crash and should be framed that way.

Express, Fastify, and API Design Questions

Express.js interview questions in 2026 still center on middleware order, error handling, and validation, but the Express 5 release changed one classic answer. Per the official Express error handling guide, Express 5 route handlers and middleware that return a rejected promise call next(err) automatically. In Express 4 you needed try/catch or a wrapper library.

app.get('/users/:id', async (req, res) => {
  const user = await users.findById(req.params.id);
  if (!user) return res.status(404).json({ error: 'not_found' });
  res.json(user);
});

app.use((err, req, res, next) => {
  req.log?.error(err);
  res.status(err.status ?? 500).json({ error: 'internal_error' });
});

Two details to mention: error middleware must declare four parameters or Express treats it as normal middleware, and errors thrown inside a plain callback (not a promise) still escape automatic handling.

Questions that separate levels:

  • Middleware order: why must body parsing, authentication, and rate limiting run before route handlers, and the error handler run last?
  • Validation: where do you validate input? A strong answer validates at the edge with a schema (Fastify's built-in JSON Schema, or Zod or similar with Express) and returns consistent error shapes.
  • Fastify trade-offs: schema-driven validation and serialization, plugin encapsulation, and good throughput, versus Express's larger ecosystem and familiarity.
  • API design: pagination with cursors instead of offsets for large tables, idempotency keys on POST endpoints that create payments or orders, and timeouts on every outbound call.

For rate limiting, be ready to say why an in-memory limiter breaks once you run more than one instance, and how a Redis-backed token bucket fixes it. Our rate limiter system design walkthrough covers the algorithms in depth.

Node.js Security Basics Interviewers Check

Security questions in Node.js interviews check whether you know the attacks that are specific to the JavaScript ecosystem, plus the universal ones.

RiskHow it happens in NodeMitigation
Prototype pollutionDeep-merging user JSON with keys like __proto__Validate schemas, use Object.create(null) or Map, avoid unsafe merge utilities
ReDoSA regex with nested quantifiers on user input blocks the loopAvoid backtracking-heavy patterns, cap input length
Command injectionchild_process.exec with interpolated inputUse execFile or spawn with an argument array
Path traversalpath.join(root, req.params.file) with ../Resolve the path and check it stays under the root
SQL/NoSQL injectionString-built queries, or operator objects like { "$gt": "" } in Mongo filtersParameterized queries, schema validation
Supply chainMalicious or compromised npm packagesLockfiles, npm audit, minimal dependencies, review install scripts

Connect ReDoS back to the event loop: one malicious request with a crafted string can freeze the whole process, which is a denial of service, not just a slow request. Also mention security headers (often via helmet in Express), secrets kept out of code and logs, and Node's permission model (--permission), which restricts file system and child process access for a process.

How to Answer Node.js Questions in the Interview

The format of your answer matters as much as its content. For any scenario question, use this order: state your hypothesis, name the measurement that would confirm it, describe the fix, and say how you would verify the fix in production. That shape fits event loop stalls, memory leaks, and backpressure bugs alike.

Practical prep for the week before:

  • Write the event loop ordering snippet from memory and run it on the current LTS.
  • Build a small Express 5 or Fastify service with streaming CSV export, graceful shutdown, and an error handler.
  • Plant a leak (a global array of request objects), then find it with two heap snapshots.
  • Block the loop on purpose with a large JSON.stringify and watch monitorEventLoopDelay catch it.

Narrating while you investigate is a skill in itself, and our guide on how to think out loud in a coding interview has phrasing you can reuse. If your loop opens with a short screen, the technical phone screen guide explains what gets you to the onsite.

When an interviewer asks you to debug a leaking Node.js service live, TechScreen gives you real-time hints on event loop phases, heap snapshots, and stream backpressure, invisible to screen sharing on Zoom, Google Meet, Teams, HackerRank, and CoderPad. Start with 3 free tokens, no credit card needed.

Get started free →

Frequently Asked Questions

What are the most common Node.js interview questions for experienced developers?

Experienced candidates are usually asked to explain the event loop phases and where process.nextTick and promises run, how streams handle backpressure, how to find a memory leak, when to use worker threads versus the cluster module, how errors propagate in async code, and how to secure an Express or Fastify API. Senior loops often add a debugging scenario, such as a service whose p99 latency spikes under load, and grade how you investigate it.

Is Node.js single-threaded?

Your JavaScript runs on a single main thread, but Node.js itself is not single-threaded. libuv maintains a thread pool, four threads by default, for file system work, dns.lookup, some crypto functions, and zlib. Network I/O uses the operating system's non-blocking mechanisms such as epoll and kqueue. You can also start worker threads to run JavaScript in parallel, each with its own event loop and V8 isolate.

What is the difference between process.nextTick and setImmediate?

process.nextTick queues a callback that runs right after the current operation finishes, before the event loop continues and before promise microtasks. setImmediate queues a callback for the check phase, which runs after the poll phase of the current loop iteration. Recursive nextTick calls can starve I/O because the loop never moves on. setImmediate is the safer choice when you want to defer work without blocking incoming requests.

How do you handle backpressure in Node.js streams?

Backpressure happens when a writable stream cannot keep up with a readable one. When writable.write() returns false, the internal buffer has passed its highWaterMark, and you should stop writing until the 'drain' event fires. The simplest correct approach is stream.pipeline from node:stream/promises, which manages backpressure, forwards errors, and destroys every stream in the chain if one fails.

Should I use Express or Fastify for a new Node.js API in 2026?

Both are reasonable. Express 5 is the most widely known framework and now forwards rejected promises from async handlers to error middleware automatically. Fastify is built around JSON Schema validation and fast serialization, which gives you input validation and good throughput by default. In an interview, the better answer explains the trade-off: ecosystem familiarity and hiring pool versus built-in schemas, plugin encapsulation, and performance.

Which Node.js version should I know for interviews in 2026?

Know the current LTS line your target company runs, most likely Node.js 22 or 24, and check the official Node.js release schedule for what is current. Interviewers care more about modern APIs than version trivia: built-in fetch, the node: import prefix, stream/promises, the built-in test runner, AbortController, and the permission model are all fair game.

How do I find a memory leak in a Node.js service?

Confirm the leak first by watching heapUsed or RSS climb across garbage collections under steady traffic. Then take two or three heap snapshots a few minutes apart, using --heapsnapshot-signal or v8.writeHeapSnapshot, and load them in Chrome DevTools. Use the comparison view to find object types whose count keeps growing, then follow the retainers path to the code that holds the references, such as an unbounded cache or listeners that are never removed.

Ready to use AI assistance in your next interview?

TechScreen is the invisible AI assistant trusted by engineers interviewing at Google, Meta, Amazon, and hundreds of other companies. Start with 3 free tokens — no credit card required.

Ace your next interview →