# Node.js Interview Questions (2026) with Answers

> 30 Node.js interview questions from junior to senior, each with what a strong answer shows. Practise, then prove the skill with evidence.

Source: https://getlemma.io/skills/nodejs/interview-questions

Node.js interviews test whether you understand what runs on the event loop and what blocks it. Expect questions on async error handling, streams, modules, process lifecycle and security, then memory, scaling and production behaviour for senior roles. Questions run from junior to senior. Each one says what a strong answer covers and the trap a weak answer falls into.

[How Lemma tests Node.js](https://getlemma.io/skills/nodejs)

## Junior Node.js interview questions

1.  ### 1.Node.js is called single-threaded. What does that actually mean?

    What a strong answer shows

    Your JavaScript runs on one main thread with an event loop, while I/O is handed to the operating system or to libuv's thread pool. That is why one process can hold many connections at once but stalls when a callback does heavy computation. A weak answer says Node.js has only one thread in total, which is false.

2.  ### 2.What is the difference between blocking and non-blocking code in Node.js?

    ```
    import fs from "node:fs";

    // Blocks every other request until the read finishes
    const a = fs.readFileSync("big.json", "utf8");

    // Frees the event loop while the read is in progress
    const b = await fs.promises.readFile("big.json", "utf8");
    ```

    What a strong answer shows

    Blocking code holds the main thread until it finishes, so no other request is served meanwhile. Non-blocking calls start the work and continue, and the result arrives through a callback or promise. A strong answer knows that synchronous APIs are fine at startup, and that large JSON.parse calls or tight loops block just like readFileSync.

3.  ### 3.How do callbacks, promises and async/await relate to each other?

    What a strong answer shows

    Node.js core started with error-first callbacks, promises then wrapped a future value, and async/await is syntax over promises. A strong answer converts a callback API with util.promisify or uses the node:fs/promises style modules. It also spots that awaiting in a loop runs operations one by one when they could run together.

4.  ### 4.How do you handle errors in async code?

    What a strong answer shows

    Wrap awaited calls in try/catch, attach catch to promises you do not await, and check the error argument in callbacks. A strong answer knows an unhandled promise rejection crashes the process by default in current Node.js versions. The classic trap is a try/catch around code that throws later inside a callback, which it cannot catch.

5.  ### 5.What is the difference between dependencies and devDependencies, and what do ^ and ~ mean in a version range?

    What a strong answer shows

    dependencies are needed at runtime, while devDependencies are only for building and testing and are skipped in production installs. A caret allows minor and patch updates, and a tilde allows only patch updates. A strong answer adds that below 1.0.0 the caret is stricter, because minor versions may break.

6.  ### 6.What does a lockfile do, and should you commit it?

    What a strong answer shows

    The lockfile records the exact resolved version of every package in the tree, so each install produces the same result. Commit it for applications and install with npm ci or the equivalent frozen-lockfile mode in CI. A weak answer deletes the lockfile to fix install errors, which silently upgrades the whole tree.

7.  ### 7.How do you configure a Node.js app for different environments?

    What a strong answer shows

    Read settings from process.env, validate them once at startup and fail fast if a required one is missing. A strong answer knows every value in process.env is a string, so "false" is truthy unless parsed. Current Node.js can load a file with --env-file, and that file must never be committed.

8.  ### 8.What is a Buffer, and when do you work with one?

    ```
    const s = "héllo";

    s.length; // 5 UTF-16 code units
    Buffer.byteLength(s, "utf8"); // 6 bytes
    Buffer.from(s).toString("base64"); // "aMOpbGxv"
    ```

    What a strong answer shows

    A Buffer is a fixed-size block of raw bytes, used for files, sockets, binary protocols and encodings such as base64. A strong answer separates byte length from string length, because UTF-8 characters can take several bytes. It also knows Buffer.alloc zero-fills memory, while Buffer.allocUnsafe may expose old data.

9.  ### 9.What is a stream, and why use one instead of reading a whole file into memory?

    What a strong answer shows

    A stream processes data in chunks as it arrives, so memory use stays flat whatever the file size. It names the four kinds: readable, writable, duplex and transform. A strong answer gives a real case, such as sending a large download to an HTTP response without loading it first.

10.  ### 10.How does middleware work in an Express app?

    ```
    function requireAuth(req, res, next) {
      if (!req.headers.authorization) {
        return res.status(401).json({ error: "UNAUTHORIZED" });
      }
      next();
    }

    app.get("/me", requireAuth, (req, res) => res.json({ ok: true }));
    ```

    What a strong answer shows

    Middleware are functions that run in order for each request, and each one either ends the response or calls next. An error handler takes four arguments and receives anything passed to next(err). A strong answer knows Express 5 forwards rejected promises from async handlers, while Express 4 needed a wrapper.

## Mid-level Node.js interview questions

1.  ### 11.What order does this code log in, and why?

    ```
    // Run as a CommonJS script
    setTimeout(() => console.log("timeout"), 0);
    setImmediate(() => console.log("immediate"));
    Promise.resolve().then(() => console.log("promise"));
    process.nextTick(() => console.log("nextTick"));
    console.log("sync");
    // sync, nextTick, promise, then timeout and immediate in either order
    ```

    What a strong answer shows

    Synchronous code runs first, then the process.nextTick queue, then promise microtasks, then the timers and check phases. From the main module, the order of a zero timeout and setImmediate is not guaranteed. Inside an I/O callback setImmediate always runs first, and in an ES module the promise logs before nextTick.

2.  ### 12.What runs on the libuv thread pool, and when does its size matter?

    What a strong answer shows

    File system calls, dns.lookup, zlib and some crypto functions such as pbkdf2 and scrypt use the pool, which defaults to four threads. Network sockets do not, because they use the operating system's async I/O. A strong answer explains that slow crypto can starve file reads, and that UV\_THREADPOOL\_SIZE must be set before the pool starts.

3.  ### 13.What is backpressure, and how do you handle it with streams?

    ```
    import { pipeline } from "node:stream/promises";
    import { createReadStream, createWriteStream } from "node:fs";
    import { createGzip } from "node:zlib";

    await pipeline(
      createReadStream("access.log"),
      createGzip(),
      createWriteStream("access.log.gz"),
    );
    ```

    What a strong answer shows

    Backpressure happens when a writer is slower than its reader, and write() returning false is the signal to wait for drain. stream.pipeline handles that for you and destroys every stream on error. A weak answer chains pipe() calls, which forwards no errors and can leak file descriptors.

4.  ### 14.What are the practical differences between CommonJS and ES modules in Node.js?

    What a strong answer shows

    CommonJS loads synchronously with require, while ESM uses static import, supports top-level await and is chosen by .mjs or "type": "module". In ESM there is no \_\_dirname, so use import.meta.dirname or import.meta.url. Recent LTS lines can require() an ES module without top-level await, and importing CommonJS only exposes statically detectable named exports.

5.  ### 15.How do you shut down a Node.js server gracefully?

    ```
    process.on("SIGTERM", () => {
      server.close(async () => {
        await db.end();
        process.exit(0);
      });
      server.closeIdleConnections();
      setTimeout(() => process.exit(1), 10_000).unref();
    });
    ```

    What a strong answer shows

    On SIGTERM, stop accepting connections, let in-flight requests finish, close database pools, then exit, with a hard timeout as a backstop. A strong answer knows idle keep-alive sockets can hold the server open, and that health checks should fail early. It also knows npm start may not forward signals, and Node.js as PID 1 needs its own handlers.

6.  ### 16.What should happen when an uncaughtException or unhandledRejection reaches the process?

    What a strong answer shows

    Log it with context, then exit and let the supervisor restart the process, because state may now be corrupt. A strong answer separates operational errors, such as a timeout you can retry, from programmer errors, which should crash. The trap is a handler that logs and carries on, leaving a broken process serving traffic.

7.  ### 17.What happens if an EventEmitter emits an error event with no listener?

    What a strong answer shows

    Node.js throws the error, which usually crashes the process, so every stream, socket and custom emitter needs an error listener. A strong answer also knows the MaxListenersExceededWarning usually points at listeners added per request and never removed. events.once and events.on give promise and async iterator forms for consuming emitters.

8.  ### 18.How do you handle CPU-heavy work, such as image resizing, in a Node.js service?

    What a strong answer shows

    Move it off the main thread with worker\_threads, a child process or a separate job queue, depending on isolation and scale. Workers share memory through SharedArrayBuffer and transfer data cheaply, while child processes give full isolation. A weak answer suggests the cluster module, which adds processes but still blocks each event loop during the work.

9.  ### 19.How do you validate and sanitise input in a Node.js API?

    What a strong answer shows

    Validate every body, query and header against a schema at the edge, rejecting unknown fields, and limit request body size. Use parameterised queries, encode output for its context, and never pass input to child\_process with a shell. A strong answer names prototype pollution through merging untrusted objects, and ReDoS from risky regular expressions.

10.  ### 20.How should a Node.js service handle secrets such as API keys and database passwords?

    What a strong answer shows

    Inject them at runtime from the platform's secret store or environment, never from the repository or the built image. Keep them out of logs and error reports by redacting known fields. A strong answer plans for rotation, so a leaked key can be replaced without a code change.

11.  ### 21.How do you make outgoing HTTP calls to another service reliable?

    ```
    const res = await fetch(url, { signal: AbortSignal.timeout(5000) });
    if (!res.ok) {
      throw new Error("Upstream failed with status " + res.status);
    }
    const data = await res.json();
    ```

    What a strong answer shows

    Set a timeout on every call, retry only idempotent requests with backoff and jitter, and reuse connections with keep-alive. A strong answer knows the built-in fetch does not reject on 4xx or 5xx, so response.ok must be checked. It also knows that without a timeout one slow dependency can exhaust your own capacity.

12.  ### 22.You need to process ten thousand records that each call an API. How do you avoid overwhelming it?

    ```
    async function mapLimit(items, limit, fn) {
      const results = new Array(items.length);
      let next = 0;
      async function worker() {
        while (next < items.length) {
          const i = next++;
          results[i] = await fn(items[i]);
        }
      }
      await Promise.all(Array.from({ length: limit }, worker));
      return results;
    }
    ```

    What a strong answer shows

    Firing every promise with Promise.all floods the downstream service and memory, and fails fast on the first rejection. Process with a fixed concurrency limit, using a small worker pool or a library such as p-limit. A strong answer picks Promise.allSettled when partial failure is acceptable and records which items failed.

## Senior Node.js interview questions

1.  ### 23.Memory in a production Node.js service grows until it restarts. How do you find the leak?

    What a strong answer shows

    Confirm the heap itself is growing, then compare heap snapshots taken some time apart and look at what is retaining the new objects. Common causes are unbounded caches, listeners added per request and closures holding large objects. A strong answer captures snapshots safely with --heapsnapshot-signal or the inspector, since each snapshot pauses the process.

2.  ### 24.One slow endpoint makes every request slow. How do you find and fix the cause?

    What a strong answer shows

    That pattern points to event loop blocking, so measure loop delay with perf\_hooks.monitorEventLoopDelay and profile with --cpu-prof or the inspector. Typical culprits are synchronous crypto, big JSON work, catastrophic regular expressions and sorting large arrays. Fixes are chunking the work, streaming it or moving it to a worker thread.

3.  ### 25.How do you scale a Node.js API horizontally, and what has to change in the code?

    What a strong answer shows

    Run several identical processes behind a load balancer, which only works if no request depends on memory in one process. Sessions, rate limits, caches and WebSocket fan-out must move to a shared store or a pub/sub backplane. A strong answer prefers one process per container with an orchestrator over the cluster module in that setup.

4.  ### 26.How do you carry a request ID through every log line without passing it to each function?

    ```
    import { AsyncLocalStorage } from "node:async_hooks";
    import { randomUUID } from "node:crypto";

    const ctx = new AsyncLocalStorage();

    app.use((req, res, next) => {
      ctx.run({ requestId: randomUUID() }, next);
    });

    function log(msg) {
      console.log(ctx.getStore()?.requestId, msg);
    }
    ```

    What a strong answer shows

    AsyncLocalStorage keeps a store that follows the async call chain started inside run(). It is the mechanism behind most tracing and logging context in Node.js. A strong answer knows context can be lost through some callback-based libraries, and keeps the stored object small.

5.  ### 27.Behind a load balancer, clients see occasional 502 errors on idle connections. What is going on?

    ```
    const server = app.listen(8080);

    // Must outlive the load balancer's idle timeout
    server.keepAliveTimeout = 65_000;
    server.headersTimeout = 66_000;
    ```

    What a strong answer shows

    The Node.js server closes an idle keep-alive socket while the load balancer still thinks it is open and sends a request down it. Set server.keepAliveTimeout longer than the load balancer's idle timeout, and headersTimeout above that. A strong answer also sets requestTimeout to stop slow clients holding sockets.

6.  ### 28.How do you reduce the risk that comes from third-party npm packages?

    What a strong answer shows

    Keep dependencies few, commit the lockfile, review new packages and their maintainers, and run npm audit or an equivalent scanner in CI. A strong answer treats install scripts as code execution and disables or allowlists them. It also covers update tooling, package provenance and the cost of pinning versions that never get patched.

7.  ### 29.How would you run background jobs alongside a Node.js API safely?

    What a strong answer shows

    Put jobs on a durable queue or database table, make every handler idempotent and retry with backoff and a dead-letter state. With several instances, use locks or the queue's own claiming so each job runs once. A strong answer stops taking new jobs on shutdown and keeps long CPU work out of the API process.

8.  ### 30.How do you choose and upgrade the Node.js version a service runs on?

    What a strong answer shows

    Run an even-numbered release in Active or Maintenance LTS and plan the upgrade before it reaches end of life. Pin the version in engines, .nvmrc and the container image, so local, CI and production match. A strong answer reads the changelog for breaking changes and native module rebuilds, then rolls out gradually.

![Painting of an olive grove in soft sunlight with hills beyond](https://cdn.sanity.io/images/islu4peo/production/613eb3f93566423c9adf45246f2cf1a61437c04c-1680x1120.jpg?w=1080&fm=webp&q=68&fit=max)

## Prove it instead of claiming it.

Answers show you can talk about Node.js. A dated test result and live sessions on a Skills Passport show you can do it. Lemma is in early access.

## Node.js interviews: common questions

### Do Node.js interviews expect a specific framework?

Most interviews care about Node.js itself: the event loop, streams, error handling and production behaviour. Framework questions usually test concepts that carry over, such as middleware order and request validation. Knowing one framework well is enough if you can explain what it does underneath.

### Which Node.js version should I know for a backend interview?

Know the current LTS line and the features it ships, such as built-in fetch, the node:test runner and --env-file. Interviewers rarely ask about release numbers. They do notice answers built on outdated patterns, such as callbacks everywhere or a missing unhandled rejection policy.

### How can I prove Node.js backend skills without production experience?

Build a small service that handles shutdown, validation, timeouts and streaming properly, and be ready to explain each decision. That gives interviewers something concrete to probe. Lemma's adaptive skills test for Node.js asks 15 to 25 questions that adjust to your answers and records a dated level you can share.

## More interview questions

[Browse the skills directory](https://getlemma.io/skills)

-   [SQL interview questionsJunior to senior, with what a strong answer shows](https://getlemma.io/skills/sql/interview-questions)
-   [Product management interview questionsJunior to senior, with what a strong answer shows](https://getlemma.io/skills/product-management/interview-questions)
-   [Python interview questionsJunior to senior, with what a strong answer shows](https://getlemma.io/skills/python/interview-questions)
-   [React interview questionsJunior to senior, with what a strong answer shows](https://getlemma.io/skills/react/interview-questions)
-   [System design interview questionsJunior to senior, with what a strong answer shows](https://getlemma.io/skills/system-design/interview-questions)
-   [JavaScript interview questionsJunior to senior, with what a strong answer shows](https://getlemma.io/skills/javascript/interview-questions)
-   [AWS interview questionsJunior to senior, with what a strong answer shows](https://getlemma.io/skills/aws/interview-questions)
-   [TypeScript interview questionsJunior to senior, with what a strong answer shows](https://getlemma.io/skills/typescript/interview-questions)
-   [Docker interview questionsJunior to senior, with what a strong answer shows](https://getlemma.io/skills/docker/interview-questions)

Practising answers is half of it; the other half is showing the skill. See [how Lemma tests Node.js](https://getlemma.io/skills/nodejs), how [adaptive skills tests](https://getlemma.io/product/assessments) work, or [how interviewers assess technical skills](https://getlemma.io/blog/assess-technical-skills-in-interview).
