ShopperCove
Menu
All writingBlogTopicsCategoriesAboutRSS
Blog
Categories
Observability & SRE62All categories
About

Plate 27

  1. Blog

undici vs node-fetch: 31k vs 7.4k Req/s on Node 22

Aditya Challa·5 October 2026·6 min read

Hands-on
On this page
  1. What I tested
  2. How much faster is undici on Node 22?
  3. What does undici cost in bundle size?
  4. Do they behave the same?
  5. Which one should you pick?
  6. Sources
  7. Related

I load-tested both HTTP clients on this box against the same local keep-alive server: undici 8.11.2's request() handled about 31,000–36,000 requests per second, while node-fetch 3.3.2 handled about 7,200–7,400, roughly 4–5× fewer. undici's own fetch() landed near 13,000 req/s, but the full undici bundle cost 173,375 bytes gzip versus 26,012 bytes for node-fetch.

Short answer: on Node 22.19 or newer, use undici, and reach for request() on hot server-to-server paths. Keep node-fetch only for older Node versions or code that depends on its Node-stream body. No affiliate links in this post.

What I tested

  • Machine: 8 vCPU Intel Xeon / 15 GB RAM shared Linux cloud box, tested 6 Oct 2026 about 02:30 IST.
  • Versions: undici 8.11.2, node-fetch 3.3.2 (both the npm latest that day), Node 22.20.0 (whose built-in fetch is undici 6.21.2), esbuild 0.28.2 node ESM minify + gzip -9.
  • Load: a local Node http server returning a 764-byte JSON body. 5,000 GET requests at 50 concurrent, JSON parsed and counted for parity, one warm-up round, median of 7 rounds, two processes with the client order reversed. undici used an Agent with 50 connections; node-fetch used http.Agent with keepAlive and 50 sockets; Node's global fetch used its defaults. Scripts and JSON in /workspace/bench-undici-node-fetch/.
  • Findings: request() 35,626 / 30,934 req/s, undici fetch() 13,321 / 13,039, node-fetch 7,176 / 7,436, global fetch 7,911 / 10,827. Bundle (minified / gzip): undici 553,516 / 173,375 B from 111 input files; node-fetch 92,583 / 26,012 B from 22. On disk: undici about 2.5 MB with no dependencies; node-fetch plus its 5 dependencies about 9.4 MB, most of it web-streams-polyfill.
  • One surprise: neither undici 8's Response nor node-fetch's passed an instanceof check against Node 22's global Response, because the built-in fetch ships its own older undici copy.
  • Not tested: HTTP/2, TLS, proxies (ProxyAgent), large uploads or streaming downloads, retries and interceptors, real network latency, Bun or Deno, and Node 18/20, which undici 8 does not support.

How much faster is undici on Node 22?

5,000 GETs, 50 concurrent (median of 7)Run 1 req/sRun 2 req/sRun 1 / Run 2 ms
undici request()35,62630,934140.3 / 161.6
undici fetch()13,32113,039375.3 / 383.5
Node 22 global fetch (undici 6.21.2)7,91110,827632.0 / 461.8
node-fetch 3.3.27,1767,436696.8 / 672.4

Run 1 went request, fetch, node-fetch, global; run 2 reversed it. request() beat node-fetch by 4.2–5.0×, and undici fetch() beat it by about 1.8×. The global fetch number moved a lot with order, so treat it as a range. On loopback this is mostly client overhead; over a real network, latency shrinks the gap. Why both clients were given keep-alive pools is shown with nginx numbers in HTTP keep-alive vs Connection: close. If you mock these calls in tests, nock vs msw measured how mocking cost differs between fetch and Node's http module.

What does undici cost in bundle size?

Build (esbuild node minify, gzip -9)MinifiedgzipInput files
undici 8.11.2 (fetch, request, Agent)553,516 B173,375 B111
node-fetch 3.3.292,583 B26,012 B22

undici is about 6.7× bigger gzipped when you bundle it. That matters for serverless cold starts or a single-file CLI, and not at all if you rely on Node's built-in fetch and bundle nothing. Track it the same way as in size-limit vs bundlesize. If you want a thin wrapper on top of fetch instead, compare ofetch vs ky and ky vs axios.

Do they behave the same?

Check (local server)node-fetch 3.3.2undici fetch()undici request()
Body typeNode PassThrough streamweb ReadableStreamBodyReadable (Node stream)
404 responsestatus 404, ok false, no throwstatus 404, ok false, no throwstatusCode 404, no throw
302 redirectfollowed, redirected truefollowed, redirected truereturns 302, not followed by default
instanceof global Responsefalsefalsenot a Response
Node support12.20+ (ESM only)22.19+22.19+

The redirect row is the one that bites people moving from fetch to request(): you get the raw 302 and have to follow it yourself or add undici's redirect interceptor. node-fetch's body is a Node stream, so code that pipes res.body into fs streams works as is; undici fetch() gives you a web stream instead. URL building around either client is covered in ufo vs URL, and safe JSON parsing of responses in destr vs superjson.

Which one should you pick?

SituationMy pick
Hot server-to-server calls on Node 22.19+undici request()
Standard fetch API, newer than the built-in copyundici fetch()
Just need fetch on Node 22 with no dependencyNode's global fetch
Stuck on Node 18 or 20, or need a Node-stream bodynode-fetch
Bundled CLI or serverless where size beats speednode-fetch or the global fetch

Bottom line: undici 8.11.2's request() moved about 31,000–36,000 req/s on this box versus about 7,200–7,400 for node-fetch 3.3.2, and even undici's fetch() was about 1.8× faster, at the price of a 173 KB gzip bundle versus 26 KB. Who should not switch: projects pinned below Node 22.19, or code built around node-fetch's Node-stream body and automatic redirects that would need rework for request(). Who should not stay on node-fetch: new Node 22 services, where the global fetch or undici covers the same API without a polyfill.

How this was made: I installed both packages on the ShopperCove box, ran a local keep-alive HTTP server, fired 5,000 requests at 50 concurrent through each client in two order-reversed processes, bundled each client with esbuild, and checked body types, 404 handling and redirects against the same server. The JSON results sit next to the scripts. The write-up was drafted with AI help and checked against that output.

Sources

  • https://github.com/nodejs/undici
  • https://undici.nodejs.org/
  • https://github.com/node-fetch/node-fetch
  • https://www.npmjs.com/package/undici
  • https://www.npmjs.com/package/node-fetch
  • https://nodejs.org/api/globals.html

Related

  • https://www.shoppercove.com/blog/ofetch-vs-ky
  • https://www.shoppercove.com/blog/ky-vs-axios
  • https://www.shoppercove.com/blog/msw-vs-nock
  • https://www.shoppercove.com/blog/http-keepalive-vs-connection-close-lab
  • https://www.shoppercove.com/blog/size-limit-vs-bundlesize
  • https://www.shoppercove.com/blog/ufo-vs-url
  • https://www.shoppercove.com/blog/destr-vs-superjson
  • https://www.shoppercove.com/blog/tinyexec-vs-execa
undicinode-fetchnode.jshttp clientperformance benchmarkfetch apibundle size

Lab evidence

What I found running this

Hands-on on ShopperCove box 6 Oct 2026 ~02:30 IST (8 vCPU Intel Xeon / 15 GB shared Linux, Node 22.20.0, built-in fetch = undici 6.21.2). undici 8.11.2 vs node-fetch 3.3.2. Load: local keep-alive http server, 764 B JSON, 5,000 GETs x 50 concurrent, warm-up + 7-round median, order-reversed (req/s): request() 35,626/30,934; undici fetch() 13,321/13,039; node-fetch 7,176/7,436; global fetch 7,911/10,827. esbuild 0.28.2 node minify + gzip -9: 553,516/173,375 B (111 inputs) vs 92,583/26,012 B (22). Disk ~2.5 MB 0 deps vs ~9.4 MB with 5 deps. Behavior: request() does not follow 302 by default; both fetches follow; no client throws on 404; node-fetch body is a Node PassThrough, undici fetch a web ReadableStream. undici 8 needs Node >=22.19. Not tested: HTTP/2, TLS, ProxyAgent, streaming, interceptors, real network, Bun/Deno. No affiliate.

Notes when a lab post goes up

Occasional email for new hands-on reviews. No sequence and no sponsors.

Related links

  • Plate 91

    tinypool vs piscina: 5.4 KB vs 10 KB Gzip

    5 Oct 2026

  • Plate 26

    Picocolors vs Chalk: 1.0 KB vs 3.4 KB Gzip

    5 Oct 2026

  • Plate 64

    Shiki vs Prism.js: 72 KB vs 8 KB, 36 ms vs 3 ms

    5 Oct 2026

On this page

  1. What I tested
  2. How much faster is undici on Node 22?
  3. What does undici cost in bundle size?
  4. Do they behave the same?
  5. Which one should you pick?
  6. Sources
  7. Related
All writingBlogCategoriesTopicsAboutPrivacyRSS

© 2026 ShopperCove