Skip to main content

Documentation Index

Fetch the complete documentation index at: https://mintlify.com/nodejs/undici/llms.txt

Use this file to discover all available pages before exploring further.

undici makes it easy to perform fast HTTP requests in Node.js with a straightforward API. This guide walks you through installing the package, making your first request, consuming response bodies, and setting up connection pooling — all with real code patterns from the undici source.

Installation

npm install undici
undici v8.x requires Node.js ≥ 22.19.0. If you are on an older Node.js release, install undici v6.x (npm install undici@6) which supports Node.js ≥18.17.

Making a basic request

1

Import request

undici ships as a CommonJS module with named exports. Use ESM import or CJS require:
ESM import
import { request } from 'undici'
2

Call request()

Pass a URL string, URL object, or a URL-like object. request() resolves with statusCode, headers, trailers, and body:
Making a request
import { request } from 'undici'

const {
  statusCode,
  headers,
  trailers,
  body
} = await request('http://localhost:3000/foo')

console.log('response received', statusCode)
console.log('headers', headers)
The default HTTP method is GET. If you pass a body option, the method defaults to PUT.
3

Consume the response body

The body object implements the Fetch Standard body mixin. Use one of these methods to read it:
Reading the response as JSON
import { request } from 'undici'

const { statusCode, headers, body } = await request('https://api.example.com/data')

console.log('status', statusCode)
console.log('content-type', headers['content-type'])
const data = await body.json()
console.log('data', data)
Available body consumption methods:
MethodReturns
body.json()Parses body as JSON
body.text()Reads body as a UTF-8 string
body.arrayBuffer()Returns body as an ArrayBuffer
body.blob()Returns body as a Blob
body.bytes()Returns body as a Uint8Array
Each body method can only be called once. Calling .json() after .text() (or any other combination) throws TypeError: unusable. If you need the body as plain text after parsing, call .text() first and parse manually.
4

Stream the body directly

For large responses, iterate the body readable stream directly instead of buffering everything into memory:
Streaming the response body
import { request } from 'undici'

const { statusCode, body } = await request('https://api.example.com/large-file')

for await (const chunk of body) {
  process.stdout.write(chunk)
}

Always consume the response body

You must always consume or cancel the response body. Node.js garbage collection is less aggressive than browsers, so leaving a body unconsumed can exhaust connection pool slots and cause stalls or deadlocks.
If you only need headers and want to discard the body, call body.dump() or use the HEAD method:
Discarding the body safely
import { request } from 'undici'

// Option 1: dump the body to free the connection
const { body, headers } = await request('https://api.example.com/data')
await body.dump()

// Option 2: use HEAD to avoid receiving a body at all
const { headers: headersOnly } = await request('https://api.example.com/data', {
  method: 'HEAD'
})

Making a fetch() request

undici also exports a standards-compliant fetch() function. It is a drop-in replacement for the built-in global fetch and works with the same options:
Using undici fetch
import { fetch } from 'undici'

const res = await fetch('https://api.example.com/data')
const json = await res.json()
console.log(json)
You can pass a custom dispatcher option directly to fetch to control connection behavior:
fetch with a custom Agent
import { fetch, Agent } from 'undici'

const res = await fetch('https://api.example.com/data', {
  dispatcher: new Agent({
    keepAliveTimeout: 10,
    keepAliveMaxTimeout: 10
  })
})
const json = await res.json()
console.log(json)

Using an Agent for connection pooling

By default, undici uses a global Agent that manages connection pools automatically. You can configure it explicitly to tune keep-alive behavior, connection limits, and more:
Configuring a global Agent
import { Agent, setGlobalDispatcher, request } from 'undici'

const agent = new Agent({
  keepAliveTimeout: 10_000,       // ms to keep an idle connection open
  keepAliveMaxTimeout: 600_000,   // maximum keep-alive timeout
  connections: 10                 // max connections per origin
})

setGlobalDispatcher(agent)

// All subsequent request() and fetch() calls use this agent
const { statusCode, body } = await request('https://api.example.com/users')
const users = await body.json()
console.log(statusCode, users)
Use setGlobalDispatcher() once at application startup. The global dispatcher is shared across all undici APIs in the same process, including Node.js’s built-in fetch.

Sending request bodies

Pass a body option to send data. The method defaults to PUT when a body is present — set it explicitly to POST for most APIs:
Sending a JSON body
import { request } from 'undici'

const { statusCode, body } = await request('https://api.example.com/users', {
  method: 'POST',
  headers: {
    'content-type': 'application/json'
  },
  body: JSON.stringify({ name: 'Alice', role: 'admin' })
})

const created = await body.json()
console.log(statusCode, created)

Error handling

undici throws typed errors that you can inspect. The most common are network errors (UND_ERR_CONNECT_TIMEOUT, UND_ERR_SOCKET) and response errors when using the responseError interceptor:
Handling errors
import { request, errors } from 'undici'

try {
  const { statusCode, body } = await request('https://api.example.com/data')

  if (statusCode >= 400) {
    const text = await body.text()
    throw new Error(`HTTP ${statusCode}: ${text}`)
  }

  const data = await body.json()
  console.log(data)
} catch (err) {
  if (err instanceof errors.ConnectTimeoutError) {
    console.error('Connection timed out:', err.message)
  } else if (err instanceof errors.ResponseStatusCodeError) {
    console.error('Bad status:', err.statusCode)
  } else {
    throw err
  }
}
Unlike built-in fetch, undici.request() does not throw on 4xx or 5xx responses by default — it resolves with the statusCode. You must check it yourself or add the responseError interceptor.

Aborting a request

Pass an AbortSignal via the signal option to cancel in-flight requests:
Aborting with a timeout
import { request } from 'undici'

const controller = new AbortController()
const timeout = setTimeout(() => controller.abort(), 5000)

try {
  const { statusCode, body } = await request('https://api.example.com/slow', {
    signal: controller.signal
  })
  const data = await body.json()
  console.log(data)
} finally {
  clearTimeout(timeout)
}

Next steps

Introduction

Learn about all of undici’s capabilities and APIs

undici vs. built-in fetch

Understand when to use the module vs. Node.js globals

Dispatchers

Deep dive into Client, Pool, Agent, and ProxyAgent

Mocking

Intercept HTTP requests in tests with MockAgent

Build docs developers (and LLMs) love