{
  "type": "module",
  "source": "doc/api/best-practices-undici-vs-builtin-fetch.md",
  "modules": [
    {
      "textRaw": "Undici Module vs. Node.js Built-in Fetch",
      "name": "undici_module_vs._node.js_built-in_fetch",
      "type": "module",
      "desc": "<p>Node.js has shipped a built-in <code>fetch()</code> implementation powered by undici since\nNode.js v18. This guide explains the relationship between the <code>undici</code> npm\npackage and the built-in <code>fetch</code>, and when you should install one versus relying\non the other.</p>",
      "modules": [
        {
          "textRaw": "Background",
          "name": "background",
          "type": "module",
          "desc": "<p>The <code>fetch()</code>, <code>Request</code>, <code>Response</code>, <code>Headers</code>, and <code>FormData</code> globals in\nNode.js v18+ are provided by a version of undici that is bundled into Node.js\nitself. You can check which version is bundled with:</p>\n<pre><code class=\"language-js\">console.log(process.versions.undici); // e.g., \"7.5.0\"\n</code></pre>\n<p>When you install undici from npm, you get the full library with all of its\nadditional APIs, and potentially a newer release than what your Node.js version\nbundles.</p>",
          "displayName": "Background"
        },
        {
          "textRaw": "Keep `fetch` and `FormData` from the same implementation",
          "name": "keep_`fetch`_and_`formdata`_from_the_same_implementation",
          "type": "module",
          "desc": "<p>When you send a <code>FormData</code> body, keep <code>fetch</code> and <code>FormData</code> together from the\nsame implementation.</p>\n<p>Use one of these patterns:</p>",
          "modules": [
            {
              "textRaw": "Built-in globals",
              "name": "built-in_globals",
              "type": "module",
              "desc": "<pre><code class=\"language-js\">const body = new FormData()\nbody.set('name', 'some')\nbody.set('someOtherProperty', '8000')\n\nawait fetch('https://example.com', {\n  method: 'POST',\n  body\n})\n</code></pre>",
              "displayName": "Built-in globals"
            },
            {
              "textRaw": "`undici` module imports",
              "name": "`undici`_module_imports",
              "type": "module",
              "desc": "<pre><code class=\"language-js\">import { fetch, FormData } from 'undici'\n\nconst body = new FormData()\nbody.set('name', 'some')\nbody.set('someOtherProperty', '8000')\n\nawait fetch('https://example.com', {\n  method: 'POST',\n  body\n})\n</code></pre>",
              "displayName": "`undici` module imports"
            },
            {
              "textRaw": "`undici.install()` globals",
              "name": "`undici.install()`_globals",
              "type": "module",
              "desc": "<p>If you want the installed <code>undici</code> package to provide the globals, call\n<a href=\"/docs/api/GlobalInstallation.html\"><code>install()</code></a>:</p>\n<pre><code class=\"language-js\">import { install } from 'undici'\n\ninstall()\n\nconst body = new FormData()\nbody.set('name', 'some')\nbody.set('someOtherProperty', '8000')\n\nawait fetch('https://example.com', {\n  method: 'POST',\n  body\n})\n</code></pre>\n<p><code>install()</code> replaces the global <code>fetch</code>, <code>Headers</code>, <code>Response</code>, <code>Request</code>, and\n<code>FormData</code> implementations with undici's versions, and also installs undici's\n<code>WebSocket</code>, <code>CloseEvent</code>, <code>ErrorEvent</code>, <code>MessageEvent</code>, and <code>EventSource</code>\nglobals.</p>\n<p>Avoid mixing implementations in the same request, for example:</p>\n<pre><code class=\"language-js\">import { fetch } from 'undici'\n\nconst body = new FormData()\n\nawait fetch('https://example.com', {\n  method: 'POST',\n  body\n})\n</code></pre>\n<pre><code class=\"language-js\">import { FormData } from 'undici'\n\nconst body = new FormData()\n\nawait fetch('https://example.com', {\n  method: 'POST',\n  body\n})\n</code></pre>\n<p>Those combinations may behave differently across Node.js and undici versions.\nUsing matching pairs keeps multipart handling predictable.</p>",
              "displayName": "`undici.install()` globals"
            }
          ],
          "displayName": "Keep `fetch` and `FormData` from the same implementation"
        },
        {
          "textRaw": "When you do NOT need to install undici",
          "name": "when_you_do_not_need_to_install_undici",
          "type": "module",
          "desc": "<p>If all of the following are true, you can rely on the built-in globals and skip\nadding undici to your dependencies:</p>\n<ul>\n<li>You only need the standard Fetch API (<code>fetch</code>, <code>Request</code>, <code>Response</code>,\n<code>Headers</code>, <code>FormData</code>).</li>\n<li>You are running Node.js v18 or later.</li>\n<li>You do not depend on features or bug fixes introduced in a version of undici\nnewer than the one bundled with your Node.js release.</li>\n<li>You want zero additional runtime dependencies.</li>\n<li>You want cross-platform interoperability with browsers and other runtimes\n(Deno, Bun, Cloudflare Workers, etc.) using the same Fetch API surface.</li>\n</ul>\n<p>This is common in applications that make straightforward HTTP requests or in\nlibraries that target multiple JavaScript runtimes.</p>",
          "displayName": "When you do NOT need to install undici"
        },
        {
          "textRaw": "When you SHOULD install undici",
          "name": "when_you_should_install_undici",
          "type": "module",
          "desc": "<p>Install undici from npm when you need capabilities beyond the standard Fetch API:</p>",
          "modules": [
            {
              "textRaw": "Advanced HTTP APIs",
              "name": "advanced_http_apis",
              "type": "module",
              "desc": "<p>undici exposes <code>request</code>, <code>stream</code>, <code>pipeline</code>, and <code>connect</code> methods that\nprovide lower-level control and significantly better performance than <code>fetch</code>:</p>\n<pre><code class=\"language-js\">import { request } from 'undici';\n\nconst { statusCode, headers, body } = await request('https://example.com');\nconst data = await body.json();\n</code></pre>",
              "displayName": "Advanced HTTP APIs"
            },
            {
              "textRaw": "Connection pooling and dispatchers",
              "name": "connection_pooling_and_dispatchers",
              "type": "module",
              "desc": "<p><code>Client</code>, <code>Pool</code>, <code>BalancedPool</code>, <code>Agent</code>, and their configuration options\nlet you manage connection lifecycle, keep-alive behavior, pipelining depth,\nand concurrency limits:</p>\n<pre><code class=\"language-js\">import { Pool } from 'undici';\n\nconst pool = new Pool('https://example.com', { connections: 10 });\nconst { body } = await pool.request({ path: '/', method: 'GET' });\n</code></pre>",
              "displayName": "Connection pooling and dispatchers"
            },
            {
              "textRaw": "Proxy support",
              "name": "proxy_support",
              "type": "module",
              "desc": "<p><code>ProxyAgent</code> and <code>EnvHttpProxyAgent</code> handle HTTP(S) proxying. Note that\nNode.js v22.21.0+ and v24.0.0+ support environment-variable-based proxy\nconfiguration for the built-in <code>fetch</code> via the <code>--use-env-proxy</code> flag (or\n<code>NODE_USE_ENV_PROXY=1</code>). However, undici's <code>ProxyAgent</code> still provides\nprogrammatic control through the dispatcher API:</p>\n<pre><code class=\"language-js\">import { ProxyAgent, fetch } from 'undici';\n\nconst proxyAgent = new ProxyAgent('https://my-proxy.example.com:8080');\nconst response = await fetch('https://example.com', { dispatcher: proxyAgent });\n</code></pre>",
              "displayName": "Proxy support"
            },
            {
              "textRaw": "Testing and mocking",
              "name": "testing_and_mocking",
              "type": "module",
              "desc": "<p><code>MockAgent</code>, <code>MockClient</code>, and <code>MockPool</code> let you intercept and mock HTTP\nrequests without patching globals or depending on external libraries:</p>\n<pre><code class=\"language-js\">import { MockAgent, setGlobalDispatcher, fetch } from 'undici';\n\nconst mockAgent = new MockAgent();\nsetGlobalDispatcher(mockAgent);\n\nconst pool = mockAgent.get('https://example.com');\npool.intercept({ path: '/api' }).reply(200, { message: 'mocked' });\n</code></pre>",
              "displayName": "Testing and mocking"
            },
            {
              "textRaw": "Interceptors and middleware",
              "name": "interceptors_and_middleware",
              "type": "module",
              "desc": "<p>Custom dispatchers and interceptors (retry, redirect, cache, DNS) give you\nfine-grained control over how requests are processed.</p>",
              "displayName": "Interceptors and middleware"
            },
            {
              "textRaw": "Newer version than what Node.js bundles",
              "name": "newer_version_than_what_node.js_bundles",
              "type": "module",
              "desc": "<p>The npm package often includes features, performance improvements, and bug fixes\nthat have not yet landed in a Node.js release. If you need a specific fix or\nfeature, you can install a newer version directly.</p>",
              "displayName": "Newer version than what Node.js bundles"
            }
          ],
          "displayName": "When you SHOULD install undici"
        },
        {
          "textRaw": "Version compatibility",
          "name": "version_compatibility",
          "type": "module",
          "desc": "<table>\n<thead>\n<tr>\n<th>Node.js version</th>\n<th>Bundled undici version</th>\n<th>Notes</th>\n</tr>\n</thead>\n<tbody>\n<tr>\n<td>v18.x</td>\n<td>~5.x</td>\n<td><code>fetch</code> is experimental (behind <code>--experimental-fetch</code> in early v18)</td>\n</tr>\n<tr>\n<td>v20.x</td>\n<td>~6.x</td>\n<td><code>fetch</code> is stable</td>\n</tr>\n<tr>\n<td>v22.x</td>\n<td>~6.x / ~7.x</td>\n<td><code>fetch</code> is stable</td>\n</tr>\n<tr>\n<td>v24.x</td>\n<td>~7.x</td>\n<td><code>fetch</code> is stable; env-proxy support via <code>--use-env-proxy</code></td>\n</tr>\n</tbody>\n</table>\n<p>You can always check the exact bundled version at runtime with\n<code>process.versions.undici</code>.</p>\n<p>Installing undici from npm does not replace the built-in globals. If you want\nyour installed version to replace the global <code>fetch</code> and related classes, use\n<a href=\"/docs/api/GlobalInstallation.html\"><code>install()</code></a>. Otherwise, import <code>fetch</code>\ndirectly from <code>'undici'</code>:</p>\n<pre><code class=\"language-js\">import { fetch } from 'undici' // uses your installed version, not the built-in\n</code></pre>",
          "displayName": "Version compatibility"
        },
        {
          "textRaw": "Further reading",
          "name": "further_reading",
          "type": "module",
          "desc": "<ul>\n<li><a href=\"/docs/api/Fetch.html\">API Reference: Fetch</a></li>\n<li><a href=\"/docs/api/Client.html\">API Reference: Client</a></li>\n<li><a href=\"/docs/api/Pool.html\">API Reference: Pool</a></li>\n<li><a href=\"/docs/api/ProxyAgent.html\">API Reference: ProxyAgent</a></li>\n<li><a href=\"/docs/api/MockAgent.html\">API Reference: MockAgent</a></li>\n<li><a href=\"/docs/api/GlobalInstallation.html\">API Reference: Global Installation</a></li>\n</ul>",
          "displayName": "Further reading"
        }
      ],
      "displayName": "Undici Module vs. Node.js Built-in Fetch"
    }
  ]
}