260706.12 · deps: update undici to 8.7.0
Getting Started
Installation
npm install undiciFetch
The quickest way to get started is with fetch, which follows the
Fetch Standard and works the same way as
the browser API:
import { fetch } from 'undici'
const res = await fetch('https://example.com')
const data = await res.json()
console.log(data)Using the Request object
undici also exports a Request class that follows the Fetch Standard:
import { fetch, Request } from 'undici'
const req = new Request('https://example.com', {
method: 'POST',
headers: { 'content-type': 'application/json' },
body: JSON.stringify({ hello: 'world' })
})
const res = await fetch(req)
console.log(res.status)Streaming the response
res.body is a web ReadableStream. Use pipeline from
node:stream/promises to stream it to a file:
import { fetch } from 'undici'
import { pipeline } from 'node:stream/promises'
import { createWriteStream } from 'node:fs'
const res = await fetch('https://example.com/large-file.zip')
await pipeline(res.body, createWriteStream('./file.zip'))Always consume or cancel the response body. In Node.js, garbage collection is not aggressive enough to release connections promptly, so leaving a body unread can cause connection leaks and stalled requests. See Specification Compliance - Garbage Collection for details.
For more on fetch, see API Reference: Fetch.
Dispatchers: Connection reuse and pooling
By default, fetch, request, stream, and pipeline create a new connection
for each call. For applications that make many requests to the same origin,
this is wasteful. undici provides dispatchers that manage connections
internally.
Agent — for requests to multiple origins
Agent is the most general-purpose dispatcher. It pools connections per-origin
and is the recommended default for most applications. Use it with
setGlobalDispatcher to affect all undici calls globally:
import { Agent, setGlobalDispatcher, fetch } from 'undici'
const agent = new Agent({
keepAliveTimeout: 30_000,
keepAliveMaxTimeout: 600_000
})
setGlobalDispatcher(agent)
// All subsequent fetch/request/stream/pipeline calls reuse connections
const res = await fetch('https://api.example.com/data')You can also pass a dispatcher per-request:
await fetch('https://api.example.com/data', { dispatcher: agent })Pool — for requests to a single origin
Pool manages a fixed set of connections to one origin. It gives you explicit
control over concurrency:
import { Pool, request } from 'undici'
const pool = new Pool('https://api.example.com', { connections: 10 })
const { body } = await request('https://api.example.com/data', {
dispatcher: pool
})
const data = await body.json()
pool.close()Client — for a single connection
Client maps to a single TCP connection. It supports pipelining (sending
multiple requests before responses arrive), which should only be enabled for
trusted remote servers:
import { Client } from 'undici'
const client = new Client('https://api.example.com', {
pipelining: 5
})
const { body } = await client.request({ path: '/', method: 'GET' })
await body.dump()
client.close()For more on dispatcher options and lifecycle, see:
Timeouts
undici applies timeouts at two levels:
headersTimeout— time to wait for response headers (default: 300s).bodyTimeout— time between consecutive body chunks (default: 300s).
Set these on the dispatcher or per-request:
import { Agent, setGlobalDispatcher } from 'undici'
const agent = new Agent({
headersTimeout: 5_000,
bodyTimeout: 30_000
})
setGlobalDispatcher(agent)Timeout errors are thrown as HeadersTimeoutError and BodyTimeoutError.
See API Reference: Errors for the full list.
Error handling
undici exposes structured errors via error.code:
import { request, errors } from 'undici'
try {
const { body } = await request('https://example.com')
await body.json()
} catch (err) {
switch (err.code) {
case 'UND_ERR_CONNECT_TIMEOUT':
console.error('Connection timed out')
break
case 'UND_ERR_HEADERS_TIMEOUT':
console.error('Headers timed out')
break
case 'UND_ERR_BODY_TIMEOUT':
console.error('Body timed out')
break
case 'UND_ERR_ABORTED':
console.error('Request was aborted')
break
default:
console.error(err)
}
}Aborting requests
import { request } from 'undici'
const ac = new AbortController()
setTimeout(() => ac.abort(), 1000)
try {
const { body } = await request('https://example.com', {
signal: ac.signal
})
await body.dump()
} catch (err) {
console.error(err.code) // UND_ERR_ABORTED
}Common patterns
Proxies
Use ProxyAgent for HTTP(S) proxies, or EnvHttpProxyAgent to pick up
proxy settings from environment variables:
import { ProxyAgent, setGlobalDispatcher } from 'undici'
const proxy = new ProxyAgent('http://proxy.internal:8080')
setGlobalDispatcher(proxy)See Best Practices: Proxy and API Reference: ProxyAgent.
Mocking in tests
import { MockAgent, setGlobalDispatcher, request } from 'undici'
const mockAgent = new MockAgent()
setGlobalDispatcher(mockAgent)
const mockPool = mockAgent.get('https://api.example.com')
mockPool.intercept({ path: '/users' }).reply(200, [{ id: 1 }])
const { body } = await request('https://api.example.com/users')
console.log(await body.json())See Best Practices: Mocking Request and API Reference: MockAgent.
Testing with undici
For test suites, set short keep-alive timeouts to avoid slow teardowns:
import { Agent, setGlobalDispatcher } from 'undici'
const agent = new Agent({
keepAliveTimeout: 10,
keepAliveMaxTimeout: 10
})
setGlobalDispatcher(agent)See Best Practices: Writing Tests.
Customizing the global fetch
You can override Node.js’s built-in globals with install():
import { install } from 'undici'
install()
// Global fetch, Headers, Response, Request, and FormData
// now come from undici, not the Node.js bundle
const res = await fetch('https://example.com')See API Reference: Global Installation.
Further reading
- Undici vs. Built-in Fetch — when to install undici vs using Node.js built-in fetch
- API Reference — full dispatcher API documentation
- Examples — runnable code examples