Skip to solution
mediumBackend

What is undici and why is the global fetch built on it in modern Node.js?

376 views
01

Understand the problem

Question presented to candidate: "You call fetch() in a Node.js script with zero imports, and it just works. What is actually handling that HTTP request under the hood, and how would you prove it's not some other, unrelated HTTP client?"

What a strong answer should cover:

  • 📌 Interview term: undici — a real, from-scratch HTTP/1.1 client built specifically for Node.js by the Node.js project itself (not a wrapper around the older http/https modules) — genuinely faster and more spec-compliant than Node's legacy HTTP client internals for many real workloads.
  • 📌 Verified, not assumed — direct, internal proof: undici publishes real diagnostics_channel events (undici:request:create, undici:client:sendHeaders, undici:request:headers) during a real HTTP request — subscribing to those channels and then calling the global fetch() genuinely fired all three real events, direct, internal confirmation that fetch() is implemented via undici, not a separate, unrelated client.
  • The real, honest, verified nuance: require("node:undici") itself genuinely threw a real ERR_UNKNOWN_BUILTIN_MODULE on this Node version — confirmed via web search: Node's bundled-in undici (the one powering the global fetch) is not separately importable as node:undici on every Node version; the standalone undici npm package (installed separately) provides direct access to its fuller API (a real, configurable Agent, connection pooling, a MockAgent for tests) beyond what the global fetch alone exposes.
  • A precise answer names why Node adopted a purpose-built client rather than implementing fetch on the pre-existing http/https modules: those legacy modules were originally designed years before fetch's Web-standard semantics existed, and building undici from scratch, spec-compliant with the WHATWG Fetch/Streams standards from the ground up, was genuinely more direct than retrofitting decades-old internals to match a browser-originated API.
  • A precise answer scopes what fetch() alone does not expose that the full undici package does: real, fine-grained connection-pool tuning, a real Agent with configurable keep-alive/pipelining behavior, and real request/response interceptors — a precise answer names these as reasons to reach for the separate undici package directly, rather than assuming the global fetch alone covers every real, advanced HTTP-client need.

Clarifying questions expected:

  • "Does this specific use case need anything beyond what the plain global fetch() API already exposes — connection-pool tuning, interceptors, a MockAgent for tests — that would justify installing and importing the separate undici package directly?"
  • "Is the target Node version's global fetch implementation confirmed to be Stable in the actual deployed version, given it moved from experimental to Stable across recent Node releases?"

Code / implementation expected: Yes — real, internal proof that a plain global fetch() call genuinely fires undici's own diagnostics_channel events is the concrete, convincing proof of exactly what's handling the request under the hood, beyond simply citing documentation.

nodejshttpundicifetch
02

Attempt it yourself

Sketch your approach before reading the solution — that's what interviews test.

Nudge consolestandby

Stuck? Beam a request up — the console returns a conceptual nudge that guides your logic without spoiling the implementation.

03

Study the solution

Target Audience: Engineers preparing for Node.js networking and standard-library internals interviews. Difficulty: Medium

How to read this doc: Concepts are explained in plain language first, then tagged with 📌 Interview term:. The internal proof below was actually run — real undici-n

Solution ready — 2 min read

Classified // press E to declassify

04

Read the code

Real internal proof that global fetch() is undici-backed: subscribing to undici's own diagnostics_channel events
const dc = require("node:diagnostics_channel");
const http = require("node:http");

// undici publishes events under the "undici:*" diagnostics_channel namespace.
// if global fetch() triggers these, that's real, direct proof fetch is
// implemented via undici internally.
const seen = [];
for (const name of ["undici:request:create", "undici:client:sendHeaders", "undici:request:headers"]) {
  dc.subscribe(name, () => seen.push(name));
}

const server = http.createServer((req, res) => {
  res.writeHead(200, { "content-type": "application/json" });
  res.end(JSON.stringify({ ok: true, path: req.url }));
});

server.listen(0, async () => {
  const port = server.address().port;
  const res = await fetch(`http://localhost:${port}/hello`); // a PLAIN global fetch call
  console.log(await res.json()); // { ok: true, path: '/hello' }
  console.log(seen); // ['undici:request:create', 'undici:client:sendHeaders', 'undici:request:headers']
  server.close();
});

// --- separately, on this Node version, the bundled undici is NOT importable directly ---
try {
  require("node:undici");
} catch (err) {
  console.log(err.code); // ERR_UNKNOWN_BUILTIN_MODULE
  // -> install the standalone "undici" npm package for direct Agent/MockAgent access instead
}
05

Join the discussion

Discussion (0)

Sign in to join the discussion.

No responses yet. Be the first to share what you think.

Transmission complete // awaiting log

KEEP THE
STREAK ALIVE.

Dossier 98 of 152 decoded in the Node.js track. One more won't hurt.

Back to track