Variable: customFetch

April 12, 2026 ยท View on GitHub

๐Ÿ’— Help the project

Support from the community to continue maintaining and improving this module is welcome. If you find the module useful, please consider supporting the project by becoming a sponsor.


โ€ข const customFetch: typeof oauth.customFetch = oauth.customFetch

When set on a Configuration, this replaces the use of global fetch. As a fetch replacement the arguments and expected return are the same as fetch.

In theory any module that claims to be compatible with the Fetch API can be used but your mileage may vary. No workarounds to allow use of non-conform Response instances will be considered.

If you only need to update the Request properties you do not need to use a Fetch API module, just change what you need and pass it to globalThis.fetch just like this module would normally do.

Its intended use cases are:

  • Request/Response tracing and logging
  • Custom caching strategies
  • Changing the Request properties like headers, body, credentials, mode before it is passed to fetch

Known caveats:

  • Expect Type-related issues when passing the inputs through to fetch-like modules, they hardly ever get their typings inline with actual fetch, you should @ts-expect-error them.

Examples

Using sindresorhus/ky for retries and its hooks feature for logging outgoing requests and their responses.

import ky from 'ky'

let config!: client.Configuration
let logRequest!: (request: Request) => void
let logResponse!: (request: Request, response: Response) => void
let logRetry!: (
  request: Request,
  error: Error,
  retryCount: number,
) => void

config[client.customFetch] = (...args) =>
  // @ts-expect-error
  ky(args[0], {
    ...args[1],
    hooks: {
      beforeRequest: [
        (request) => {
          logRequest(request)
        },
      ],
      beforeRetry: [
        ({ request, error, retryCount }) => {
          logRetry(request, error, retryCount)
        },
      ],
      afterResponse: [
        (request, _, response) => {
          logResponse(request, response)
        },
      ],
    },
  })

Using nodejs/undici to detect and use HTTP proxies.

import * as undici from 'undici'

// see https://undici.nodejs.org/#/docs/api/EnvHttpProxyAgent
let envHttpProxyAgent = new undici.EnvHttpProxyAgent()

let config!: client.Configuration

// @ts-ignore
config[client.customFetch] = (...args) => {
  // @ts-ignore
  return undici.fetch(args[0], { ...args[1], dispatcher: envHttpProxyAgent }) // prettier-ignore
}

Using nodejs/undici to automatically retry network errors.

import * as undici from 'undici'

// see https://undici.nodejs.org/#/docs/api/RetryAgent
let retryAgent = new undici.RetryAgent(new undici.Agent(), {
  statusCodes: [],
  errorCodes: [
    'ECONNRESET',
    'ECONNREFUSED',
    'ENOTFOUND',
    'ENETDOWN',
    'ENETUNREACH',
    'EHOSTDOWN',
    'UND_ERR_SOCKET',
  ],
})

let config!: client.Configuration

// @ts-ignore
config[client.customFetch] = (...args) => {
  // @ts-ignore
  return undici.fetch(args[0], { ...args[1], dispatcher: retryAgent }) // prettier-ignore
}

Using nodejs/undici to mock responses in tests.

import * as undici from 'undici'

// see https://undici.nodejs.org/#/docs/api/MockAgent
let mockAgent = new undici.MockAgent()
mockAgent.disableNetConnect()

let config!: client.Configuration

// @ts-ignore
config[client.customFetch] = (...args) => {
  // @ts-ignore
  return undici.fetch(args[0], { ...args[1], dispatcher: mockAgent }) // prettier-ignore
}

Correcting the redirect_uri token endpoint request parameter when the registered redirect URI contains query string components or when URL normalization alters it (e.g. adding a trailing slash to a bare origin). The module derives redirect_uri from the callback URL by stripping all query parameters but it cannot distinguish the redirect URI's own parameters from those added by the authorization server response.

let config!: client.Configuration
let registeredRedirectUri!: string

// @ts-ignore
config[client.customFetch] = (...args) => {
  let [url, options] = args
  if (
    options.body instanceof URLSearchParams &&
    options.body.get('grant_type') === 'authorization_code'
  ) {
    options.body.set('redirect_uri', registeredRedirectUri)
  }

  // @ts-ignore
  return fetch(...args)
}