gitGood.dev
Back to Blog

Top 50 Node.js Interview Questions in 2026 (With Answers and Code)

P
Patrick Wilson
11 min read

Top 50 Node.js Interview Questions in 2026 (With Answers and Code)

Node.js is still the default backend at most fast-moving companies in 2026. Even shops that started "we'll go all-in on Go" usually have a Node service or three. Interviewers want to see that you understand the event loop, can debug a memory leak, and know what changed in modern Node (22 LTS).

These are the 50 questions that actually came up in 2025-2026 interviews. Compact answers, real code, no filler.


Runtime and Event Loop (1-10)

1. What is the Node.js event loop?

A single-threaded loop that processes async callbacks. It runs in phases: timers, pending callbacks, idle/prepare, poll (I/O), check (setImmediate), close. Microtasks (promises, queueMicrotask, process.nextTick) drain between every phase.

2. setImmediate vs setTimeout(fn, 0) vs process.nextTick?

  • process.nextTick runs before the event loop continues - highest priority. Abusing it starves I/O.
  • setImmediate runs in the check phase, after I/O.
  • setTimeout(fn, 0) runs in the timers phase, with a minimum 1ms delay in practice.

Inside an I/O callback, setImmediate always fires before setTimeout(fn, 0). Outside one, the order is non-deterministic.

3. Is Node.js single-threaded?

The JS execution is single-threaded. Under the hood, libuv runs a thread pool (default 4 threads) for filesystem ops, DNS, crypto, and zlib. You can also spin worker threads for CPU-bound JS.

4. What's the difference between blocking and non-blocking?

Blocking calls (fs.readFileSync) hold the event loop until they finish. Non-blocking calls return immediately and complete via a callback or promise. One blocking call on a hot path tanks throughput for every request.

5. How do you debug a stuck event loop?

--inspect + Chrome DevTools, or node --prof. For production: clinic.js (doctor, flame, bubbleprof). The event-loop-lag symptom is requests piling up while CPU is high - usually a sync hot loop or a giant JSON.parse.

6. What are worker threads and when do you use them?

worker_threads for CPU-bound work that would block the main loop: image processing, crypto, ML inference. Communication via MessagePort or shared SharedArrayBuffer. Don't use them for I/O - that's what async is for.

7. What's the difference between cluster and worker threads?

Cluster forks separate Node processes that share a port via the master. Worker threads are threads inside one process sharing memory. Cluster for scaling I/O-bound services across cores; worker threads for CPU-bound tasks within a service.

8. Explain require vs import.

require is CommonJS - synchronous, returns the module's exports object. import is ESM - async-graph, supports top-level await, static analysis, tree-shaking. Modern Node supports both. New code in 2026 should be ESM unless a tooling constraint forces CJS.

9. What's the module resolution algorithm?

For require('foo'): check core modules, then ./node_modules/foo walking up the tree. For require('./foo'): try ./foo.js, ./foo.json, ./foo/index.js. ESM adds package.json exports resolution and explicit file extensions.

10. What does package.json "type": "module" do?

It tells Node to treat .js files in that package as ESM by default. Use .cjs to force CommonJS within an ESM package and .mjs to force ESM in a CJS package.


Async Patterns (11-20)

11. Callback hell vs promises vs async/await?

Callbacks: nested, hard to read, error handling per call. Promises: chainable, single error path with .catch. Async/await: linear code, try/catch works, but you must await or you create unhandled rejections.

12. What is an unhandled promise rejection?

A rejected promise with no .catch handler. In modern Node it crashes the process by default (--unhandled-rejections=throw). Always handle rejections, or attach a global process.on('unhandledRejection') for last-resort logging.

13. How does Promise.all differ from Promise.allSettled?

Promise.all rejects on the first rejection - the rest still run, but you lose their results. Promise.allSettled waits for all and gives you a status array. Use allSettled when partial failure is fine.

14. What's Promise.race and Promise.any?

race resolves/rejects with the first settled promise (success OR failure). any resolves with the first fulfilled one and only rejects if all reject. Useful for fastest-of-N or fallback patterns.

15. How do you add a timeout to a promise?

function withTimeout(p, ms) {
  return Promise.race([
    p,
    new Promise((_, rej) => setTimeout(() => rej(new Error('timeout')), ms))
  ]);
}

In modern Node, prefer AbortSignal.timeout(ms) passed into APIs that accept it (fetch, fs).

16. What is async_hooks?

A core API to track the lifetime of async resources. Used by tracing libraries (OpenTelemetry, Sentry) to correlate logs across async boundaries. Has nontrivial perf cost - use sparingly in hot paths.

17. What does process.nextTick do?

Queues a callback to run before the event loop continues. Microtask priority - runs before promises in some Node versions. Abuse causes I/O starvation.

18. How do you parallelize 100 async calls without overwhelming a server?

Use a concurrency limiter. p-limit is the standard library. Pattern:

import pLimit from 'p-limit';
const limit = pLimit(10);
const results = await Promise.all(items.map(i => limit(() => fetchOne(i))));

19. What's the difference between setImmediate and queueMicrotask?

queueMicrotask runs in the microtask queue, drained between phases - much sooner than setImmediate, which waits for the check phase. Use queueMicrotask when you want "as soon as the current stack unwinds."

20. What happens if you forget to await a promise?

The promise still runs but you don't see its result or errors in the calling scope. If it rejects, you get an unhandled rejection. The function returns before the work finishes - common cause of "the test passed but the data wasn't written" bugs.


Streams and Buffers (21-30)

21. What are the stream types?

Readable (sources), Writable (sinks), Duplex (both), Transform (Duplex with transformation). Examples: fs.createReadStream is Readable, process.stdin is Readable, zlib.createGzip is Transform.

22. Why use streams instead of buffering?

Memory. A 2GB file fs.readFile'd crashes a 512MB container. Streamed, it uses ~64KB at a time. Streams also start producing output immediately - lower latency for the first byte.

23. Explain backpressure.

When a consumer is slower than a producer, data piles up in memory. Streams handle this with .write() returning false and emitting drain when ready for more. pipeline() handles backpressure for you.

24. Why use pipeline() over .pipe()?

pipeline propagates errors and cleans up all streams on failure. pipe silently drops errors and leaks file descriptors when something breaks mid-stream. New code should use pipeline.

import { pipeline } from 'node:stream/promises';
await pipeline(fs.createReadStream('in'), gzip, fs.createWriteStream('out.gz'));

25. What's a Buffer?

A fixed-length byte array outside the V8 heap. Used for binary data: file contents, network packets, crypto. Buffer.from, Buffer.alloc, Buffer.concat are the common constructors.

26. Buffer.alloc vs Buffer.allocUnsafe?

alloc zero-fills - safe but slower. allocUnsafe doesn't - fast but the buffer may contain old memory. Use allocUnsafe only when you immediately overwrite the entire buffer.

27. How do you handle a CSV file too big to fit in memory?

Stream it. csv-parse or papaparse both have streaming APIs. Pipe the file stream through the parser, write rows in batches.

28. What is Readable.from?

Constructs a Readable stream from an iterable or async iterable. Essential for adapting generators or DB cursors into the stream pipeline.

29. Web Streams vs Node Streams?

Web Streams (WHATWG: ReadableStream, WritableStream, TransformStream) are the cross-platform standard, used in fetch and the browser. Node has both APIs and converters between them. New code that crosses runtimes (Edge, workers) should prefer Web Streams.

30. How do you turn a stream into an async iterable?

Already is one. for await (const chunk of stream) {...} works on any Readable stream.


HTTP, Networking, and Frameworks (31-40)

31. How does the built-in http module differ from Express/Fastify?

http is bare metal - no routing, no body parsing, no middleware. Express adds middleware/routing on top. Fastify is faster (schema-based serialization, async by default). Most production apps in 2026 use Fastify or Hono for performance, Express for legacy.

32. What is keep-alive and why does it matter?

Keep-alive reuses a TCP connection for multiple HTTP requests. Without it, every request pays a TCP+TLS handshake (~100ms of latency). Node's http.Agent defaults to keep-alive in modern versions; verify with httpAgent.keepAlive = true.

33. How do you handle large file uploads?

Stream the request body. busboy or formidable both support streaming. Don't buffer the whole file - 100 concurrent 1GB uploads = OOM. For S3, use multipart upload directly.

34. What is HTTP/2 and does Node support it?

Yes, node:http2. HTTP/2 multiplexes streams over one connection, supports server push (mostly deprecated), and uses HPACK for header compression. Most production traffic is now HTTP/2 or HTTP/3 between client and CDN.

35. How do you handle WebSockets in Node?

ws is the standard library. socket.io adds rooms, fallbacks, and reconnection. For Cloudflare/edge, prefer ws or the built-in WebSocket - socket.io doesn't run on edge runtimes.

36. What's the difference between Express middleware and Fastify hooks?

Express middleware is sequential: (req, res, next) => {}. Fastify hooks are stage-specific (onRequest, preHandler, preSerialization, onResponse) and async by default. Fastify's design avoids the next-as-control-flow trap.

37. How do you implement graceful shutdown?

const server = app.listen(3000);
process.on('SIGTERM', async () => {
  server.close();
  await db.disconnect();
  process.exit(0);
});

server.close() stops accepting new connections but waits for in-flight requests. Set a timeout if you need a hard cap.

38. What is cluster and when would you use it?

cluster forks one Node process per CPU core, all sharing a port. For an I/O-bound app you usually don't need it - the event loop saturates one core's work fast enough. Use it when CPU on a single process is the bottleneck. PM2 or systemd usually wrap it.

39. How do you do rate limiting?

In-memory: express-rate-limit for single instance. Distributed: Redis-backed (rate-limiter-flexible) so all instances share counts. Token bucket and sliding window are the common algorithms.

40. CORS - what does the middleware actually do?

Sets Access-Control-Allow-* headers and handles preflight OPTIONS requests. The browser does the enforcement, not the server - CORS is not a security boundary, it's a browser policy.


Performance, Memory, and Production (41-50)

41. How do you find a memory leak?

Take heap snapshots over time (v8.writeHeapSnapshot()), diff them in Chrome DevTools. Common causes: closures holding large scope, unbounded caches, listeners on long-lived event emitters, retained timers.

42. What is --max-old-space-size?

V8's heap size limit. Default ~1.5GB on 64-bit. Production containers usually set it explicitly: node --max-old-space-size=4096 server.js. Set it ~80% of the container memory limit.

43. How do you profile CPU?

node --prof writes V8 logs, processed by --prof-process. clinic flame is friendlier. For production: enable continuous profiling (Pyroscope, Datadog) - sampling profilers have low overhead.

44. What does --inspect do?

Opens a debug port (default 9229) for Chrome DevTools or VS Code to attach. --inspect-brk pauses on the first line so you can attach before any code runs.

45. How do you handle uncaught exceptions?

Don't keep running after one. The official guidance: log the error, do crash reporting, then exit. Process managers (systemd, PM2, Kubernetes) restart you. Trying to "recover" risks running with corrupted state.

process.on('uncaughtException', (err) => {
  logger.fatal(err);
  process.exit(1);
});

46. What's the difference between console.log and a real logger?

console.log is synchronous on TTY (slow), formats poorly, has no log levels, and isn't structured. Use pino (fast, JSON output) or winston for production. JSON logs go straight into Datadog, CloudWatch, Loki without parsing.

47. How do you secure a Node API?

  • TLS termination at the edge (don't roll your own).
  • Helmet for sensible HTTP headers.
  • Rate limiting at the edge AND the app.
  • Validate every input (Zod, Joi, ajv).
  • Parameterize DB queries (no string concat).
  • Run npm audit in CI; pin dependencies.

48. What's prototype pollution?

Setting __proto__ or prototype properties on objects from untrusted input mutates Object.prototype - poisoning every object in the process. Mitigate by validating input shape (Zod) and using Object.create(null) for maps.

49. How do you handle secrets in production?

Environment variables read at boot, sourced from a secret manager (AWS Secrets Manager, Vault, GCP Secret Manager). Never commit .env. Don't log them - sanitize logger middleware.

50. What changed in Node 22 LTS?

  • Stable WebSocket client (no ws needed for outbound).
  • --watch mode for dev (replaces nodemon for many cases).
  • Built-in test runner (node --test) is production-ready.
  • require() of ESM is now stable.
  • node:sqlite shipped (no extra dep for embedded SQLite).
  • Faster startup, better V8 inlining.

How to Use These

Don't memorize. For the questions you don't know, write code in a REPL until you can answer them in your own words. Interviewers can tell the difference between "I read this" and "I built something with this."

The patterns matter more than the trivia: backpressure, graceful shutdown, error propagation, the event loop. Know those cold and the specific APIs follow.