Guides

18 Node.js Interview Questions with Sample Answers

Node.js interview questions on the event loop, async patterns, streams, clustering, and security — with concise sample answers.

Practical guideInformational12 min read
18 Node.js Interview Questions with Sample Answers

Put this into action

Turn this guide into better conversations with Articuler

Use this guide as the research layer, then turn the next step into a live networking workflow: search by intent, prep for the conversation, and send outreach that is built for replies.

Try the Articuler workflow

Node.js interviews rarely test whether you can write a server. They test whether you understand *why* a single-threaded runtime can handle thousands of concurrent connections — and whether you can reason about what happens when something blocks.

This guide covers 18 questions that come up across junior, mid, and senior backend and full-stack JavaScript roles. Each one pairs the question with a focused, correct answer you can adapt. We move from fundamentals (event loop, non-blocking I/O) through modules, async patterns, streams, concurrency, Express, security, and performance.

Here's roughly how topics map to seniority:

LevelWhat interviewers probe
JuniorEvent loop basics, require vs import, callbacks vs promises, basic Express routing
MidStreams, EventEmitter, error handling in async code, middleware design, setImmediate vs process.nextTick
SeniorClustering and worker threads, backpressure, memory profiling, security hardening, scaling strategy

Fundamentals: the runtime model

1. Why is Node.js single-threaded, and how does it handle concurrency?

Sample answer: Node.js runs your JavaScript on a single main thread, but it isn't single-threaded all the way down. It hands off I/O work — file reads, network calls, DNS lookups — to libuv, a C library with its own thread pool and an event loop. Your code stays single-threaded, which removes the need for locks and makes reasoning about state simple, while the heavy lifting happens off-thread.

The model works because most server work is I/O-bound, not CPU-bound. While one request waits on a database, the main thread is free to start the next one. Concurrency comes from *not blocking*, not from running JavaScript in parallel.

2. Explain the Node.js event loop and its phases.

Sample answer: The event loop is the mechanism that lets Node schedule callbacks without blocking. It runs in ordered phases on each iteration ("tick"):

  1. Timers — runs setTimeout and setInterval callbacks whose delay has elapsed.
  2. Pending callbacks — runs certain deferred I/O callbacks (e.g. TCP errors).
  3. Poll — retrieves new I/O events and runs their callbacks; Node blocks here when appropriate.
  4. Check — runs setImmediate callbacks.
  5. Close callbacks — runs close event handlers like socket.on('close', ...).

Between every phase, Node fully drains two microtask queues: process.nextTick callbacks first, then resolved Promise callbacks. That ordering is the source of a lot of interview gotchas.

3. What is non-blocking I/O?

Sample answer: A blocking call stops the thread until the operation finishes — read a file synchronously and nothing else runs until the bytes arrive. Non-blocking I/O returns immediately and notifies you later via a callback, promise, or event. Node's standard library is built around this: fs.readFile is non-blocking, fs.readFileSync is not.

The practical rule: avoid the *Sync variants in any request-handling path. One synchronous file read or a tight CPU loop blocks the entire event loop, and every concurrent user waits behind it.

Modules: CommonJS vs ESM

4. What's the difference between require and import?

Sample answer: require is CommonJS — Node's original module system. import is the standard ES module syntax. They differ in more than spelling:

AspectCommonJS (require)ES Modules (import)
LoadingSynchronousAsynchronous
ResolutionRuntimeParsed statically before execution
BindingCopy of the valueLive binding (updates reflect)
Top-level awaitNot supportedSupported
File hint.cjs or default.mjs or "type": "module"
__dirnameAvailableUse import.meta.url instead

Because ES modules resolve statically, they enable tree-shaking and top-level await. CommonJS is still everywhere in older packages, so most real codebases interoperate between the two.

5. How do you use ESM in a Node project?

Sample answer: Either name the file .mjs, or set "type": "module" in package.json so .js files are treated as ES modules. Inside an ES module you lose require, module.exports, __dirname, and __filename. You replace them with import/export, and reconstruct the path helpers from import.meta.url. You can still pull in a CommonJS package with a default import, but you generally can't require an ES module from CommonJS without dynamic import().

Async patterns and error handling

6. Walk through the evolution from callbacks to async/await.

Sample answer: Node started with the error-first callback convention: fn(err, data), where you check err before using data. Nesting these produced "callback hell." Promises flattened the nesting with chainable .then/.catch. async/await is syntactic sugar over Promises that makes async code read top-to-bottom like synchronous code.

// Error-first callback
fs.readFile('config.json', 'utf8', (err, data) => {
  if (err) return handleError(err);
  process(data);
});

// async/await
async function load() {
  try {
    const data = await fs.promises.readFile('config.json', 'utf8');
    return process(data);
  } catch (err) {
    handleError(err);
  }
}

7. How do you handle errors in async Node code?

Sample answer: It depends on the pattern. Error-first callbacks pass the error as the first argument — always check it. Promise chains use .catch(). Inside async functions, wrap awaited calls in try/catch.

The common mistakes interviewers look for: forgetting await (the error escapes your try/catch as an unhandled rejection), and swallowing errors silently. For run-anyway parallelism, Promise.allSettled returns every result regardless of individual failures, while Promise.all short-circuits on the first rejection. At the process level, an uncaught exception should log and exit — recovering in place leaves the app in an undefined state.

8. What's the difference between setImmediate and process.nextTick?

Sample answer: Both defer work, but they fire at different times. process.nextTick runs before the event loop continues to its next phase — effectively as soon as the current operation finishes. setImmediate runs in the check phase, after the poll phase of the current iteration.

process.nextTicksetImmediate
FiresBefore next event loop phaseIn the check phase
QueueMicrotask (drained every tick)Macrotask (one per loop)
RiskRecursion can starve the loopSafe to schedule repeatedly
Use whenYou must run before any I/OYou want to yield to I/O first

The official docs recommend setImmediate in most cases because it's easier to reason about and won't starve I/O.

Streams, buffers, and events

9. What are streams and why use them?

Sample answer: Streams process data in chunks instead of loading everything into memory. Reading a 2 GB file with fs.readFile buffers all 2 GB in RAM; piping it through a read stream uses a small, constant amount. There are four types:

  • Readable — sources you read from (fs.createReadStream, an HTTP request).
  • Writable — destinations you write to (an HTTP response, process.stdout).
  • Duplex — both, like a TCP socket.
  • Transform — a duplex stream that modifies data in transit, like zlib.createGzip().
fs.createReadStream('big.log')
  .pipe(zlib.createGzip())
  .pipe(fs.createWriteStream('big.log.gz'));

pipe (or stream.pipeline) also handles backpressure — pausing the source when the destination can't keep up, which is a strong follow-up topic at the senior level.

10. What is a Buffer?

Sample answer: A Buffer is a fixed-length chunk of raw binary data living outside the V8 heap. JavaScript strings can't represent arbitrary bytes cleanly, so Node uses Buffers for file contents, network packets, and crypto. You convert between Buffers and strings by specifying an encoding ('utf8', 'base64', 'hex'). The key gotcha: a Buffer's size is fixed at allocation, and Buffer.allocUnsafe skips zero-filling for speed, so it can leak old memory contents if you don't overwrite it.

11. What is EventEmitter?

Sample answer: `EventEmitter` is the backbone of Node's event-driven architecture. Objects emit named events, and listeners registered with .on() react to them. Streams, HTTP servers, and many core objects extend it.

import { EventEmitter } from 'node:events';
const bus = new EventEmitter();
bus.on('order', (id) => console.log(`Processing ${id}`));
bus.emit('order', 42);

Two things interviewers probe: the special 'error' event (an emitted 'error' with no listener throws and can crash the process), and the default limit of 10 listeners per event, which warns about possible memory leaks.

Concurrency: clustering and worker threads

12. How do you use all CPU cores in Node?

Sample answer: A single Node process uses one CPU core for JavaScript. To use more, you scale out. The cluster module forks multiple worker *processes* that share a server port, with the primary process load-balancing connections across them. Each worker is a full Node instance with its own memory.

import cluster from 'node:cluster';
import { availableParallelism } from 'node:os';

if (cluster.isPrimary) {
  for (let i = 0; i < availableParallelism(); i++) cluster.fork();
} else {
  startServer();
}

In production, people often delegate this to a process manager (PM2) or a container orchestrator instead of writing cluster code by hand.

13. When would you reach for worker threads instead of clustering?

Sample answer: Worker threads are for CPU-bound work inside a single process — image processing, parsing, encryption — that would otherwise block the event loop. Unlike cluster's separate processes, threads can share memory through SharedArrayBuffer, which makes passing large data cheaper.

ClusterWorker threads
UnitSeparate processesThreads in one process
Best forScaling network I/O across coresCPU-heavy computation
MemoryIsolated per processCan share via SharedArrayBuffer
CommunicationIPC messagesMessagePort / shared memory

The rule of thumb: cluster to scale a web server, worker threads to keep one heavy calculation from freezing everything else.

Express, security, and performance

14. What is middleware in Express?

Sample answer: Middleware is a function with the signature (req, res, next) that runs in order on the request pipeline. Each function can read or modify req/res, end the response, or call next() to pass control onward. Logging, authentication, body parsing, and error handling are all middleware. Error-handling middleware is the special four-argument form (err, req, res, next), and Express routes errors to it when you call next(err).

15. How do you secure a Node/Express application?

Sample answer: Several layers, and naming a few specifics matters more than listing buzzwords:

  • Validate and sanitize input to block injection (SQL/NoSQL) and XSS — use parameterized queries, never string-concatenate user input into queries.
  • Set security headers with a tool like Helmet (HSTS, X-Content-Type-Options, a content security policy).
  • Hash passwords with bcrypt or argon2 — never store plaintext or fast hashes like MD5.
  • Rate-limit auth and public endpoints to slow brute-force and abuse.
  • Keep dependencies patched — run npm audit and watch for known CVEs in transitive packages.
  • Never leak secrets — keep tokens in environment variables, not source control.

16. How do you find and fix a performance bottleneck?

Sample answer: Measure first. Use --prof or --cpu-prof to capture a CPU profile, or clinic.js, and find what's actually hot — guessing wastes time. Common culprits: a synchronous call blocking the event loop, an N+1 query pattern, unbounded concurrency hammering a downstream service, or a memory leak from listeners or caches that never release.

For throughput, cache expensive results, stream large payloads instead of buffering, and offload CPU-heavy work to worker threads. For latency, watch event-loop lag — if it climbs, something synchronous is stealing time from every request.

17. What causes memory leaks in Node, and how do you detect them?

Sample answer: Typical sources are global variables that keep growing, closures holding references longer than needed, forgotten EventEmitter listeners, and unbounded in-memory caches. You detect them by watching heap usage trend upward across requests that should be steady-state, then taking heap snapshots in Chrome DevTools (via --inspect) and comparing them to see which object types accumulate.

18. How do you scale a Node application?

Sample answer: Scale horizontally — run many stateless instances behind a load balancer, keep session state in Redis or a database rather than process memory, and use cluster or container replicas to fill every core. Push slow work to a message queue and background workers so request handlers stay fast. Cache aggressively at every layer. The recurring theme: keep each instance stateless and non-blocking so you can add more of them freely.

From technical screen to offer

Strong answers to these questions get you through the coding round. But the technical screen is only one gate, and it's often not the one that decides the offer.

The fastest path into a backend role is rarely the apply button — it's a 15-minute conversation with the engineering manager who owns the team. Articuler helps jobseekers find that actual hiring manager behind a posting using semantic search across 980M+ profiles, build a Playbook on what that specific person cares about, and send a personalized note that earns roughly 8x the reply rate of a generic cold message. Resumes and clean answers carry you to the door; the right conversation gets you through it.

If you're preparing across the whole JavaScript stack, pair this with the JavaScript interview questions guide for language fundamentals.

Before the interview, it helps to get the rest of your materials in shape too — the software engineer resume guide covers what to put in front of an engineering manager, and how to ace an interview covers the behavioral side that decides most offers.

Next step

Use Articuler to act on what you just read

Start with one concrete goal: investor intros, sales prospects, event meetings, hiring-manager outreach, or expert conversations. Articuler turns that goal into people, prep, and messages.

Start networking with intent

FAQ

What level of Node.js knowledge do interviewers expect?

It scales with the role. Juniors should explain the event loop, non-blocking I/O, and basic async patterns. Mid-level candidates are expected to handle streams, EventEmitter, and async error handling cleanly. Senior candidates should discuss clustering, worker threads, backpressure, security hardening, and how they'd scale a service.

Do I need to memorize the event loop phases?

You should understand them well enough to predict output order — especially how process.nextTick and Promise microtasks drain before setTimeout and setImmediate. Reciting the phase names verbatim matters less than explaining *why* a given snippet logs in the order it does.

Should I know Express for a Node interview?

Usually yes, unless the job explicitly uses another framework like Fastify or NestJS. Be ready to explain middleware, routing, and error-handling middleware. Even if the team uses something else, the middleware pipeline concept transfers directly.

How do I answer a Node question I don't know?

Reason out loud. Connect it to something you do know — if you're unsure about worker threads, explain what you understand about the single-threaded model and where parallelism would help. Interviewers weigh how you think about a problem more than whether you've memorized the API.

Keep reading

More from Guides

Resources