README.md
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 16.
22The benchmarks below have the [simd](https://github.com/WebAssembly/simd) feature enabled.
23
24### Connections 1
25
26| Tests | Samples | Result | Tolerance | Difference with slowest |
27|---------------------|---------|---------------|-----------|-------------------------|
28| http - no keepalive | 15 | 4.63 req/sec | ± 2.77 % | - |
29| http - keepalive | 10 | 4.81 req/sec | ± 2.16 % | + 3.94 % |
30| undici - stream | 25 | 62.22 req/sec | ± 2.67 % | + 1244.58 % |
31| undici - dispatch | 15 | 64.33 req/sec | ± 2.47 % | + 1290.24 % |
32| undici - request | 15 | 66.08 req/sec | ± 2.48 % | + 1327.88 % |
33| undici - pipeline | 10 | 66.13 req/sec | ± 1.39 % | + 1329.08 % |
34
35### Connections 50
36
37| Tests | Samples | Result | Tolerance | Difference with slowest |
38|---------------------|---------|------------------|-----------|-------------------------|
39| http - no keepalive | 50 | 3546.49 req/sec | ± 2.90 % | - |
40| http - keepalive | 15 | 5692.67 req/sec | ± 2.48 % | + 60.52 % |
41| undici - pipeline | 25 | 8478.71 req/sec | ± 2.62 % | + 139.07 % |
42| undici - request | 20 | 9766.66 req/sec | ± 2.79 % | + 175.39 % |
43| undici - stream | 15 | 10109.74 req/sec | ± 2.94 % | + 185.06 % |
44| undici - dispatch | 25 | 10949.73 req/sec | ± 2.54 % | + 208.75 % |
45
46## Quick Start
47
48```js
49import { request } from 'undici'
50
51const {
52 statusCode,
53 headers,
54 trailers,
55 body
56} = await request('http://localhost:3000/foo')
57
58console.log('response received', statusCode)
59console.log('headers', headers)
60
61for await (const data of body) {
62 console.log('data', data)
63}
64
65console.log('trailers', trailers)
66```
67
68## Body Mixins
69
70The `body` mixins are the most common way to format the request/response body. Mixins include:
71
72- [`.formData()`](https://fetch.spec.whatwg.org/#dom-body-formdata)
73- [`.json()`](https://fetch.spec.whatwg.org/#dom-body-json)
74- [`.text()`](https://fetch.spec.whatwg.org/#dom-body-text)
75
76Example usage:
77
78```js
79import { request } from 'undici'
80
81const {
82 statusCode,
83 headers,
84 trailers,
85 body
86} = await request('http://localhost:3000/foo')
87
88console.log('response received', statusCode)
89console.log('headers', headers)
90console.log('data', await body.json())
91console.log('trailers', trailers)
92```
93
94_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._
95
96Should 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.
97
98For more information about their behavior, please reference the body mixin from the [Fetch Standard](https://fetch.spec.whatwg.org/#body-mixin).
99
100## Common API Methods
101
102This 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.
103
104### `undici.request([url, options]): Promise`
105
106Arguments:
107
108* **url** `string | URL | UrlObject`
109* **options** [`RequestOptions`](./docs/api/Dispatcher.md#parameter-requestoptions)
110 * **dispatcher** `Dispatcher` - Default: [getGlobalDispatcher](#undicigetglobaldispatcher)
111 * **method** `String` - Default: `PUT` if `options.body`, otherwise `GET`
112 * **maxRedirections** `Integer` - Default: `0`
113
114Returns a promise with the result of the `Dispatcher.request` method.
115
116Calls `options.dispatcher.request(options)`.
117
118See [Dispatcher.request](./docs/api/Dispatcher.md#dispatcherrequestoptions-callback) for more details.
119
120### `undici.stream([url, options, ]factory): Promise`
121
122Arguments:
123
124* **url** `string | URL | UrlObject`
125* **options** [`StreamOptions`](./docs/api/Dispatcher.md#parameter-streamoptions)
126 * **dispatcher** `Dispatcher` - Default: [getGlobalDispatcher](#undicigetglobaldispatcher)
127 * **method** `String` - Default: `PUT` if `options.body`, otherwise `GET`
128 * **maxRedirections** `Integer` - Default: `0`
129* **factory** `Dispatcher.stream.factory`
130
131Returns a promise with the result of the `Dispatcher.stream` method.
132
133Calls `options.dispatcher.stream(options, factory)`.
134
135See [Dispatcher.stream](docs/api/Dispatcher.md#dispatcherstreamoptions-factory-callback) for more details.
136
137### `undici.pipeline([url, options, ]handler): Duplex`
138
139Arguments:
140
141* **url** `string | URL | UrlObject`
142* **options** [`PipelineOptions`](docs/api/Dispatcher.md#parameter-pipelineoptions)
143 * **dispatcher** `Dispatcher` - Default: [getGlobalDispatcher](#undicigetglobaldispatcher)
144 * **method** `String` - Default: `PUT` if `options.body`, otherwise `GET`
145 * **maxRedirections** `Integer` - Default: `0`
146* **handler** `Dispatcher.pipeline.handler`
147
148Returns: `stream.Duplex`
149
150Calls `options.dispatch.pipeline(options, handler)`.
151
152See [Dispatcher.pipeline](docs/api/Dispatcher.md#dispatcherpipelineoptions-handler) for more details.
153
154### `undici.connect([url, options]): Promise`
155
156Starts two-way communications with the requested resource using [HTTP CONNECT](https://developer.mozilla.org/en-US/docs/Web/HTTP/Methods/CONNECT).
157
158Arguments:
159
160* **url** `string | URL | UrlObject`
161* **options** [`ConnectOptions`](docs/api/Dispatcher.md#parameter-connectoptions)
162 * **dispatcher** `Dispatcher` - Default: [getGlobalDispatcher](#undicigetglobaldispatcher)
163 * **maxRedirections** `Integer` - Default: `0`
164* **callback** `(err: Error | null, data: ConnectData | null) => void` (optional)
165
166Returns a promise with the result of the `Dispatcher.connect` method.
167
168Calls `options.dispatch.connect(options)`.
169
170See [Dispatcher.connect](docs/api/Dispatcher.md#dispatcherconnectoptions-callback) for more details.
171
172### `undici.fetch(input[, init]): Promise`
173
174Implements [fetch](https://fetch.spec.whatwg.org/#fetch-method).
175
176* https://developer.mozilla.org/en-US/docs/Web/API/WindowOrWorkerGlobalScope/fetch
177* https://fetch.spec.whatwg.org/#fetch-method
178
179Only supported on Node 16.8+.
180
181Basic usage example:
182
183```js
184import { fetch } from 'undici'
185
186
187const res = await fetch('https://example.com')
188const json = await res.json()
189console.log(json)
190```
191
192You can pass an optional dispatcher to `fetch` as:
193
194```js
195import { fetch, Agent } from 'undici'
196
197const res = await fetch('https://example.com', {
198 // Mocks are also supported
199 dispatcher: new Agent({
200 keepAliveTimeout: 10,
201 keepAliveMaxTimeout: 10
202 })
203})
204const json = await res.json()
205console.log(json)
206```
207
208#### `request.body`
209
210A body can be of the following types:
211
212- ArrayBuffer
213- ArrayBufferView
214- AsyncIterables
215- Blob
216- Iterables
217- String
218- URLSearchParams
219- FormData
220
221In this implementation of fetch, ```request.body``` now accepts ```Async Iterables```. It is not present in the [Fetch Standard.](https://fetch.spec.whatwg.org)
222
223```js
224import { fetch } from 'undici'
225
226const data = {
227 async *[Symbol.asyncIterator]() {
228 yield 'hello'
229 yield 'world'
230 },
231}
232
233await fetch('https://example.com', { body: data, method: 'POST', duplex: 'half' })
234```
235
236#### `request.duplex`
237
238- half
239
240In 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)
241
242#### `response.body`
243
244Nodejs 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()`.
245
246```js
247import { fetch } from 'undici'
248import { Readable } from 'node:stream'
249
250const response = await fetch('https://example.com')
251const readableWebStream = response.body
252const readableNodeStream = Readable.fromWeb(readableWebStream)
253```
254
255#### Specification Compliance
256
257This section documents parts of the [Fetch Standard](https://fetch.spec.whatwg.org) that Undici does
258not support or does not fully implement.
259
260##### Garbage Collection
261
262* https://fetch.spec.whatwg.org/#garbage-collection
263
264The [Fetch Standard](https://fetch.spec.whatwg.org) allows users to skip consuming the response body by relying on
265[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.
266
267Garbage collection in Node is less aggressive and deterministic
268(due to the lack of clear idle periods that browsers have through the rendering refresh rate)
269which means that leaving the release of connection resources to the garbage collector can lead
270to excessive connection usage, reduced performance (due to less connection re-use), and even
271stalls or deadlocks when running out of connections.
272
273```js
274// Do
275const headers = await fetch(url)
276 .then(async res => {
277 for await (const chunk of res.body) {
278 // force consumption of body
279 }
280 return res.headers
281 })
282
283// Do not
284const headers = await fetch(url)
285 .then(res => res.headers)
286```
287
288However, 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.
289
290```js
291const headers = await fetch(url, { method: 'HEAD' })
292 .then(res => res.headers)
293```
294
295##### Forbidden and Safelisted Header Names
296
297* https://fetch.spec.whatwg.org/#cors-safelisted-response-header-name
298* https://fetch.spec.whatwg.org/#forbidden-header-name
299* https://fetch.spec.whatwg.org/#forbidden-response-header-name
300* https://github.com/wintercg/fetch/issues/6
301
302The [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.
303
304### `undici.upgrade([url, options]): Promise`
305
306Upgrade 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.
307
308Arguments:
309
310* **url** `string | URL | UrlObject`
311* **options** [`UpgradeOptions`](docs/api/Dispatcher.md#parameter-upgradeoptions)
312 * **dispatcher** `Dispatcher` - Default: [getGlobalDispatcher](#undicigetglobaldispatcher)
313 * **maxRedirections** `Integer` - Default: `0`
314* **callback** `(error: Error | null, data: UpgradeData) => void` (optional)
315
316Returns a promise with the result of the `Dispatcher.upgrade` method.
317
318Calls `options.dispatcher.upgrade(options)`.
319
320See [Dispatcher.upgrade](docs/api/Dispatcher.md#dispatcherupgradeoptions-callback) for more details.
321
322### `undici.setGlobalDispatcher(dispatcher)`
323
324* dispatcher `Dispatcher`
325
326Sets the global dispatcher used by Common API Methods.
327
328### `undici.getGlobalDispatcher()`
329
330Gets the global dispatcher used by Common API Methods.
331
332Returns: `Dispatcher`
333
334### `undici.setGlobalOrigin(origin)`
335
336* origin `string | URL | undefined`
337
338Sets the global origin used in `fetch`.
339
340If `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.
341
342```js
343setGlobalOrigin('http://localhost:3000')
344
345const response = await fetch('/api/ping')
346
347console.log(response.url) // http://localhost:3000/api/ping
348```
349
350### `undici.getGlobalOrigin()`
351
352Gets the global origin used in `fetch`.
353
354Returns: `URL`
355
356### `UrlObject`
357
358* **port** `string | number` (optional)
359* **path** `string` (optional)
360* **pathname** `string` (optional)
361* **hostname** `string` (optional)
362* **origin** `string` (optional)
363* **protocol** `string` (optional)
364* **search** `string` (optional)
365
366## Specification Compliance
367
368This section documents parts of the HTTP/1.1 specification that Undici does
369not support or does not fully implement.
370
371### Expect
372
373Undici does not support the `Expect` request header field. The request
374body is always immediately sent and the `100 Continue` response will be
375ignored.
376
377Refs: https://tools.ietf.org/html/rfc7231#section-5.1.1
378
379### Pipelining
380
381Undici will only use pipelining if configured with a `pipelining` factor
382greater than `1`.
383
384Undici always assumes that connections are persistent and will immediately
385pipeline requests, without checking whether the connection is persistent.
386Hence, automatic fallback to HTTP/1.0 or HTTP/1.1 without pipelining is
387not supported.
388
389Undici will immediately pipeline when retrying requests after a failed
390connection. However, Undici will not retry the first remaining requests in
391the prior pipeline and instead error the corresponding callback/promise/stream.
392
393Undici will abort all running requests in the pipeline when any of them are
394aborted.
395
396* Refs: https://tools.ietf.org/html/rfc2616#section-8.1.2.2
397* Refs: https://tools.ietf.org/html/rfc7230#section-6.3.2
398
399### Manual Redirect
400
401Since it is not possible to manually follow an HTTP redirect on the server-side,
402Undici returns the actual response instead of an `opaqueredirect` filtered one
403when invoked with a `manual` redirect. This aligns `fetch()` with the other
404implementations in Deno and Cloudflare Workers.
405
406Refs: https://fetch.spec.whatwg.org/#atomic-http-redirect-handling
407
408## Workarounds
409
410### Network address family autoselection.
411
412If you experience problem when connecting to a remote server that is resolved by your DNS servers to a IPv6 (AAAA record)
413first, there are chances that your local router or ISP might have problem connecting to IPv6 networks. In that case
414undici will throw an error with code `UND_ERR_CONNECT_TIMEOUT`.
415
416If the target server resolves to both a IPv6 and IPv4 (A records) address and you are using a compatible Node version
417(18.3.0 and above), you can fix the problem by providing the `autoSelectFamily` option (support by both `undici.request`
418and `undici.Agent`) which will enable the family autoselection algorithm when establishing the connection.
419
420## Collaborators
421
422* [__Daniele Belardi__](https://github.com/dnlup), <https://www.npmjs.com/~dnlup>
423* [__Ethan Arrowood__](https://github.com/ethan-arrowood), <https://www.npmjs.com/~ethan_arrowood>
424* [__Matteo Collina__](https://github.com/mcollina), <https://www.npmjs.com/~matteo.collina>
425* [__Matthew Aitken__](https://github.com/KhafraDev), <https://www.npmjs.com/~khaf>
426* [__Robert Nagy__](https://github.com/ronag), <https://www.npmjs.com/~ronag>
427* [__Szymon Marczak__](https://github.com/szmarczak), <https://www.npmjs.com/~szmarczak>
428* [__Tomas Della Vedova__](https://github.com/delvedor), <https://www.npmjs.com/~delvedor>
429
430### Releasers
431
432* [__Ethan Arrowood__](https://github.com/ethan-arrowood), <https://www.npmjs.com/~ethan_arrowood>
433* [__Matteo Collina__](https://github.com/mcollina), <https://www.npmjs.com/~matteo.collina>
434* [__Robert Nagy__](https://github.com/ronag), <https://www.npmjs.com/~ronag>
435
436## License
437
438MIT
439