{
  "type": "module",
  "source": "doc/api/api-interceptors.md",
  "modules": [
    {
      "textRaw": "Interceptors",
      "name": "interceptors",
      "type": "module",
      "desc": "<p>Undici ships with a set of built-in interceptors that can be composed via\n<code>dispatcher.compose()</code> to add cross-cutting behaviour such as automatic\nretries, response decompression, redirect following, DNS caching, and more.</p>",
      "modules": [
        {
          "textRaw": "Usage",
          "name": "usage",
          "type": "module",
          "desc": "<pre><code class=\"language-js\">import { Agent, interceptors } from 'undici'\n\nconst { retry, redirect, decompress, dump, responseError, dns, cache, deduplicate } = interceptors\n\nconst agent = new Agent().compose([\n  retry({ maxRetries: 3 }),\n  redirect({ maxRedirections: 5 }),\n  decompress()\n])\n\nconst response = await agent.request({ origin: 'https://example.com', path: '/', method: 'GET' })\n</code></pre>\n<p>You can also apply interceptors to a single <code>Client</code> or <code>Pool</code>:</p>\n<pre><code class=\"language-js\">import { Client, interceptors } from 'undici'\n\nconst client = new Client('https://example.com').compose(\n  interceptors.retry({ maxRetries: 2 })\n)\n</code></pre>\n<hr>",
          "displayName": "Usage"
        },
        {
          "textRaw": "Composing multiple interceptors",
          "name": "composing_multiple_interceptors",
          "type": "module",
          "desc": "<p>Interceptors are applied in the order they appear in the <code>compose()</code> call.\nThe first interceptor in the array wraps the outermost layer.</p>\n<pre><code class=\"language-js\">import { Agent, interceptors } from 'undici'\n\nconst agent = new Agent().compose([\n  interceptors.dns({ maxTTL: 30_000 }),\n  interceptors.retry({ maxRetries: 3 }),\n  interceptors.redirect({ maxRedirections: 5 }),\n  interceptors.decompress(),\n  interceptors.responseError()\n])\n</code></pre>\n<p>In the example above the request flow is:</p>\n<ol>\n<li><strong>dns</strong> — resolves and caches the target IP</li>\n<li><strong>retry</strong> — retries the dispatch on transient failures</li>\n<li><strong>redirect</strong> — follows any 3xx redirects</li>\n<li><strong>decompress</strong> — decompresses the response body</li>\n<li><strong>responseError</strong> — converts 4xx/5xx into thrown errors</li>\n</ol>",
          "displayName": "Composing multiple interceptors"
        }
      ],
      "methods": [
        {
          "textRaw": "`interceptors.dump([opts])`",
          "name": "dump",
          "type": "method",
          "signatures": [
            {
              "params": [
                {
                  "textRaw": "`opts` {Object} (optional)",
                  "name": "opts",
                  "type": "Object",
                  "desc": "(optional)",
                  "options": [
                    {
                      "textRaw": "`maxSize` {number} Maximum number of bytes to read and discard. Responses whose `Content-Length` exceeds this value are aborted. **Default:** `1_048_576` (1 MiB).",
                      "name": "maxSize",
                      "type": "number",
                      "default": "`1_048_576` (1 MiB)",
                      "desc": "Maximum number of bytes to read and discard. Responses whose `Content-Length` exceeds this value are aborted."
                    }
                  ],
                  "optional": true
                }
              ]
            }
          ],
          "desc": "<p>Reads and discards the response body up to a configurable size limit. Useful\nfor keeping a connection alive after an error response without reading the\nbody yourself.</p>\n<p><strong>Parameters</strong></p>\n<p>Per-request override: set <code>dumpMaxSize</code> on the dispatch options to override\nthe global <code>maxSize</code> for a specific request.</p>\n<p><strong>Returns:</strong> <a href=\"Dispatcher.html#class-dispatcherdispatchercomposeinterceptor\"><code>&#x3C;Dispatcher.DispatcherComposeInterceptor></code></a></p>\n<p><strong>Example</strong></p>\n<pre><code class=\"language-js\">import { Agent, interceptors } from 'undici'\n\nconst agent = new Agent().compose(\n  interceptors.dump({ maxSize: 128 * 1024 }) // discard up to 128 KiB\n)\n</code></pre>\n<hr>"
        },
        {
          "textRaw": "`interceptors.retry([opts])`",
          "name": "retry",
          "type": "method",
          "signatures": [
            {
              "params": [
                {
                  "textRaw": "`opts` {RetryHandler.RetryOptions} (optional) Global retry options applied to every request. Individual requests can override via `opts.retryOptions`. See `RetryOptions` for the full list of accepted fields.",
                  "name": "opts",
                  "type": "RetryHandler.RetryOptions",
                  "desc": "(optional) Global retry options applied to every request. Individual requests can override via `opts.retryOptions`. See `RetryOptions` for the full list of accepted fields.",
                  "optional": true
                }
              ]
            }
          ],
          "desc": "<p>Automatically retries failed requests using the same options accepted by\n<a href=\"./RetryHandler.html\"><code>RetryHandler</code></a>.</p>\n<p><strong>Parameters</strong></p>\n<p><strong>Returns:</strong> <a href=\"Dispatcher.html#class-dispatcherdispatchercomposeinterceptor\"><code>&#x3C;Dispatcher.DispatcherComposeInterceptor></code></a></p>\n<p><strong>Example</strong></p>\n<pre><code class=\"language-js\">import { Agent, interceptors } from 'undici'\n\nconst agent = new Agent().compose(\n  interceptors.retry({\n    maxRetries: 5,\n    minTimeout: 200,\n    maxTimeout: 5000,\n    timeoutFactor: 2,\n    statusCodes: [429, 502, 503, 504]\n  })\n)\n</code></pre>\n<hr>"
        },
        {
          "textRaw": "`interceptors.redirect([opts])`",
          "name": "redirect",
          "type": "method",
          "signatures": [
            {
              "params": [
                {
                  "textRaw": "`opts` {Object} (optional)",
                  "name": "opts",
                  "type": "Object",
                  "desc": "(optional)",
                  "options": [
                    {
                      "textRaw": "`maxRedirections` {number} Maximum number of redirects to follow. Passing `0` disables redirect following entirely. **Default:** `undefined` (inherits from the per-request `maxRedirections` option).",
                      "name": "maxRedirections",
                      "type": "number",
                      "default": "`undefined` (inherits from the per-request `maxRedirections` option)",
                      "desc": "Maximum number of redirects to follow. Passing `0` disables redirect following entirely."
                    },
                    {
                      "textRaw": "`throwOnMaxRedirect` {boolean} When `true`, throws an error once the redirect limit is reached instead of returning the final redirect response. **Default:** `false`.",
                      "name": "throwOnMaxRedirect",
                      "type": "boolean",
                      "default": "`false`",
                      "desc": "When `true`, throws an error once the redirect limit is reached instead of returning the final redirect response."
                    },
                    {
                      "textRaw": "`stripHeadersOnRedirect` {string}[] List of header names to remove from the request when following any redirect. **Default:** `[]`.",
                      "name": "stripHeadersOnRedirect",
                      "type": "string",
                      "default": "`[]`",
                      "desc": "[] List of header names to remove from the request when following any redirect."
                    },
                    {
                      "textRaw": "`stripHeadersOnCrossOriginRedirect` {string}[] List of header names to remove from the request when following a cross-origin redirect (i.e. the redirect target has a different origin). Useful for stripping `Authorization` on cross-origin hops. **Default:** `[]`.",
                      "name": "stripHeadersOnCrossOriginRedirect",
                      "type": "string",
                      "default": "`[]`",
                      "desc": "[] List of header names to remove from the request when following a cross-origin redirect (i.e. the redirect target has a different origin). Useful for stripping `Authorization` on cross-origin hops."
                    }
                  ],
                  "optional": true
                }
              ]
            }
          ],
          "desc": "<p>Follows HTTP redirects (3xx responses) automatically.</p>\n<p><strong>Parameters</strong></p>\n<p>Per-request override: any of the four options above can also be set directly\non the dispatch options to override the interceptor defaults for a specific\nrequest.</p>\n<p><strong>Returns:</strong> <a href=\"Dispatcher.html#class-dispatcherdispatchercomposeinterceptor\"><code>&#x3C;Dispatcher.DispatcherComposeInterceptor></code></a></p>\n<p><strong>Example</strong></p>\n<pre><code class=\"language-js\">import { Agent, interceptors } from 'undici'\n\nconst agent = new Agent().compose(\n  interceptors.redirect({\n    maxRedirections: 10,\n    throwOnMaxRedirect: true,\n    stripHeadersOnCrossOriginRedirect: ['authorization', 'cookie']\n  })\n)\n</code></pre>\n<hr>"
        },
        {
          "textRaw": "`interceptors.decompress([opts])`",
          "name": "decompress",
          "type": "method",
          "signatures": [
            {
              "params": [
                {
                  "textRaw": "`opts` {Object} (optional)",
                  "name": "opts",
                  "type": "Object",
                  "desc": "(optional)",
                  "options": [
                    {
                      "textRaw": "`skipStatusCodes` {number}[] Status codes for which decompression is skipped. **Default:** `[204, 304]`.",
                      "name": "skipStatusCodes",
                      "type": "number",
                      "default": "`[204, 304]`",
                      "desc": "[] Status codes for which decompression is skipped."
                    },
                    {
                      "textRaw": "`skipErrorResponses` {boolean} When `true`, responses with a status code= 400 are not decompressed. **Default:** `true`.",
                      "name": "skipErrorResponses",
                      "type": "boolean",
                      "default": "`true`",
                      "desc": "When `true`, responses with a status code= 400 are not decompressed."
                    }
                  ],
                  "optional": true
                }
              ]
            }
          ],
          "desc": "<p>Automatically decompresses response bodies encoded with <code>gzip</code>, <code>x-gzip</code>,\n<code>br</code> (Brotli), <code>deflate</code>, <code>compress</code>, <code>x-compress</code>, or <code>zstd</code>.</p>\n<blockquote>\n<p><strong>Experimental:</strong> This interceptor is experimental and subject to change.\nA one-time <code>ExperimentalWarning</code> is emitted on first use.</p>\n</blockquote>\n<p><strong>Parameters</strong></p>\n<p><strong>Returns:</strong> <a href=\"Dispatcher.html#class-dispatcherdispatchercomposeinterceptor\"><code>&#x3C;Dispatcher.DispatcherComposeInterceptor></code></a></p>\n<p><strong>Example</strong></p>\n<pre><code class=\"language-js\">import { Agent, interceptors } from 'undici'\n\nconst agent = new Agent().compose(\n  interceptors.decompress({\n    skipStatusCodes: [204, 304],\n    skipErrorResponses: false // decompress error bodies too\n  })\n)\n</code></pre>\n<hr>"
        },
        {
          "textRaw": "`interceptors.responseError([opts])`",
          "name": "responseError",
          "type": "method",
          "signatures": [
            {
              "params": [
                {
                  "textRaw": "`opts` {Object} (optional) — currently reserved for future use; may be omitted.",
                  "name": "opts",
                  "type": "Object",
                  "desc": "(optional) — currently reserved for future use; may be omitted.",
                  "optional": true
                }
              ]
            }
          ],
          "desc": "<p>Converts 4xx/5xx responses into thrown <code>ResponseError</code> instances, making it\neasy to handle HTTP errors with a standard <code>try/catch</code> block.</p>\n<p>The error body is automatically decoded for <code>application/json</code> and\n<code>text/plain</code> responses. For JSON responses the body is parsed and exposed as\n<code>error.body</code>.</p>\n<p><strong>Parameters</strong></p>\n<p><strong>Returns:</strong> <a href=\"Dispatcher.html#class-dispatcherdispatchercomposeinterceptor\"><code>&#x3C;Dispatcher.DispatcherComposeInterceptor></code></a></p>\n<p><strong>Example</strong></p>\n<pre><code class=\"language-js\">import { Agent, interceptors, errors } from 'undici'\n\nconst agent = new Agent().compose(interceptors.responseError())\n\ntry {\n  await agent.request({ origin: 'https://example.com', path: '/not-found', method: 'GET' })\n} catch (err) {\n  if (err instanceof errors.ResponseError) {\n    console.error(err.status, err.body)\n  }\n}\n</code></pre>\n<hr>"
        },
        {
          "textRaw": "`interceptors.dns([opts])`",
          "name": "dns",
          "type": "method",
          "signatures": [
            {
              "params": [
                {
                  "textRaw": "`opts` {Object} (optional)",
                  "name": "opts",
                  "type": "Object",
                  "desc": "(optional)",
                  "options": [
                    {
                      "textRaw": "`maxTTL` {number} Maximum number of milliseconds a DNS record is cached, regardless of the TTL returned by the resolver. **Default:** `0` (use the TTL from the DNS record).",
                      "name": "maxTTL",
                      "type": "number",
                      "default": "`0` (use the TTL from the DNS record)",
                      "desc": "Maximum number of milliseconds a DNS record is cached, regardless of the TTL returned by the resolver."
                    },
                    {
                      "textRaw": "`maxItems` {number} Maximum number of origins to cache simultaneously. Oldest entries are evicted when the limit is reached. **Default:** `Infinity`.",
                      "name": "maxItems",
                      "type": "number",
                      "default": "`Infinity`",
                      "desc": "Maximum number of origins to cache simultaneously. Oldest entries are evicted when the limit is reached."
                    },
                    {
                      "textRaw": "`dualStack` {boolean} When `true`, both IPv4 (`A`) and IPv6 (`AAAA`) records are looked up and the interceptor picks between them based on `affinity`. **Default:** `true`.",
                      "name": "dualStack",
                      "type": "boolean",
                      "default": "`true`",
                      "desc": "When `true`, both IPv4 (`A`) and IPv6 (`AAAA`) records are looked up and the interceptor picks between them based on `affinity`."
                    },
                    {
                      "textRaw": "`affinity` {4|6|null} Preferred IP family when `dualStack` is enabled. `null` lets the interceptor alternate between families. **Default:** `null`.",
                      "name": "affinity",
                      "type": "4|6|null",
                      "default": "`null`",
                      "desc": "Preferred IP family when `dualStack` is enabled. `null` lets the interceptor alternate between families."
                    },
                    {
                      "textRaw": "`lookup` {Function} (optional) Custom DNS resolution function with the same signature as `node:dns`'s `lookup` callback form: `(origin, options, callback) => void`.",
                      "name": "lookup",
                      "type": "Function",
                      "desc": "(optional) Custom DNS resolution function with the same signature as `node:dns`'s `lookup` callback form: `(origin, options, callback) => void`."
                    },
                    {
                      "textRaw": "`pick` {Function} (optional) Custom record-selection function called with `(origin, records, affinity)` to choose which resolved address to use.",
                      "name": "pick",
                      "type": "Function",
                      "desc": "(optional) Custom record-selection function called with `(origin, records, affinity)` to choose which resolved address to use."
                    },
                    {
                      "textRaw": "`storage` {DNSStorage} (optional) Custom storage backend. Must implement `get`, `set`, `delete`, `full`, and `size`.",
                      "name": "storage",
                      "type": "DNSStorage",
                      "desc": "(optional) Custom storage backend. Must implement `get`, `set`, `delete`, `full`, and `size`."
                    }
                  ],
                  "optional": true
                }
              ]
            }
          ],
          "desc": "<p>Caches DNS lookups so that repeated requests to the same origin reuse the\nresolved IP address instead of performing a fresh lookup every time. Supports\ndual-stack (IPv4 + IPv6) and custom lookup/storage implementations.</p>\n<p><strong>Parameters</strong></p>\n<p><strong><code>DNSStorage</code> interface</strong></p>\n<pre><code class=\"language-ts\">interface DNSStorage {\n  size: number\n  get(origin: string): DNSInterceptorOriginRecords | null\n  set(origin: string, records: DNSInterceptorOriginRecords | null, options: { ttl: number }): void\n  delete(origin: string): void\n  full(): boolean\n}\n</code></pre>\n<p><strong>Returns:</strong> <a href=\"Dispatcher.html#class-dispatcherdispatchercomposeinterceptor\"><code>&#x3C;Dispatcher.DispatcherComposeInterceptor></code></a></p>\n<p><strong>Example</strong></p>\n<pre><code class=\"language-js\">import { Agent, interceptors } from 'undici'\n\nconst agent = new Agent().compose(\n  interceptors.dns({\n    maxTTL: 60_000, // cache for at most 60 seconds\n    dualStack: true,\n    affinity: 4     // prefer IPv4\n  })\n)\n</code></pre>\n<hr>"
        },
        {
          "textRaw": "`interceptors.cache([opts])`",
          "name": "cache",
          "type": "method",
          "signatures": [
            {
              "params": [
                {
                  "textRaw": "`opts` {CacheHandler.CacheOptions} (optional) See the `CacheStore` documentation for accepted fields.",
                  "name": "opts",
                  "type": "CacheHandler.CacheOptions",
                  "desc": "(optional) See the `CacheStore` documentation for accepted fields.",
                  "optional": true
                }
              ]
            }
          ],
          "desc": "<p>Caches HTTP responses according to RFC 9111 (HTTP Caching). See\n<a href=\"./CacheStore.html\"><code>CacheStore</code></a> for information on providing a custom\nbacking store.</p>\n<p><strong>Parameters</strong></p>\n<p><strong>Returns:</strong> <a href=\"Dispatcher.html#class-dispatcherdispatchercomposeinterceptor\"><code>&#x3C;Dispatcher.DispatcherComposeInterceptor></code></a></p>\n<p><strong>Example</strong></p>\n<pre><code class=\"language-js\">import { Agent, interceptors, cacheStores } from 'undici'\n\nconst agent = new Agent().compose(\n  interceptors.cache({ store: new cacheStores.MemoryCacheStore() })\n)\n</code></pre>\n<hr>"
        },
        {
          "textRaw": "`interceptors.deduplicate([opts])`",
          "name": "deduplicate",
          "type": "method",
          "signatures": [
            {
              "params": [
                {
                  "textRaw": "`opts` {Object} (optional)",
                  "name": "opts",
                  "type": "Object",
                  "desc": "(optional)",
                  "options": [
                    {
                      "textRaw": "`methods` {string}[] HTTP methods to deduplicate. Must be safe HTTP methods (`GET`, `HEAD`, `OPTIONS`, `TRACE`). **Default:** `['GET']`.",
                      "name": "methods",
                      "type": "string",
                      "default": "`['GET']`",
                      "desc": "[] HTTP methods to deduplicate. Must be safe HTTP methods (`GET`, `HEAD`, `OPTIONS`, `TRACE`)."
                    },
                    {
                      "textRaw": "`skipHeaderNames` {string}[] Header names whose presence in a request causes it to bypass deduplication entirely. Matching is case-insensitive. **Default:** `[]`.",
                      "name": "skipHeaderNames",
                      "type": "string",
                      "default": "`[]`",
                      "desc": "[] Header names whose presence in a request causes it to bypass deduplication entirely. Matching is case-insensitive."
                    },
                    {
                      "textRaw": "`excludeHeaderNames` {string}[] Header names to exclude from the deduplication key. Requests that differ only in these headers are still deduplicated together. Useful for headers like `x-request-id` that vary per request but should not prevent deduplication. Matching is case-insensitive. **Default:** `[]`.",
                      "name": "excludeHeaderNames",
                      "type": "string",
                      "default": "`[]`",
                      "desc": "[] Header names to exclude from the deduplication key. Requests that differ only in these headers are still deduplicated together. Useful for headers like `x-request-id` that vary per request but should not prevent deduplication. Matching is case-insensitive."
                    },
                    {
                      "textRaw": "`maxBufferSize` {number} Maximum number of bytes buffered per paused waiting handler. If a waiting handler exceeds this threshold it is failed with an `AbortError` to prevent unbounded memory growth. **Default:** `5_242_880` (5 MiB).",
                      "name": "maxBufferSize",
                      "type": "number",
                      "default": "`5_242_880` (5 MiB)",
                      "desc": "Maximum number of bytes buffered per paused waiting handler. If a waiting handler exceeds this threshold it is failed with an `AbortError` to prevent unbounded memory growth."
                    }
                  ],
                  "optional": true
                }
              ]
            }
          ],
          "desc": "<p>Deduplicates concurrent identical requests so that only one is sent over the\nwire. All waiting callers receive the same response once the in-flight request\ncompletes. Only safe HTTP methods (e.g. <code>GET</code>, <code>HEAD</code>) may be deduplicated.</p>\n<p><strong>Parameters</strong></p>\n<p><strong>Returns:</strong> <a href=\"Dispatcher.html#class-dispatcherdispatchercomposeinterceptor\"><code>&#x3C;Dispatcher.DispatcherComposeInterceptor></code></a></p>\n<p><strong>Example</strong></p>\n<pre><code class=\"language-js\">import { Agent, interceptors } from 'undici'\n\nconst agent = new Agent().compose(\n  interceptors.deduplicate({\n    methods: ['GET', 'HEAD'],\n    excludeHeaderNames: ['x-request-id', 'x-trace-id'],\n    maxBufferSize: 2 * 1024 * 1024\n  })\n)\n</code></pre>\n<hr>"
        }
      ],
      "displayName": "Interceptors"
    }
  ]
}