1# undici 2 3[](https://github.com/nodejs/undici/actions/workflows/nodejs.yml) [](http://standardjs.com/) [](https://badge.fury.io/js/undici) [](https://codecov.io/gh/nodejs/undici) 4 5An HTTP/1.1 client, written from scratch for Node.js. 6 7> Undici means eleven in Italian. 1.1 -> 11 -> Eleven -> Undici. 8It is also a Stranger Things reference. 9 10Have a question about using Undici? Open a [Q&A Discussion](https://github.com/nodejs/undici/discussions/new) or join our official OpenJS [Slack](https://openjs-foundation.slack.com/archives/C01QF9Q31QD) channel. 11 12## Install 13 14``` 15npm i undici 16``` 17 18## Benchmarks 19 20The benchmark is a simple `hello world` [example](benchmarks/benchmark.js) using a 21number of unix sockets (connections) with a pipelining depth of 10 running on Node 20.6.0. 22 23### Connections 1 24 25 26| Tests | Samples | Result | Tolerance | Difference with slowest | 27|---------------------|---------|---------------|-----------|-------------------------| 28| http - no keepalive | 15 | 5.32 req/sec | ± 2.61 % | - | 29| http - keepalive | 10 | 5.35 req/sec | ± 2.47 % | + 0.44 % | 30| undici - fetch | 15 | 41.85 req/sec | ± 2.49 % | + 686.04 % | 31| undici - pipeline | 40 | 50.36 req/sec | ± 2.77 % | + 845.92 % | 32| undici - stream | 15 | 60.58 req/sec | ± 2.75 % | + 1037.72 % | 33| undici - request | 10 | 61.19 req/sec | ± 2.60 % | + 1049.24 % | 34| undici - dispatch | 20 | 64.84 req/sec | ± 2.81 % | + 1117.81 % | 35 36 37### Connections 50 38 39| Tests | Samples | Result | Tolerance | Difference with slowest | 40|---------------------|---------|------------------|-----------|-------------------------| 41| undici - fetch | 30 | 2107.19 req/sec | ± 2.69 % | - | 42| http - no keepalive | 10 | 2698.90 req/sec | ± 2.68 % | + 28.08 % | 43| http - keepalive | 10 | 4639.49 req/sec | ± 2.55 % | + 120.17 % | 44| undici - pipeline | 40 | 6123.33 req/sec | ± 2.97 % | + 190.59 % | 45| undici - stream | 50 | 9426.51 req/sec | ± 2.92 % | + 347.35 % | 46| undici - request | 10 | 10162.88 req/sec | ± 2.13 % | + 382.29 % | 47| undici - dispatch | 50 | 11191.11 req/sec | ± 2.98 % | + 431.09 % | 48 49 50## Quick Start 51 52```js 53import { request } from 'undici' 54 55const { 56 statusCode, 57 headers, 58 trailers, 59 body 60} = await request('http://localhost:3000/foo') 61 62console.log('response received', statusCode) 63console.log('headers', headers) 64 65for await (const data of body) { 66 console.log('data', data) 67} 68 69console.log('trailers', trailers) 70``` 71 72## Body Mixins 73 74The `body` mixins are the most common way to format the request/response body. Mixins include: 75 76- [`.formData()`](https://fetch.spec.whatwg.org/#dom-body-formdata) 77- [`.json()`](https://fetch.spec.whatwg.org/#dom-body-json) 78- [`.text()`](https://fetch.spec.whatwg.org/#dom-body-text) 79 80Example usage: 81 82```js 83import { request } from 'undici' 84 85const { 86 statusCode, 87 headers, 88 trailers, 89 body 90} = await request('http://localhost:3000/foo') 91 92console.log('response received', statusCode) 93console.log('headers', headers) 94console.log('data', await body.json()) 95console.log('trailers', trailers) 96``` 97 98_Note: Once a mixin has been called then the body cannot be reused, thus calling additional mixins on `.body`, e.g. `.body.json(); .body.text()` will result in an error `TypeError: unusable` being thrown and returned through the `Promise` rejection._ 99 100Should you need to access the `body` in plain-text after using a mixin, the best practice is to use the `.text()` mixin first and then manually parse the text to the desired format. 101 102For more information about their behavior, please reference the body mixin from the [Fetch Standard](https://fetch.spec.whatwg.org/#body-mixin). 103 104## Common API Methods 105 106This section documents our most commonly used API methods. Additional APIs are documented in their own files within the [docs](./docs/) folder and are accessible via the navigation list on the left side of the docs site. 107 108### `undici.request([url, options]): Promise` 109 110Arguments: 111 112* **url** `string | URL | UrlObject` 113* **options** [`RequestOptions`](./docs/api/Dispatcher.md#parameter-requestoptions) 114 * **dispatcher** `Dispatcher` - Default: [getGlobalDispatcher](#undicigetglobaldispatcher) 115 * **method** `String` - Default: `PUT` if `options.body`, otherwise `GET` 116 * **maxRedirections** `Integer` - Default: `0` 117 118Returns a promise with the result of the `Dispatcher.request` method. 119 120Calls `options.dispatcher.request(options)`. 121 122See [Dispatcher.request](./docs/api/Dispatcher.md#dispatcherrequestoptions-callback) for more details. 123 124### `undici.stream([url, options, ]factory): Promise` 125 126Arguments: 127 128* **url** `string | URL | UrlObject` 129* **options** [`StreamOptions`](./docs/api/Dispatcher.md#parameter-streamoptions) 130 * **dispatcher** `Dispatcher` - Default: [getGlobalDispatcher](#undicigetglobaldispatcher) 131 * **method** `String` - Default: `PUT` if `options.body`, otherwise `GET` 132 * **maxRedirections** `Integer` - Default: `0` 133* **factory** `Dispatcher.stream.factory` 134 135Returns a promise with the result of the `Dispatcher.stream` method. 136 137Calls `options.dispatcher.stream(options, factory)`. 138 139See [Dispatcher.stream](docs/api/Dispatcher.md#dispatcherstreamoptions-factory-callback) for more details. 140 141### `undici.pipeline([url, options, ]handler): Duplex` 142 143Arguments: 144 145* **url** `string | URL | UrlObject` 146* **options** [`PipelineOptions`](docs/api/Dispatcher.md#parameter-pipelineoptions) 147 * **dispatcher** `Dispatcher` - Default: [getGlobalDispatcher](#undicigetglobaldispatcher) 148 * **method** `String` - Default: `PUT` if `options.body`, otherwise `GET` 149 * **maxRedirections** `Integer` - Default: `0` 150* **handler** `Dispatcher.pipeline.handler` 151 152Returns: `stream.Duplex` 153 154Calls `options.dispatch.pipeline(options, handler)`. 155 156See [Dispatcher.pipeline](docs/api/Dispatcher.md#dispatcherpipelineoptions-handler) for more details. 157 158### `undici.connect([url, options]): Promise` 159 160Starts two-way communications with the requested resource using [HTTP CONNECT](https://developer.mozilla.org/en-US/docs/Web/HTTP/Methods/CONNECT). 161 162Arguments: 163 164* **url** `string | URL | UrlObject` 165* **options** [`ConnectOptions`](docs/api/Dispatcher.md#parameter-connectoptions) 166 * **dispatcher** `Dispatcher` - Default: [getGlobalDispatcher](#undicigetglobaldispatcher) 167 * **maxRedirections** `Integer` - Default: `0` 168* **callback** `(err: Error | null, data: ConnectData | null) => void` (optional) 169 170Returns a promise with the result of the `Dispatcher.connect` method. 171 172Calls `options.dispatch.connect(options)`. 173 174See [Dispatcher.connect](docs/api/Dispatcher.md#dispatcherconnectoptions-callback) for more details. 175 176### `undici.fetch(input[, init]): Promise` 177 178Implements [fetch](https://fetch.spec.whatwg.org/#fetch-method). 179 180* https://developer.mozilla.org/en-US/docs/Web/API/WindowOrWorkerGlobalScope/fetch 181* https://fetch.spec.whatwg.org/#fetch-method 182 183Only supported on Node 16.8+. 184 185Basic usage example: 186 187```js 188import { fetch } from 'undici' 189 190 191const res = await fetch('https://example.com') 192const json = await res.json() 193console.log(json) 194``` 195 196You can pass an optional dispatcher to `fetch` as: 197 198```js 199import { fetch, Agent } from 'undici' 200 201const res = await fetch('https://example.com', { 202 // Mocks are also supported 203 dispatcher: new Agent({ 204 keepAliveTimeout: 10, 205 keepAliveMaxTimeout: 10 206 }) 207}) 208const json = await res.json() 209console.log(json) 210``` 211 212#### `request.body` 213 214A body can be of the following types: 215 216- ArrayBuffer 217- ArrayBufferView 218- AsyncIterables 219- Blob 220- Iterables 221- String 222- URLSearchParams 223- FormData 224 225In this implementation of fetch, ```request.body``` now accepts ```Async Iterables```. It is not present in the [Fetch Standard.](https://fetch.spec.whatwg.org) 226 227```js 228import { fetch } from 'undici' 229 230const data = { 231 async *[Symbol.asyncIterator]() { 232 yield 'hello' 233 yield 'world' 234 }, 235} 236 237await fetch('https://example.com', { body: data, method: 'POST', duplex: 'half' }) 238``` 239 240#### `request.duplex` 241 242- half 243 244In this implementation of fetch, `request.duplex` must be set if `request.body` is `ReadableStream` or `Async Iterables`. And fetch requests are currently always be full duplex. More detail refer to [Fetch Standard.](https://fetch.spec.whatwg.org/#dom-requestinit-duplex) 245 246#### `response.body` 247 248Nodejs has two kinds of streams: [web streams](https://nodejs.org/dist/latest-v16.x/docs/api/webstreams.html), which follow the API of the WHATWG web standard found in browsers, and an older Node-specific [streams API](https://nodejs.org/api/stream.html). `response.body` returns a readable web stream. If you would prefer to work with a Node stream you can convert a web stream using `.fromWeb()`. 249 250```js 251import { fetch } from 'undici' 252import { Readable } from 'node:stream' 253 254const response = await fetch('https://example.com') 255const readableWebStream = response.body 256const readableNodeStream = Readable.fromWeb(readableWebStream) 257``` 258 259#### Specification Compliance 260 261This section documents parts of the [Fetch Standard](https://fetch.spec.whatwg.org) that Undici does 262not support or does not fully implement. 263 264##### Garbage Collection 265 266* https://fetch.spec.whatwg.org/#garbage-collection 267 268The [Fetch Standard](https://fetch.spec.whatwg.org) allows users to skip consuming the response body by relying on 269[garbage collection](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Memory_Management#garbage_collection) to release connection resources. Undici does not do the same. Therefore, it is important to always either consume or cancel the response body. 270 271Garbage collection in Node is less aggressive and deterministic 272(due to the lack of clear idle periods that browsers have through the rendering refresh rate) 273which means that leaving the release of connection resources to the garbage collector can lead 274to excessive connection usage, reduced performance (due to less connection re-use), and even 275stalls or deadlocks when running out of connections. 276 277```js 278// Do 279const headers = await fetch(url) 280 .then(async res => { 281 for await (const chunk of res.body) { 282 // force consumption of body 283 } 284 return res.headers 285 }) 286 287// Do not 288const headers = await fetch(url) 289 .then(res => res.headers) 290``` 291 292However, if you want to get only headers, it might be better to use `HEAD` request method. Usage of this method will obviate the need for consumption or cancelling of the response body. See [MDN - HTTP - HTTP request methods - HEAD](https://developer.mozilla.org/en-US/docs/Web/HTTP/Methods/HEAD) for more details. 293 294```js 295const headers = await fetch(url, { method: 'HEAD' }) 296 .then(res => res.headers) 297``` 298 299##### Forbidden and Safelisted Header Names 300 301* https://fetch.spec.whatwg.org/#cors-safelisted-response-header-name 302* https://fetch.spec.whatwg.org/#forbidden-header-name 303* https://fetch.spec.whatwg.org/#forbidden-response-header-name 304* https://github.com/wintercg/fetch/issues/6 305 306The [Fetch Standard](https://fetch.spec.whatwg.org) requires implementations to exclude certain headers from requests and responses. In browser environments, some headers are forbidden so the user agent remains in full control over them. In Undici, these constraints are removed to give more control to the user. 307 308### `undici.upgrade([url, options]): Promise` 309 310Upgrade to a different protocol. See [MDN - HTTP - Protocol upgrade mechanism](https://developer.mozilla.org/en-US/docs/Web/HTTP/Protocol_upgrade_mechanism) for more details. 311 312Arguments: 313 314* **url** `string | URL | UrlObject` 315* **options** [`UpgradeOptions`](docs/api/Dispatcher.md#parameter-upgradeoptions) 316 * **dispatcher** `Dispatcher` - Default: [getGlobalDispatcher](#undicigetglobaldispatcher) 317 * **maxRedirections** `Integer` - Default: `0` 318* **callback** `(error: Error | null, data: UpgradeData) => void` (optional) 319 320Returns a promise with the result of the `Dispatcher.upgrade` method. 321 322Calls `options.dispatcher.upgrade(options)`. 323 324See [Dispatcher.upgrade](docs/api/Dispatcher.md#dispatcherupgradeoptions-callback) for more details. 325 326### `undici.setGlobalDispatcher(dispatcher)` 327 328* dispatcher `Dispatcher` 329 330Sets the global dispatcher used by Common API Methods. 331 332### `undici.getGlobalDispatcher()` 333 334Gets the global dispatcher used by Common API Methods. 335 336Returns: `Dispatcher` 337 338### `undici.setGlobalOrigin(origin)` 339 340* origin `string | URL | undefined` 341 342Sets the global origin used in `fetch`. 343 344If `undefined` is passed, the global origin will be reset. This will cause `Response.redirect`, `new Request()`, and `fetch` to throw an error when a relative path is passed. 345 346```js 347setGlobalOrigin('http://localhost:3000') 348 349const response = await fetch('/api/ping') 350 351console.log(response.url) // http://localhost:3000/api/ping 352``` 353 354### `undici.getGlobalOrigin()` 355 356Gets the global origin used in `fetch`. 357 358Returns: `URL` 359 360### `UrlObject` 361 362* **port** `string | number` (optional) 363* **path** `string` (optional) 364* **pathname** `string` (optional) 365* **hostname** `string` (optional) 366* **origin** `string` (optional) 367* **protocol** `string` (optional) 368* **search** `string` (optional) 369 370## Specification Compliance 371 372This section documents parts of the HTTP/1.1 specification that Undici does 373not support or does not fully implement. 374 375### Expect 376 377Undici does not support the `Expect` request header field. The request 378body is always immediately sent and the `100 Continue` response will be 379ignored. 380 381Refs: https://tools.ietf.org/html/rfc7231#section-5.1.1 382 383### Pipelining 384 385Undici will only use pipelining if configured with a `pipelining` factor 386greater than `1`. 387 388Undici always assumes that connections are persistent and will immediately 389pipeline requests, without checking whether the connection is persistent. 390Hence, automatic fallback to HTTP/1.0 or HTTP/1.1 without pipelining is 391not supported. 392 393Undici will immediately pipeline when retrying requests after a failed 394connection. However, Undici will not retry the first remaining requests in 395the prior pipeline and instead error the corresponding callback/promise/stream. 396 397Undici will abort all running requests in the pipeline when any of them are 398aborted. 399 400* Refs: https://tools.ietf.org/html/rfc2616#section-8.1.2.2 401* Refs: https://tools.ietf.org/html/rfc7230#section-6.3.2 402 403### Manual Redirect 404 405Since it is not possible to manually follow an HTTP redirect on the server-side, 406Undici returns the actual response instead of an `opaqueredirect` filtered one 407when invoked with a `manual` redirect. This aligns `fetch()` with the other 408implementations in Deno and Cloudflare Workers. 409 410Refs: https://fetch.spec.whatwg.org/#atomic-http-redirect-handling 411 412## Workarounds 413 414### Network address family autoselection. 415 416If you experience problem when connecting to a remote server that is resolved by your DNS servers to a IPv6 (AAAA record) 417first, there are chances that your local router or ISP might have problem connecting to IPv6 networks. In that case 418undici will throw an error with code `UND_ERR_CONNECT_TIMEOUT`. 419 420If the target server resolves to both a IPv6 and IPv4 (A records) address and you are using a compatible Node version 421(18.3.0 and above), you can fix the problem by providing the `autoSelectFamily` option (support by both `undici.request` 422and `undici.Agent`) which will enable the family autoselection algorithm when establishing the connection. 423 424## Collaborators 425 426* [__Daniele Belardi__](https://github.com/dnlup), <https://www.npmjs.com/~dnlup> 427* [__Ethan Arrowood__](https://github.com/ethan-arrowood), <https://www.npmjs.com/~ethan_arrowood> 428* [__Matteo Collina__](https://github.com/mcollina), <https://www.npmjs.com/~matteo.collina> 429* [__Matthew Aitken__](https://github.com/KhafraDev), <https://www.npmjs.com/~khaf> 430* [__Robert Nagy__](https://github.com/ronag), <https://www.npmjs.com/~ronag> 431* [__Szymon Marczak__](https://github.com/szmarczak), <https://www.npmjs.com/~szmarczak> 432* [__Tomas Della Vedova__](https://github.com/delvedor), <https://www.npmjs.com/~delvedor> 433 434### Releasers 435 436* [__Ethan Arrowood__](https://github.com/ethan-arrowood), <https://www.npmjs.com/~ethan_arrowood> 437* [__Matteo Collina__](https://github.com/mcollina), <https://www.npmjs.com/~matteo.collina> 438* [__Robert Nagy__](https://github.com/ronag), <https://www.npmjs.com/~ronag> 439* [__Matthew Aitken__](https://github.com/KhafraDev), <https://www.npmjs.com/~khaf> 440 441## License 442 443MIT 444