undici Documentation

An HTTP/1.1 client, written from scratch for Node.js

7,654 stars JavaScript JavaScript 1 file ยท ~20,584 tokens #client#http#nodejs
RAW Doc

Docker & Self-Hosted Deployment Verified OCI

Deploy undici on any server, homelab, or cloud container platform.

1-Click Terminal Command
bash (docker run)
docker run -d --name undici -p 1080:1080 --restart always serjs/go-socks5-proxy:latest
Configured Services & Ports
โš™๏ธ socks5-no-auth
image: serjs/go-socks5-proxy:latest
๐Ÿ”Œ Ports: 1080:1080
โš™๏ธ socks5-auth
image: serjs/go-socks5-proxy:latest
๐Ÿ”Œ Ports: 1081:1081
โš™๏ธ dante-socks5
image: build locally
๐Ÿ”Œ Ports: 1082:1080
โš™๏ธ http-server
image: node:20-alpine
๐Ÿ”Œ Ports: 8080:8080
โš™๏ธ https-server
image: node:20-alpine
๐Ÿ”Œ Ports: 8443:8443
โš™๏ธ echo-server
image: ealen/echo-server:latest
๐Ÿ”Œ Ports: 3000:3000
โš™๏ธ blocked-server
image: alpine:latest
docker-compose.yml
yaml (docker-compose.yml)
services:
  # SOCKS5 proxy without authentication
  socks5-no-auth:
    image: serjs/go-socks5-proxy:latest
    container_name: socks5-no-auth
    environment:
      - PROXY_PORT=1080
      - REQUIRE_AUTH=false
    ports:
      - "1080:1080"
    networks:
      - test-network

  # SOCKS5 proxy with username/password authentication
  socks5-auth:
    image: serjs/go-socks5-proxy:latest
    container_name: socks5-auth
    environment:
      - PROXY_USER=testuser
      - PROXY_PASSWORD=testpass
      - PROXY_PORT=1081
    ports:
      - "1081:1081"
    networks:
      - test-network

  # Alternative: Dante SOCKS5 server (more configurable)
  dante-socks5:
    build:
      context: ./dante
      dockerfile: Dockerfile
    container_name: dante-socks5
    ports:
      - "1082:1080"
    networks:
      - test-network
    volumes:
      - ./dante/danted.conf:/etc/danted.conf:ro

  # HTTP test server
  http-server:
    image: node:20-alpine
    container_name: http-test-server
    working_dir: /app
    volumes:
      - ../servers/http-server.js:/app/server.js:ro
    command: node server.js
    ports:
      - "8080:8080"
    networks:
      - test-network

  # HTTPS test server
  https-server:
    image: node:20-alpine
    container_name: https-test-server
    working_dir: /app
    volumes:
      - ../servers/https-server.js:/app/server.js:ro
      - ../certs:/app/certs:ro
    command: node server.js
    ports:
      - "8443:8443"
    networks:
      - test-network

  # Echo server for testing
  echo-server:
    image: ealen/echo-server:latest
    container_name: echo-server
    environment:
      - PORT=3000
    ports:
      - "3000:3000"
    networks:
      - test-network

  # Blocked target (for testing connection failures)
  blocked-server:
    image: alpine:latest
    container_name: blocked-server
    command: sleep infinity
    networks:
      - isolated-network

networks:
  test-network:
    driver: bridge
  isolated-network:
    driver: bridge
    internal: true

1. Project Overview & Quickstart (nodejs/undici)

File: README.md

undici

An HTTP/1.1 client, written from scratch for Node.js.

Undici means eleven in Italian. 1.1 -> 11 -> Eleven -> Undici.
It is also a Stranger Things reference.

How to get involved

Have a question about using Undici? Open a Q&A Discussion or join our official OpenJS Slack channel.

Looking to contribute? Start by reading the contributing guide

Install

text
npm i undici

Benchmarks

The benchmark is a simple getting data example using
50 TCP connections with a pipelining depth of 10 running on Node 24.14.1.

HTTP/1.1

text
โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
โ”‚  Tests                 โ”‚ Samples โ”‚ Result             โ”‚ Tolerance  โ”‚ Difference with slowest โ”‚
โ”œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ผโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ผโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ผโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ผโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ค
โ”‚  'node-fetch'          โ”‚ 50      โ”‚ '4711.86 req/sec'  โ”‚ 'ยฑ 2.92 %' โ”‚ '-'                     โ”‚
โ”‚  'undici - fetch'      โ”‚ 75      โ”‚ '5438.50 req/sec'  โ”‚ 'ยฑ 2.97 %' โ”‚ '+ 15.42 %'             โ”‚
โ”‚  'axios'               โ”‚ 45      โ”‚ '5448.08 req/sec'  โ”‚ 'ยฑ 2.98 %' โ”‚ '+ 15.62 %'             โ”‚
โ”‚  'request'             โ”‚ 65      โ”‚ '5809.63 req/sec'  โ”‚ 'ยฑ 2.90 %' โ”‚ '+ 23.30 %'             โ”‚
โ”‚  'http - no keepalive' โ”‚ 35      โ”‚ '5910.77 req/sec'  โ”‚ 'ยฑ 2.87 %' โ”‚ '+ 25.44 %'             โ”‚
โ”‚  'got'                 โ”‚ 50      โ”‚ '6047.80 req/sec'  โ”‚ 'ยฑ 2.91 %' โ”‚ '+ 28.35 %'             โ”‚
โ”‚  'superagent'          โ”‚ 60      โ”‚ '7534.53 req/sec'  โ”‚ 'ยฑ 2.97 %' โ”‚ '+ 59.91 %'             โ”‚
โ”‚  'http - keepalive'    โ”‚ 75      โ”‚ '9343.41 req/sec'  โ”‚ 'ยฑ 2.90 %' โ”‚ '+ 98.30 %'             โ”‚
โ”‚  'undici - pipeline'   โ”‚ 65      โ”‚ '13470.70 req/sec' โ”‚ 'ยฑ 2.93 %' โ”‚ '+ 185.89 %'            โ”‚
โ”‚  'undici - request'    โ”‚ 80      โ”‚ '16850.87 req/sec' โ”‚ 'ยฑ 2.93 %' โ”‚ '+ 257.63 %'            โ”‚
โ”‚  'undici - stream'     โ”‚ 101     โ”‚ '18488.56 req/sec' โ”‚ 'ยฑ 3.81 %' โ”‚ '+ 292.38 %'            โ”‚
โ”‚  'undici - dispatch'   โ”‚ 101     โ”‚ '20786.44 req/sec' โ”‚ 'ยฑ 3.08 %' โ”‚ '+ 341.15 %'            โ”‚
โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ดโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ดโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ดโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ดโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜

HTTP/1.1 over HTTPS

Using benchmark-https.js against an h1-over-TLS server (50 connections, pipelining depth 10, Node 24.14.1).

text
โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
โ”‚  Tests                 โ”‚ Samples โ”‚ Result            โ”‚ Tolerance  โ”‚ Difference with slowest โ”‚
โ”œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ผโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ผโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ผโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ผโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ค
โ”‚  'https - no keepalive'โ”‚ 10      โ”‚ '1358.40 req/sec' โ”‚ 'ยฑ 1.99 %' โ”‚ '-'                     โ”‚
โ”‚  'undici - fetch'      โ”‚ 30      โ”‚ '3721.76 req/sec' โ”‚ 'ยฑ 2.97 %' โ”‚ '+ 173.98 %'            โ”‚
โ”‚  'https - keepalive'   โ”‚ 35      โ”‚ '5633.91 req/sec' โ”‚ 'ยฑ 2.84 %' โ”‚ '+ 314.75 %'            โ”‚
โ”‚  'undici - pipeline'   โ”‚ 15      โ”‚ '6254.05 req/sec' โ”‚ 'ยฑ 2.80 %' โ”‚ '+ 360.40 %'            โ”‚
โ”‚  'undici - request'    โ”‚ 25      โ”‚ '6669.80 req/sec' โ”‚ 'ยฑ 2.73 %' โ”‚ '+ 391.01 %'            โ”‚
โ”‚  'undici - stream'     โ”‚ 25      โ”‚ '7019.04 req/sec' โ”‚ 'ยฑ 2.77 %' โ”‚ '+ 416.71 %'            โ”‚
โ”‚  'undici - dispatch'   โ”‚ 20      โ”‚ '7361.85 req/sec' โ”‚ 'ยฑ 2.90 %' โ”‚ '+ 441.95 %'            โ”‚
โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ดโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ดโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ดโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ดโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜

HTTP/2

Using benchmark-http2.js against an h2 server (50 connections, pipelining depth 10, Node 24.14.1).

text
โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
โ”‚  Tests                 โ”‚ Samples โ”‚ Result            โ”‚ Tolerance  โ”‚ Difference with slowest โ”‚
โ”œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ผโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ผโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ผโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ผโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ค
โ”‚  'undici - fetch'      โ”‚ 45      โ”‚ '3499.03 req/sec' โ”‚ 'ยฑ 2.93 %' โ”‚ '-'                     โ”‚
โ”‚  'native - http2'      โ”‚ 25      โ”‚ '4904.58 req/sec' โ”‚ 'ยฑ 2.81 %' โ”‚ '+ 40.17 %'             โ”‚
โ”‚  'undici - pipeline'   โ”‚ 60      โ”‚ '5836.82 req/sec' โ”‚ 'ยฑ 2.99 %' โ”‚ '+ 66.81 %'             โ”‚
โ”‚  'undici - request'    โ”‚ 65      โ”‚ '6831.25 req/sec' โ”‚ 'ยฑ 2.83 %' โ”‚ '+ 95.23 %'             โ”‚
โ”‚  'undici - stream'     โ”‚ 55      โ”‚ '6874.30 req/sec' โ”‚ 'ยฑ 2.91 %' โ”‚ '+ 96.46 %'             โ”‚
โ”‚  'undici - dispatch'   โ”‚ 55      โ”‚ '7791.23 req/sec' โ”‚ 'ยฑ 2.96 %' โ”‚ '+ 122.67 %'            โ”‚
โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ดโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ดโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ดโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ดโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜

Undici vs. Fetch

Overview

Node.js includes a built-in fetch() implementation powered by undici starting from Node.js v18. However, there are important differences between using the built-in fetch and installing undici as a separate module.

Built-in Fetch (Node.js v18+)

Node.js's built-in fetch is powered by a bundled version of undici:

js
// Available globally in Node.js v18+
const response = await fetch('https://api.example.com/data');
const data = await response.json();

// Check the bundled undici version
console.log(process.versions.undici); // e.g., "5.28.4"

Pros:

  • No additional dependencies required
  • Works across different JavaScript runtimes
  • Automatic compression handling (gzip, deflate, br)
  • Built-in caching support (in development)

Cons:

  • Limited to the undici version bundled with your Node.js version
  • Less control over connection pooling and advanced features
  • Error handling follows Web API standards (errors wrapped in TypeError)
  • Performance overhead due to Web Streams implementation

Undici Module

Installing undici as a separate module gives you access to the latest features and APIs:

bash
npm install undici
js
import { request, fetch, Agent, setGlobalDispatcher } from 'undici';

// Use undici.request for maximum performance
const { statusCode, headers, body } = await request('https://api.example.com/data');
const data = await body.json();

// Or use undici.fetch with custom configuration
const agent = new Agent({ keepAliveTimeout: 10000 });
setGlobalDispatcher(agent);
const response = await fetch('https://api.example.com/data');

Pros:

  • Latest undici features and bug fixes
  • Access to advanced APIs (request, stream, pipeline)
  • Fine-grained control over connection pooling
  • Better error handling with clearer error messages
  • Superior performance, especially with undici.request
  • HTTP/1.1 pipelining support
  • Custom interceptors and middleware
  • Advanced features like ProxyAgent, Socks5Agent, MockAgent

Cons:

  • Additional dependency to manage
  • Larger bundle size

When to Use Each

Use Built-in Fetch When:

  • You want zero dependencies
  • Building isomorphic code that runs in browsers and Node.js
  • Publishing to npm and want to maximize compatibility with JS runtimes
  • Simple HTTP requests without advanced configuration
  • You're publishing to npm and you want to maximize compatiblity
  • You don't depend on features from a specific version of undici

Use Undici Module When:

  • You need the latest undici features and performance improvements
  • You require advanced connection pooling configuration
  • You need APIs not available in the built-in fetch (ProxyAgent, Socks5Agent, MockAgent, etc.)
  • Performance is critical (use undici.request for maximum speed)
  • You want better error handling and debugging capabilities
  • You need HTTP/1.1 pipelining or advanced interceptors
  • You prefer decoupled protocol and API interfaces

Performance Comparison

Based on benchmarks, here's the typical performance hierarchy:

  1. undici.request() - Fastest, most efficient
  2. undici.fetch() - Good performance, standard compliance
  3. Node.js http/https - Baseline performance

Migration Guide

If you're currently using built-in fetch and want to migrate to undici:

js
// Before: Built-in fetch
const response = await fetch('https://api.example.com/data');

// After: Undici fetch (drop-in replacement)
import { fetch } from 'undici';
const response = await fetch('https://api.example.com/data');

// Or: Undici request (better performance)
import { request } from 'undici';
const { statusCode, body } = await request('https://api.example.com/data');
const data = await body.json();

Keep `fetch` and `FormData` together

When you send a FormData body, keep fetch and FormData from the same
implementation.

Use one of these patterns:

js
// Built-in globals
const body = new FormData()
body.set('name', 'some')
await fetch('https://example.com', {
  method: 'POST',
  body
})
js
// undici module imports
import { fetch, FormData } from 'undici'

const body = new FormData()
body.set('name', 'some')
await fetch('https://example.com', {
  method: 'POST',
  body
})

If you want the installed undici package to provide the globals, call
install() first:

js
import { install } from 'undici'

install()

const body = new FormData()
body.set('name', 'some')
await fetch('https://example.com', {
  method: 'POST',
  body
})

install() replaces the global fetch, Headers, Response, Request, and
FormData implementations with undici's versions, so they all match. It also
installs undici's WebSocket, CloseEvent, ErrorEvent, MessageEvent, and
EventSource globals.

Avoid mixing a global FormData with undici.fetch(), or undici.FormData
with the built-in global fetch().

Version Compatibility

You can check which version of undici is bundled with your Node.js version:

js
console.log(process.versions.undici);

Installing undici as a module allows you to use a newer version than what's bundled with Node.js, giving you access to the latest features and performance improvements.

Quick Start

Basic Request

js
import { request } from 'undici'

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

console.log('response received', statusCode)
console.log('headers', headers)

for await (const data of body) { console.log('data', data) }

console.log('trailers', trailers)

Using Cache Interceptor

Undici provides a powerful HTTP caching interceptor that follows HTTP caching best practices. Here's how to use it:

js
import { fetch, Agent, interceptors, cacheStores } from 'undici';

// Create a client with cache interceptor
const client = new Agent().compose(interceptors.cache({
  // Optional: Configure cache store (defaults to MemoryCacheStore)
  store: new cacheStores.MemoryCacheStore({
    maxSize: 100 * 1024 * 1024, // 100MB
    maxCount: 1000,
    maxEntrySize: 5 * 1024 * 1024 // 5MB
  }),
  
  // Optional: Specify which HTTP methods to cache (default: ['GET', 'HEAD'])
  methods: ['GET', 'HEAD']
}));

// Set the global dispatcher to use our caching client
setGlobalDispatcher(client);

// Now all fetch requests will use the cache
async function getData() {
  const response = await fetch('https://api.example.com/data');
  // The server should set appropriate Cache-Control headers in the response
  // which the cache will respect based on the cache policy
  return response.json();
}

// First request - fetches from origin
const data1 = await getData();

// Second request - served from cache if within max-age
const data2 = await getData();

Key Features:

  • Automatic Caching: Respects Cache-Control and Expires headers
  • Validation: Supports ETag and Last-Modified validation
  • Storage Options: In-memory or persistent SQLite storage
  • Flexible: Configure cache size, TTL, and more

Global Installation

Undici provides an install() function to add fetch-related and other web API classes to globalThis, making them available globally:

js
import { install } from 'undici'

// Install undici's global web APIs
install()

// Now you can use fetch classes globally without importing
const response = await fetch('https://api.example.com/data')
const data = await response.json()

// All classes are available globally:
const headers = new Headers([['content-type', 'application/json']])
const request = new Request('https://example.com')
const formData = new FormData()
const ws = new WebSocket('wss://example.com')
const eventSource = new EventSource('https://example.com/events')

The install() function adds the following classes to globalThis:

  • fetch - The fetch function
  • Headers - HTTP headers management
  • Response - HTTP response representation
  • Request - HTTP request representation
  • FormData - Form data handling
  • WebSocket - WebSocket client
  • CloseEvent, ErrorEvent, MessageEvent - WebSocket events
  • EventSource - Server-sent events client

When you call install(), these globals come from the same undici
implementation. For example, global fetch and global FormData will both be
undici's versions, and WebSocket and EventSource will also come from
undici, which is the recommended setup if you want to use undici through
globals.

This is useful for:

  • Polyfilling environments that don't have fetch
  • Ensuring consistent fetch behavior across different Node.js versions
  • Making undici's implementations available globally for libraries that expect them

Body Mixins

The body mixins are the most common way to format the request/response body. Mixins include:

[!NOTE]
The body returned from undici.request does not implement .formData().

[!WARNING]
Calling body.formData() on a fetch response causes undici to buffer and parse the entire body. Since this is dictated by the spec, body.formData() must only be called on responses from trusted servers.

Example usage:

js
import { request } from 'undici'

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

console.log('response received', statusCode)
console.log('headers', headers)
console.log('data', await body.json())
console.log('trailers', trailers)

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.

Should 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.

For more information about their behavior, please reference the body mixin from the Fetch Standard.

Common API Methods

This section documents our most commonly used API methods. Additional APIs are documented in their own files within the docs folder and are accessible via the navigation list on the left side of the docs site.

For the top-level APIs below, the url argument supplies the request origin and
path. Do not pass origin or path in the second options argument. The linked
Dispatcher option types include those fields because dispatcher methods are
lower-level APIs that do not receive a separate url argument.

`undici.request([url, options]): Promise`

Arguments:

  • url string | URL | UrlObject
  • options RequestOptions
    • dispatcher Dispatcher - Default: getGlobalDispatcher
    • method String - Default: PUT if options.body, otherwise GET

Returns a promise with the result of the Dispatcher.request method.

Calls options.dispatcher.request(options).

See Dispatcher.request for more details, and request examples for examples.

`undici.stream([url, options, ]factory): Promise`

Arguments:

  • url string | URL | UrlObject
  • options StreamOptions
    • dispatcher Dispatcher - Default: getGlobalDispatcher
    • method String - Default: PUT if options.body, otherwise GET
  • factory Dispatcher.stream.factory

Returns a promise with the result of the Dispatcher.stream method.

Calls options.dispatcher.stream(options, factory).

See Dispatcher.stream for more details.

`undici.pipeline([url, options, ]handler): Duplex`

Arguments:

  • url string | URL | UrlObject
  • options PipelineOptions
    • dispatcher Dispatcher - Default: getGlobalDispatcher
    • method String - Default: PUT if options.body, otherwise GET
  • handler Dispatcher.pipeline.handler

Returns: stream.Duplex

Calls options.dispatch.pipeline(options, handler).

See Dispatcher.pipeline for more details.

`undici.connect([url, options]): Promise`

Starts two-way communications with the requested resource using HTTP CONNECT.

Arguments:

  • url string | URL | UrlObject
  • options ConnectOptions
  • callback (err: Error | null, data: ConnectData | null) => void (optional)

Returns a promise with the result of the Dispatcher.connect method.

Calls options.dispatch.connect(options).

See Dispatcher.connect for more details.

`undici.fetch(input[, init]): Promise`

Implements fetch.

Basic usage example:

js
import { fetch } from 'undici'


const res = await fetch('https://example.com')
const json = await res.json()
console.log(json)

You can pass an optional dispatcher to fetch as:

js
import { fetch, Agent } from 'undici'

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

`request.body`

A body can be of the following types:

  • ArrayBuffer
  • ArrayBufferView
  • AsyncIterables
  • Blob
  • Iterables
  • String
  • URLSearchParams
  • FormData

In this implementation of fetch, request.body now accepts Async Iterables. It is not present in the Fetch Standard.

js
import { fetch } from 'undici'

const data = {
  async *[Symbol.asyncIterator]() {
    yield 'hello'
    yield 'world'
  },
}

await fetch('https://example.com', { body: data, method: 'POST', duplex: 'half' })

FormData besides text data and buffers can also utilize streams via Blob objects:

js
import { openAsBlob } from 'node:fs'

const file = await openAsBlob('./big.csv')
const body = new FormData()
body.set('file', file, 'big.csv')

await fetch('http://example.com', { method: 'POST', body })

`request.duplex`

  • 'half'

In this implementation of fetch, request.duplex must be set if request.body is ReadableStream or Async Iterables, however, even though the value must be set to 'half', it is actually a full duplex. For more detail refer to the Fetch Standard.

`response.body`

Nodejs has two kinds of streams: web streams, which follow the API of the WHATWG web standard found in browsers, and an older Node-specific streams API. 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().

js
import { fetch } from 'undici'
import { Readable } from 'node:stream'

const response = await fetch('https://example.com')
const readableWebStream = response.body
const readableNodeStream = Readable.fromWeb(readableWebStream)

Specification Compliance

This section documents parts of the HTTP/1.1 and Fetch Standard that Undici does
not support or does not fully implement.

CORS

Unlike browsers, Undici does not implement CORS (Cross-Origin Resource Sharing) checks by default. This means:

  • No preflight requests are automatically sent for cross-origin requests
  • No validation of Access-Control-Allow-Origin headers is performed
  • Requests to any origin are allowed regardless of the source

This behavior is intentional for server-side environments where CORS restrictions are typically unnecessary. If your application requires CORS-like protections, you will need to implement these checks manually.

Garbage Collection

The Fetch Standard allows users to skip consuming the response body by relying on
garbage collection to release connection resources.

Garbage collection in Node is less aggressive and deterministic
(due to the lack of clear idle periods that browsers have through the rendering refresh rate)
which means that leaving the release of connection resources to the garbage collector can lead
to excessive connection usage, reduced performance (due to less connection re-use), and even
stalls or deadlocks when running out of connections.
Therefore, it is important to always either consume or cancel the response body anyway.

js
// Do
const { body, headers } = await fetch(url);
for await (const chunk of body) {
  // force consumption of body
}

// Do not
const { headers } = await fetch(url);

However, 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 for more details.

js
const headers = await fetch(url, { method: 'HEAD' })
  .then(res => res.headers)

Note that consuming the response body is mandatory for request:

js
// Do
const { body, headers } = await request(url);
await body.dump(); // force consumption of body

// Do not
const { headers } = await request(url);

Forbidden and Safelisted Header Names

The Fetch Standard 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.

Content-Encoding

Undici limits the number of Content-Encoding layers in a response to 5 to prevent resource exhaustion attacks. If a server responds with more than 5 content-encodings (e.g., Content-Encoding: gzip, gzip, gzip, gzip, gzip, gzip), the fetch will be rejected with an error. This limit matches the approach taken by curl and urllib3.

`undici.upgrade([url, options]): Promise`

Upgrade to a different protocol. See MDN - HTTP - Protocol upgrade mechanism for more details.

Arguments:

  • url string | URL | UrlObject
  • options UpgradeOptions
  • callback (error: Error | null, data: UpgradeData) => void (optional)

Returns a promise with the result of the Dispatcher.upgrade method.

Calls options.dispatcher.upgrade(options).

See Dispatcher.upgrade for more details.

`undici.setGlobalDispatcher(dispatcher)`

  • dispatcher Dispatcher

Sets the global dispatcher used by Common API Methods. Global dispatcher is shared among compatible undici modules,
including undici that is bundled internally with node.js.

Undici stores this dispatcher under Symbol.for('undici.globalDispatcher.2').

setGlobalDispatcher() also mirrors the configured dispatcher to
Symbol.for('undici.globalDispatcher.1') using Dispatcher1Wrapper, so Node.js built-in fetch
can keep using the legacy handler contract while Undici uses the new handler API.

`undici.getGlobalDispatcher()`

Gets the global dispatcher used by Common API Methods.

Returns: Dispatcher

`undici.setGlobalOrigin(origin)`

  • origin string | URL | undefined

Sets the global origin used in fetch.

If 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.

js
setGlobalOrigin('http://localhost:3000')

const response = await fetch('/api/ping')

console.log(response.url) // http://localhost:3000/api/ping

`undici.getGlobalOrigin()`

Gets the global origin used in fetch.

Returns: URL

`UrlObject`

  • port string | number (optional)
  • path string (optional)
  • pathname string (optional)
  • hostname string (optional)
  • origin string (optional)
  • protocol string (optional)
  • search string (optional)

Expect

Undici does not support the Expect request header field. The request
body is always immediately sent and the 100 Continue response will be
ignored.

Refs: https://tools.ietf.org/html/rfc7231#section-5.1.1

Pipelining

Undici will only use pipelining if configured with a pipelining factor
greater than 1. Only enable pipelining when the remote server is trusted.
Also it is important to pass blocking: false to the request options to
properly pipeline requests.

Undici always assumes that connections are persistent and will immediately
pipeline requests, without checking whether the connection is persistent.
Hence, automatic fallback to HTTP/1.0 or HTTP/1.1 without pipelining is
not supported.

Undici will immediately pipeline when retrying requests after a failed
connection. However, Undici will not retry the first remaining requests in
the prior pipeline and instead error the corresponding callback/promise/stream.

Undici will abort all running requests in the pipeline when any of them are
aborted.

Manual Redirect

Since it is not possible to manually follow an HTTP redirect on the server-side,
Undici returns the actual response instead of an opaqueredirect filtered one
when invoked with a manual redirect. This aligns fetch() with the other
implementations in Deno and Cloudflare Workers.

Refs: https://fetch.spec.whatwg.org/#atomic-http-redirect-handling

Workarounds

Network address family autoselection.

If you experience problem when connecting to a remote server that is resolved by your DNS servers to a IPv6 (AAAA record)
first, there are chances that your local router or ISP might have problem connecting to IPv6 networks. In that case
undici will throw an error with code UND_ERR_CONNECT_TIMEOUT.

If the target server resolves to both a IPv6 and IPv4 (A records) address and you are using a compatible Node version
(18.3.0 and above), you can fix the problem by providing the autoSelectFamily option (support by both undici.request
and undici.Agent) which will enable the family autoselection algorithm when establishing the connection.

Collaborators

Past Collaborators

Releasers

Long Term Support

Undici aligns with the Node.js LTS schedule. The following table shows the supported versions:

Undici Version Bundled in Node.js Node.js Versions Supported End of Life
5.x 18.x โ‰ฅ14.0 (tested: 14, 16, 18) 2024-04-30
6.x 20.x, 22.x โ‰ฅ18.17 (tested: 18, 20, 21, 22) 2027-04-30
7.x 24.x โ‰ฅ20.18.1 (tested: 20, 22, 24) 2028-04-30
8.x 26.x โ‰ฅ22.19.0 (tested: 22, 24, 26) 2029-04-30

License

MIT


File: docs/docs/best-practices/client-certificate.md

Client certificate

Client certificate authentication can be configured with the Client, the required options are passed along through the connect option.

The client certificates must be signed by a trusted CA. The Node.js default is to trust the well-known CAs curated by Mozilla.

Setting the server option requestCert: true tells the server to request the client certificate.

The server option rejectUnauthorized: false allows us to handle any invalid certificate errors in client code. The authorized property on the socket of the incoming request will show if the client certificate was valid. The authorizationError property will give the reason if the certificate was not valid.

Client Certificate Authentication

js
const { readFileSync } = require('node:fs')
const { join } = require('node:path')
const { createServer } = require('node:https')
const { Client } = require('undici')

const serverOptions = {
  ca: [
    readFileSync(join(__dirname, 'client-ca-crt.pem'), 'utf8')
  ],
  key: readFileSync(join(__dirname, 'server-key.pem'), 'utf8'),
  cert: readFileSync(join(__dirname, 'server-crt.pem'), 'utf8'),
  requestCert: true,
  rejectUnauthorized: false
}

const server = createServer(serverOptions, (req, res) => {
  // true if client cert is valid
  if(req.client.authorized === true) {
    console.log('valid')
  } else {
    console.error(req.client.authorizationError)
  }
  res.end()
})

server.listen(0, function () {
  const tls = {
    ca: [
      readFileSync(join(__dirname, 'server-ca-crt.pem'), 'utf8')
    ],
    key: readFileSync(join(__dirname, 'client-key.pem'), 'utf8'),
    cert: readFileSync(join(__dirname, 'client-crt.pem'), 'utf8'),
    rejectUnauthorized: false,
    servername: 'agent1'
  }
  const client = new Client(`https://localhost:${server.address().port}`, {
    connect: tls
  })

  client.request({
    path: '/',
    method: 'GET'
  }, (err, { body }) => {
    body.on('data', (buf) => {})
    body.on('end', () => {
      client.close()
      server.close()
    })
  })
})

File: docs/docs/best-practices/mocking-request.md

Mocking Request

Undici has its own mocking utility. It allow us to intercept undici HTTP requests and return mocked values instead. It can be useful for testing purposes.

Example:

js
// bank.mjs
import { request } from 'undici'

export async function bankTransfer(recipient, amount) {
  const { body } = await request('http://localhost:3000/bank-transfer',
    {
      method: 'POST',
      headers: {
        'X-TOKEN-SECRET': 'SuperSecretToken',
      },
      body: JSON.stringify({
        recipient,
        amount
      })
    }
  )
  return await body.json()
}

And this is what the test file looks like:

js
// index.test.mjs
import { strict as assert } from 'node:assert'
import { MockAgent, setGlobalDispatcher, } from 'undici'
import { bankTransfer } from './bank.mjs'

const mockAgent = new MockAgent();

setGlobalDispatcher(mockAgent);

// Provide the base url to the request
const mockPool = mockAgent.get('http://localhost:3000');

// intercept the request
mockPool.intercept({
  path: '/bank-transfer',
  method: 'POST',
  headers: {
    'X-TOKEN-SECRET': 'SuperSecretToken',
  },
  body: JSON.stringify({
    recipient: '1234567890',
    amount: '100'
  })
}).reply(200, {
  message: 'transaction processed'
})

const success = await bankTransfer('1234567890', '100')

assert.deepEqual(success, { message: 'transaction processed' })

// if you dont want to check whether the body or the headers contain the same value
// just remove it from interceptor
mockPool.intercept({
  path: '/bank-transfer',
  method: 'POST',
}).reply(400, {
  message: 'bank account not found'
})

const badRequest = await bankTransfer('1234567890', '100')

assert.deepEqual(badRequest, { message: 'bank account not found' })

Explore other MockAgent functionality here

Access agent call history

Using a MockAgent also allows you to make assertions on the configuration used to make your request in your application.

Here is an example :

js
// index.test.mjs
import { strict as assert } from 'node:assert'
import { MockAgent, setGlobalDispatcher, fetch } from 'undici'
import { app } from './app.mjs'

// given an application server running on http://localhost:3000
await app.start()

// enable call history at instantiation
const mockAgent = new MockAgent({ enableCallHistory: true })
// or after instantiation
mockAgent.enableCallHistory()

setGlobalDispatcher(mockAgent)

// this call is made (not intercepted)
await fetch(`http://localhost:3000/endpoint?query='hello'`, {
  method: 'POST',
  headers: { 'content-type': 'application/json' },
  body: JSON.stringify({ data: '' })
})

// access to the call history of the MockAgent (which register every call made intercepted or not)
assert.ok(mockAgent.getCallHistory()?.calls().length === 1)
assert.strictEqual(mockAgent.getCallHistory()?.firstCall()?.fullUrl, `http://localhost:3000/endpoint?query='hello'`)
assert.strictEqual(mockAgent.getCallHistory()?.firstCall()?.body, JSON.stringify({ data: '' }))
assert.deepStrictEqual(mockAgent.getCallHistory()?.firstCall()?.searchParams, { query: 'hello' })
assert.strictEqual(mockAgent.getCallHistory()?.firstCall()?.port, '3000')
assert.strictEqual(mockAgent.getCallHistory()?.firstCall()?.host, 'localhost:3000')
assert.strictEqual(mockAgent.getCallHistory()?.firstCall()?.method, 'POST')
assert.strictEqual(mockAgent.getCallHistory()?.firstCall()?.path, '/endpoint')
assert.deepStrictEqual(mockAgent.getCallHistory()?.firstCall()?.headers, { 'content-type': 'application/json' })

// clear all call history logs
mockAgent.clearCallHistory()

assert.ok(mockAgent.getCallHistory()?.calls().length === 0)

Calling mockAgent.close() will automatically clear and delete every call history for you.

Explore other MockAgent functionality here

Explore other MockCallHistory functionality here

Explore other MockCallHistoryLog functionality here

Debug Mock Value

When the interceptor and the request options are not the same, undici will automatically make a real HTTP request. To prevent real requests from being made, use mockAgent.disableNetConnect():

js
const mockAgent = new MockAgent();

setGlobalDispatcher(mockAgent);
mockAgent.disableNetConnect()

// Provide the base url to the request
const mockPool = mockAgent.get('http://localhost:3000');

mockPool.intercept({
  path: '/bank-transfer',
  method: 'POST',
}).reply(200, {
  message: 'transaction processed'
})

const badRequest = await bankTransfer('1234567890', '100')
// Will throw an error
// MockNotMatchedError: Mock dispatch not matched for path '/bank-transfer':
// subsequent request to origin http://localhost:3000 was not allowed (net.connect disabled)

Reply with data based on request

If the mocked response needs to be dynamically derived from the request parameters, you can provide a function instead of an object to reply:

js
mockPool.intercept({
  path: '/bank-transfer',
  method: 'POST',
  headers: {
    'X-TOKEN-SECRET': 'SuperSecretToken',
  },
  body: JSON.stringify({
    recipient: '1234567890',
    amount: '100'
  })
}).reply(200, (opts) => {
  // do something with opts

  return { message: 'transaction processed' }
})

in this case opts will be

js
{
  method: 'POST',
  headers: { 'X-TOKEN-SECRET': 'SuperSecretToken' },
  body: '{"recipient":"1234567890","amount":"100"}',
  origin: 'http://localhost:3000',
  path: '/bank-transfer'
}

File: docs/docs/best-practices/proxy.md

Connecting through a proxy

Connecting through a proxy is possible by:

  • Using {ProxyAgent}.
  • Configuring Client or Pool constructor.

The proxy url should be passed to the Client or Pool constructor, while the upstream server url
should be added to every request call in the path.
For instance, if you need to send a request to the /hello route of your upstream server,
the path should be path: 'http://upstream.server:port/hello?foo=bar'.

If you proxy requires basic authentication, you can send it via the proxy-authorization header.

Connect without authentication

js
import { Client } from 'undici'
import { createServer } from 'http'
import { createProxy } from 'proxy'

const server = await buildServer()
const proxyServer = await buildProxy()

const serverUrl = `http://localhost:${server.address().port}`
const proxyUrl = `http://localhost:${proxyServer.address().port}`

server.on('request', (req, res) => {
  console.log(req.url) // '/hello?foo=bar'
  res.setHeader('content-type', 'application/json')
  res.end(JSON.stringify({ hello: 'world' }))
})

const client = new Client(proxyUrl)

const response = await client.request({
  method: 'GET',
  path: serverUrl + '/hello?foo=bar'
})

response.body.setEncoding('utf8')
let data = ''
for await (const chunk of response.body) {
  data += chunk
}
console.log(response.statusCode) // 200
console.log(JSON.parse(data)) // { hello: 'world' }

server.close()
proxyServer.close()
client.close()

function buildServer () {
  return new Promise((resolve, reject) => {
    const server = createServer()
    server.listen(0, () => resolve(server))
  })
}

function buildProxy () {
  return new Promise((resolve, reject) => {
    const server = createProxy(createServer())
    server.listen(0, () => resolve(server))
  })
}

Connect with authentication

js
import { Client } from 'undici'
import { createServer } from 'http'
import { createProxy } from 'proxy'

const server = await buildServer()
const proxyServer = await buildProxy()

const serverUrl = `http://localhost:${server.address().port}`
const proxyUrl = `http://localhost:${proxyServer.address().port}`

proxyServer.authenticate = function (req) {
  return req.headers['proxy-authorization'] === `Basic ${Buffer.from('user:pass').toString('base64')}`
}

server.on('request', (req, res) => {
  console.log(req.url) // '/hello?foo=bar'
  res.setHeader('content-type', 'application/json')
  res.end(JSON.stringify({ hello: 'world' }))
})

const client = new Client(proxyUrl)

const response = await client.request({
  method: 'GET',
  path: serverUrl + '/hello?foo=bar',
  headers: {
    'proxy-authorization': `Basic ${Buffer.from('user:pass').toString('base64')}`
  }
})

response.body.setEncoding('utf8')
let data = ''
for await (const chunk of response.body) {
  data += chunk
}
console.log(response.statusCode) // 200
console.log(JSON.parse(data)) // { hello: 'world' }

server.close()
proxyServer.close()
client.close()

function buildServer () {
  return new Promise((resolve, reject) => {
    const server = createServer()
    server.listen(0, () => resolve(server))
  })
}

function buildProxy () {
  return new Promise((resolve, reject) => {
    const server = createProxy(createServer())
    server.listen(0, () => resolve(server))
  })
}

File: docs/docs/best-practices/writing-tests.md

Writing tests

Undici is tuned for a production use case and its default will keep
a socket open for a few seconds after an HTTP request is completed to
remove the overhead of opening up a new socket. These settings that makes
Undici shine in production are not a good fit for using Undici in automated
tests, as it will result in longer execution times.

The following are good defaults that will keep the socket open for only 10ms:

js
import { request, setGlobalDispatcher, Agent } from 'undici'

const agent = new Agent({
  keepAliveTimeout: 10, // milliseconds
  keepAliveMaxTimeout: 10 // milliseconds
})

setGlobalDispatcher(agent)

Guarding against unexpected disconnects

Undici's Client automatically reconnects after a socket error. This means
a test can silently disconnect, reconnect, and still pass. Unfortunately,
this could mask bugs like unexpected parser errors or protocol violations.
To catch these silent reconnections, add a disconnect guard after creating
a Client:

js
const { Client } = require('undici')
const { test, after } = require('node:test')
const { tspl } = require('@matteo.collina/tspl')

test('example with disconnect guard', async (t) => {
  t = tspl(t, { plan: 1 })

  const client = new Client('http://localhost:3000')
  after(() => client.close())

  client.on('disconnect', () => {
    if (!client.closed && !client.destroyed) {
      t.fail('unexpected disconnect')
    }
  })

  // ... test logic ...
})

client.close() and client.destroy() both emit 'disconnect' events, but
those are expected. The guard only fails when a disconnect happens during the
active test (i.e., !client.closed && !client.destroyed is true).

Skip the guard for tests where a disconnect is expected behavior, such as:

  • Signal aborts (signal.emit('abort'), ac.abort())
  • Server-side destruction (res.destroy(), req.socket.destroy())
  • Client-side body destruction mid-stream (data.body.destroy())
  • Timeout errors (HeadersTimeoutError, BodyTimeoutError)
  • Successful upgrades (the socket is detached from the Client)
  • Retry/reconnect tests where the disconnect triggers the retry
  • HTTP parser errors from malformed responses (HTTPParserError)

File: docs/docs/api/Agent.md

Agent

Stability: 2 - Stable

An Agent dispatches requests against multiple different origins, creating and
reusing a per-origin Pool (or Client) on demand. It is the default
dispatcher used by request, stream, and fetch when no explicit
dispatcher is supplied.

Requests are not guaranteed to be dispatched in their order of invocation.

mjs
import { Agent, setGlobalDispatcher } from 'undici'

const agent = new Agent({ connections: 10 })
setGlobalDispatcher(agent)

Class: `Agent`

  • Extends: {Dispatcher}

Agent keeps an internal map of origins to dispatchers. The first request to a
given origin lazily creates a dispatcher through the configured factory, and
subsequent requests to the same origin reuse it. Idle dispatchers are closed
automatically once they have no open connections and are no longer busy.

`new Agent([options])`

  • options {AgentOptions} (optional) Extends {PoolOptions}.
    • factory {Function} Builds the dispatcher used for a given origin. When the
      resolved options contain connections: 1, the default factory creates a
      {Client}; otherwise it creates a {Pool}. Default: (origin, opts) => new Pool(origin, opts).
      • origin {URL|string} The origin to create a dispatcher for.
      • opts {Object} The resolved options for the new dispatcher.
      • Returns: {Dispatcher}
    • maxOrigins {number} Limits the total number of distinct origins that may
      receive requests at the same time. A {MaxOriginsReachedError} is thrown when
      a request targets a new origin once the limit is reached. When set to
      Infinity, no limit is enforced. Must be a number greater than 0.
      Default: Infinity.

Agent inherits all {PoolOptions} (and therefore all {ClientOptions}). The
per-origin {Pool} it creates uses the default unlimited connections, so
concurrent requests to the same origin are spread across separate {Client}
instances on separate sockets.

[!NOTE]
Because each concurrent request to an origin may use a different {Client},
HTTP/2 multiplexing on a shared session does not apply unless connections is
set to a small value (for example connections: 1). See {PoolOptions} and
{ClientOptions} for the full set of inherited options such as allowH2
(default true) and maxConcurrentStreams (default 100).

`agent.closed`

  • Type: {boolean}

true after agent.close() has been called.

`agent.destroyed`

  • Type: {boolean}

true after agent.destroy() has been called, or after agent.close()
has been called and the shutdown has completed.

`agent.stats`

  • Type: {Record<string, ClientStats|PoolStats>}

Aggregate statistics for the agent, keyed by origin. Each value is the
ClientStats or PoolStats object reported by the dispatcher currently
serving that origin. Origins whose dispatcher does not expose stats are
omitted.

mjs
import { Agent } from 'undici'

const agent = new Agent()
await agent.request({ origin: 'http://localhost:3000', path: '/', method: 'GET' })
console.log(agent.stats)
// { 'http://localhost:3000': PoolStats { ... } }

`agent.dispatch(options, handler)`

  • options {AgentDispatchOptions} Extends {DispatchOptions}.
    • origin {string|URL} The origin to dispatch the request against. Required.
  • handler {DispatchHandler}
  • Returns: {boolean} false if the dispatcher is busy and the caller should
    wait for the 'drain' event before dispatching again.

Routes the request to the dispatcher for options.origin, creating one through
the factory if needed. Throws an {InvalidArgumentError} when options.origin
is not a non-empty string or {URL}, and a {MaxOriginsReachedError} when a new
origin would exceed maxOrigins. Implements Dispatcher.dispatch().

`agent.close([callback])`

  • callback {Function} (optional) Invoked once every per-origin dispatcher has
    closed. When omitted, a {Promise} is returned.
  • Returns: {Promise|void}

Gracefully closes the agent and every dispatcher it owns, waiting for in-flight
requests to complete. Implements Dispatcher.close().

`agent.destroy([error[, callback]])`

  • error {Error} (optional) The error to abort pending requests with.
  • callback {Function} (optional) Invoked once every per-origin dispatcher has
    been destroyed. When omitted, a {Promise} is returned.
  • Returns: {Promise|void}

Forcefully destroys the agent and every dispatcher it owns, aborting all pending
requests. Implements Dispatcher.destroy().

`agent.connect(options[, callback])`

See Dispatcher.connect(). The request is routed to the dispatcher for
options.origin.

`agent.pipeline(options, handler)`

See Dispatcher.pipeline(). The request is routed to the dispatcher for
options.origin.

`agent.request(options[, callback])`

See Dispatcher.request(). The request is routed to the dispatcher for
options.origin.

mjs
import { Agent } from 'undici'

const agent = new Agent()
const { statusCode, body } = await agent.request({
  origin: 'http://localhost:3000',
  path: '/',
  method: 'GET',
})

console.log('response received', statusCode)
console.log('data', await body.text())

`agent.stream(options, factory[, callback])`

See Dispatcher.stream(). The request is routed to the dispatcher for
options.origin.

`agent.upgrade(options[, callback])`

See Dispatcher.upgrade(). The request is routed to the dispatcher for
options.origin.

Event: `'connect'`

  • origin {URL}
  • targets {Array} The dispatchers involved, starting with this
    Agent followed by the per-origin dispatcher chain.

Emitted when a socket has been created for an origin and is ready to receive
requests. Forwarded from the underlying dispatcher's 'connect' event.

Event: `'disconnect'`

  • origin {URL}
  • targets {Array} The dispatchers involved, starting with this
    Agent.
  • error {Error}

Emitted when a socket for an origin has disconnected. Forwarded from the
underlying dispatcher's 'disconnect' event.

Event: `'connectionError'`

  • origin {URL}
  • targets {Array} The dispatchers involved, starting with this
    Agent.
  • error {Error}

Emitted when a connection attempt for an origin fails. Forwarded from the
underlying dispatcher's 'connectionError' event.

Event: `'drain'`

  • origin {URL}
  • targets {Array} The dispatchers involved, starting with this
    Agent.

Emitted when an origin's dispatcher is no longer busy and can accept more
requests. Forwarded from the underlying dispatcher's 'drain' event.


File: docs/docs/api/api-lifecycle.md

Client Lifecycle

A {Client} can be understood as a state machine. As requests are dispatched,
processed, and drained, the client moves through a well-defined set of states
until it is eventually destroyed. This document describes those states and the
transitions between them.

The {Client} class is not literally implemented as a state machine, so the
actual execution may deviate slightly from what is described here. Treat this
guide as a conceptual model for reasoning about a client's behavior rather than
as a formal specification.

State transition overview

  • A {Client} begins in the idle state with no socket connection and no
    queued requests.
    • The connect transition moves the {Client} to the pending state, where
      requests can be queued before they are processed.
    • Calling client.close() or client.destroy() moves the {Client}
      to the destroyed state. Because there are no queued requests in this
      state, close transitions straight to destroyed.
  • The pending state indicates that the underlying socket connection has been
    established and that requests are queueing.
    • The process transition moves the {Client} to the processing state,
      where requests are processed.
    • If requests are queued, close transitions to processing; otherwise it
      transitions to destroyed.
    • The destroy transition moves the {Client} to destroyed.
  • The processing state initializes to the processing.running sub-state.
    • If the current request body requires draining, needDrain moves the
      {Client} into the processing.busy sub-state, which returns to
      processing.running once drainComplete fires.
    • After all queued requests complete, keepalive moves the {Client} back to
      the pending state. If no requests are queued before the socket times
      out, the {Client} transitions to idle.
    • If close is fired while the {Client} still has queued requests, the
      {Client} transitions to processing.closing, where it completes all
      outstanding requests before firing done.
    • The done transition moves the {Client} gracefully to destroyed.
    • At any time, destroy moves the {Client} from processing to
      destroyed, aborting any queued requests.
  • The destroyed state is final; the {Client} is no longer functional.

A state diagram representing a {Client} instance:

mermaid
stateDiagram-v2
  [*] --> idle
  idle --> pending : connect
  idle --> destroyed : destroy/close

  pending --> idle : timeout
  pending --> destroyed : destroy

  state close_fork <<fork>>
  pending --> close_fork : close
  close_fork --> processing
  close_fork --> destroyed

  pending --> processing : process

  processing --> pending : keepalive
  processing --> destroyed : done
  processing --> destroyed : destroy

  destroyed --> [*]

  state processing {
      [*] --> running
      running --> closing : close
      running --> busy : needDrain
      busy --> running : drainComplete
      running --> [*] : keepalive
      closing --> [*] : done
  }

State details

idle

idle is the initial state of a {Client} instance. Although an origin is
required to construct a {Client}, the underlying socket connection is not
established until a request is queued through client.dispatch(). Calling
client.dispatch() directly, or through one of its higher-level wrappers
(client.connect(), client.pipeline(), client.request(),
client.stream(), or client.upgrade()), moves the {Client} from
idle to pending, and then usually directly on to processing.

Calling client.close() or client.destroy() in this state moves the
{Client} straight to destroyed, since there are no queued requests.

pending

pending signifies a connected but non-processing {Client}. On entering this
state, the {Client} establishes a socket connection and emits the
'connect' event, signalling that a connection to the origin supplied at
construction time has been established. The internal queue starts empty, and
requests can begin queueing.

Calling client.close() with queued requests moves the {Client} to the
processing state; without queued requests it moves to destroyed.

Calling client.destroy() moves the {Client} directly to destroyed,
regardless of any queued requests.

processing

processing is itself a sub-state machine that initializes to the
processing.running sub-state. client.dispatch(), client.close(),
and client.destroy() can all be called while the {Client} is in this
state. client.dispatch() queues additional requests while existing ones
continue to be processed, client.close() moves the {Client} to the
processing.closing sub-state, and client.destroy() moves it to
destroyed.

running

In processing.running, queued requests are processed in FIFO order. If a
request body requires draining, needDrain moves the {Client} to the
processing.busy sub-state. The close transition moves the {Client} to the
processing.closing sub-state. When all queued requests have completed and
neither client.close() nor client.destroy() has been called, the
keepalive transition moves the {Client} back to the pending state. There
the {Client} waits for the socket connection to time out; once it does, the
{Client} returns to the idle state.

busy

This sub-state is entered only when a request body is a {stream.Readable} that
requires draining. The {Client} cannot process additional requests while in
this state and waits until the current request body has been fully drained
before returning to processing.running.

closing

This sub-state is entered only when a {Client} has queued requests and
client.close() is called. The {Client} continues to process requests as
usual, except that no additional requests may be queued. Once all queued
requests have been processed, done fires and the {Client} moves gracefully to
destroyed without an error.

destroyed

destroyed is the final state of a {Client} instance. Once in this state, the
{Client} is no longer functional, and calling any further {Client} method
rejects or fails with a {ClientDestroyedError}.


File: docs/docs/api/BalancedPool.md

BalancedPool

Stability: 2 - Stable

A BalancedPool distributes requests across a set of {Pool} instances, each
connected to a different upstream. It uses a weighted round-robin algorithm:
every upstream starts with the same weight, and the weight is adjusted up on
successful connections and down on connection errors, so that healthy upstreams
receive a larger share of traffic.

Requests are not guaranteed to be dispatched in the order they are issued.

mjs
import { BalancedPool } from 'undici'

const pool = new BalancedPool([
  'http://localhost:3000',
  'http://localhost:3001',
  'http://localhost:3002',
])

const { statusCode, body } = await pool.request({
  path: '/',
  method: 'GET',
})
console.log('response received', statusCode)
for await (const chunk of body) {
  console.log('data', chunk.toString())
}

Class: `BalancedPool`

  • Extends: {Dispatcher}

A pool of {Pool} instances connected to multiple upstreams.

`new BalancedPool(upstreams[, options])`

  • upstreams {URL|string|URL[]|string[]} One or more upstream origins. Each
    value should only include the protocol, hostname, and port. Default:
    [].
  • options {BalancedPoolOptions} (optional)

The options passed to the constructor are forwarded to every {Pool} instance
that is created for an upstream.

Parameter: `BalancedPoolOptions`

Extends: {PoolOptions}

In addition to all {PoolOptions}, the following options are supported:

  • factory {Function} A function used to create the {Pool} instance for each
    upstream. Default: (origin, opts) => new Pool(origin, opts).
    • origin {URL|string} The upstream origin.
    • opts {Object} The options object derived from the constructor options.
    • Returns: {Dispatcher}
  • maxWeightPerServer {number} The maximum weight any single upstream can
    reach. Successful connections increase an upstream's weight up to this value.
    Default: 100.
  • errorPenalty {number} The amount by which an upstream's weight is decreased
    on a connection error and increased on a successful connection. Default:
    15.

`balancedPool.addUpstream(upstream)`

  • upstream {URL|string} The upstream origin to add. It should only include the
    protocol, hostname, and port.
  • Returns: {BalancedPool} The BalancedPool instance, for chaining.

Adds an upstream. If an open, non-destroyed upstream with the same origin is
already present, the call is a no-op.

`balancedPool.removeUpstream(upstream)`

  • upstream {URL|string} The upstream origin to remove. It should only include
    the protocol, hostname, and port.
  • Returns: {BalancedPool} The BalancedPool instance, for chaining.

Removes an upstream that was previously added. If no matching upstream is found,
the call is a no-op.

`balancedPool.getUpstream(upstream)`

  • upstream {URL|string} The upstream origin to look up. It should only include
    the protocol, hostname, and port.
  • Returns: {Pool|undefined} The {Pool} instance managing the given upstream, or
    undefined if it is not present.

Returns the {Pool} instance for the given upstream origin, if it is open and not
destroyed.

`balancedPool.upstreams`

  • Type: {string[]} The origins of the upstreams that are currently open and not
    destroyed.

`balancedPool.closed`

  • Type: {boolean} true after balancedPool.close() has been called.

Implements [client.closed][].

`balancedPool.destroyed`

  • Type: {boolean} true after balancedPool.destroy() has been called, or after
    balancedPool.close() has been called and the shutdown has completed.

Implements [client.destroyed][].

`balancedPool.stats`

  • Type: {PoolStats}

Returns a {PoolStats} instance aggregating the statistics of the underlying
pools.

`balancedPool.close([callback])`

Implements [Dispatcher.close([callback])][].

`balancedPool.destroy([error[, callback]])`

Implements [Dispatcher.destroy([error, callback])][].

`balancedPool.connect(options[, callback])`

See [Dispatcher.connect(options[, callback])][].

`balancedPool.dispatch(options, handlers)`

Implements [Dispatcher.dispatch(options, handler)][].

`balancedPool.pipeline(options, handler)`

See [Dispatcher.pipeline(options, handler)][].

`balancedPool.request(options[, callback])`

See [Dispatcher.request(options[, callback])][].

`balancedPool.stream(options, factory[, callback])`

See [Dispatcher.stream(options, factory[, callback])][].

`balancedPool.upgrade(options[, callback])`

See [Dispatcher.upgrade(options[, callback])][].

Event: `'connect'`

See Dispatcher Event: 'connect'.

Event: `'disconnect'`

See Dispatcher Event: 'disconnect'.

Event: `'drain'`

See Dispatcher Event: 'drain'.

[Dispatcher.close([callback])]: Dispatcher.md#dispatcherclosecallback-promise
[Dispatcher.connect(options[, callback])]: Dispatcher.md#dispatcherconnectoptions-callback
[Dispatcher.destroy([error, callback])]: Dispatcher.md#dispatcherdestroyerror-callback-promise
[Dispatcher.dispatch(options, handler)]: Dispatcher.md#dispatcherdispatchoptions-handler
[Dispatcher.pipeline(options, handler)]: Dispatcher.md#dispatcherpipelineoptions-handler
[Dispatcher.request(options[, callback])]: Dispatcher.md#dispatcherrequestoptions-callback
[Dispatcher.stream(options, factory[, callback])]: Dispatcher.md#dispatcherstreamoptions-factory-callback
[Dispatcher.upgrade(options[, callback])]: Dispatcher.md#dispatcherupgradeoptions-callback
[client.closed]: Client.md#clientclosed
[client.destroyed]: Client.md#clientdestroyed


File: docs/docs/api/CacheStorage.md

CacheStorage

Stability: 2 - Stable

undici provides a spec-compliant implementation of the W3C Service Worker
CacheStorage
and Cache interfaces. A CacheStorage is a named
collection of Cache objects, each of which stores Request/Response
pairs.

The class itself is not exported; instead, undici exposes a single shared
CacheStorage instance named caches. Import it from 'undici':

mjs
import { caches } from 'undici'

const cache = await caches.open('v1')
cjs
const { caches } = require('undici')

caches.open('v1').then((cache) => {
  // ...
})

`caches`

  • Type: {CacheStorage}

The shared CacheStorage instance exported by undici.
All caches opened through caches live for the lifetime of the process.

mjs
import { caches } from 'undici'

await caches.open('v1')
console.log(await caches.keys()) // ['v1']

Class: `CacheStorage`

Represents the storage for Cache objects. Each CacheStorage keeps a
mapping of cache names to their backing list of cached responses.

The constructor is not public: attempting to instantiate CacheStorage
directly throws a TypeError. Use the exported caches instance
instead.

`caches.match(request[, options])`

  • request {Request|string} The request to match against. A string is treated
    as a URL.
  • options {Object} (optional)
    • ignoreSearch {boolean} When true, ignores the query string of the URL
      when matching. Default: false.
    • ignoreMethod {boolean} When true, prevents matching operations from
      validating the Request http method. Default: false.
    • ignoreVary {boolean} When true, ignores the Vary header when matching.
      Default: false.
    • cacheName {string} When set, restricts the search to the cache with this
      name. Default: undefined.
  • Returns: {Promise} Fulfills with the first matching {Response}, or
    undefined if no response matched.

Searches the caches for a response matching request. When options.cacheName
is provided, only that cache is searched; otherwise every cache is searched in
insertion order and the first match is returned.

mjs
import { caches } from 'undici'

const response = await caches.match('https://example.com/data', {
  cacheName: 'v1'
})

`caches.has(cacheName)`

  • cacheName {string} The name of the cache to look up.
  • Returns: {Promise} Fulfills with true if a cache named cacheName exists,
    otherwise false.

Checks whether a cache with the given name exists in the storage.

mjs
import { caches } from 'undici'

await caches.open('v1')
console.log(await caches.has('v1')) // true
console.log(await caches.has('v2')) // false

`caches.open(cacheName)`

  • cacheName {string} The name of the cache to open.
  • Returns: {Promise} Fulfills with the {Cache} named cacheName.

Opens the cache named cacheName, creating it if it does not already exist.

Each call returns a new Cache object, but caches opened with the same name
share their underlying list of cached responses. As a result, a response stored
through one instance is visible through another instance with the same name.

mjs
import { caches } from 'undici'
import assert from 'node:assert'

const cache1 = await caches.open('v1')
const cache2 = await caches.open('v1')

// open() returns distinct objects,
assert(cache1 !== cache2)
// but they share the same cached responses.
assert.deepStrictEqual(await cache1.match('/req'), await cache2.match('/req'))

`caches.delete(cacheName)`

  • cacheName {string} The name of the cache to delete.
  • Returns: {Promise} Fulfills with true if the cache existed and was deleted,
    otherwise false.

Removes the cache named cacheName from the storage. Any Cache objects
already obtained for that name, along with the Request/Response pairs they
returned, remain usable after deletion.

mjs
import { caches } from 'undici'

const cache = await caches.open('v1')
const response = await cache.match('/req')

await caches.delete('v1')

// The already-retrieved response is still usable.
await response.text()

`caches.keys()`

  • Returns: {Promise} Fulfills with a {string[]} of cache names in insertion
    order.

Returns the names of all caches currently held by the storage.

mjs
import { caches } from 'undici'

await caches.open('v1')
await caches.open('v2')
console.log(await caches.keys()) // ['v1', 'v2']

File: docs/docs/api/CacheStore.md

Cache Store

Stability: 2 - Stable

A cache store is the storage backend used by the cache interceptor to persist
and retrieve cached responses. A store decides which stored response to serve
for a request by comparing the request against the response's Vary header, and
is expected to be compliant with RFC 9111.

undici ships two stores. MemoryCacheStore keeps responses in memory, while
SqliteCacheStore persists them to a SQLite database. Both are available under
the cacheStores export:

mjs
import { cacheStores } from 'undici'

const { MemoryCacheStore, SqliteCacheStore } = cacheStores

SqliteCacheStore requires the node:sqlite API. The class is always
exported, but constructing one throws if node:sqlite is not available in the
running Node.js version.

A cache store implements three methods โ€” get(key), createWriteStream(key, value), and delete(key) โ€” that operate on a CacheKey and a
CacheValue. Any object implementing this contract can be passed
to the cache interceptor, so custom stores (for example, backed by Redis or a
remote service) are supported as well.

Class: `MemoryCacheStore`

  • Extends: {EventEmitter}

Stores cached responses in memory. The store enforces upper bounds on the total
number of responses, the total size of all responses, and the size of any single
response. When a limit is exceeded, the store evicts approximately half of its
entries and emits a 'maxSizeExceeded' event.

mjs
import { interceptors, cacheStores, Agent, setGlobalDispatcher } from 'undici'

const store = new cacheStores.MemoryCacheStore({ maxSize: 50 * 1024 * 1024 })

setGlobalDispatcher(
  new Agent().compose(interceptors.cache({ store }))
)

`new MemoryCacheStore([options])`

  • options {Object} (optional)
    • maxCount {number} The maximum number of responses to store. Default:
      1024.
    • maxSize {number} The maximum total size, in bytes, of all stored
      responses. Default: 104857600 (100 MiB).
    • maxEntrySize {number} The maximum size, in bytes, of a single response
      body. Responses whose body exceeds this value are not cached. Default:
      5242880 (5 MiB).
    • errorCallback {Function} A callback invoked with any error raised by the
      store. (optional)
      • err {Error}

Each of maxCount, maxSize, and maxEntrySize must be a non-negative
integer; a TypeError is thrown otherwise.

`memoryCacheStore.size`

  • Type: {number}

The current total size, in bytes, of all stored response bodies.

`memoryCacheStore.isFull()`

  • Returns: {boolean} true when the store has reached its maxSize or
    maxCount limit, false otherwise.

`memoryCacheStore.get(key)`

  • key {CacheKey} The request to look up. See CacheKey.
  • Returns: {GetResult|undefined} The matching cached response, or undefined
    when there is no fresh entry for key. See GetResult.

Looks up a cached response for key. A stored entry matches only when its
method equals key.method, its deleteAt time is in the future, and every
header listed in its vary map matches the corresponding header in
key.headers.

`memoryCacheStore.createWriteStream(key, value)`

  • key {CacheKey} The request the response is being cached for. See
    CacheKey.
  • value {CacheValue} The response metadata to store. See
    CacheValue.
  • Returns: {Writable|undefined} A writable stream that the response body is
    written to, or undefined when the response cannot be cached.

Returns a {Writable} stream used to write the response body into the store. When
the stream finishes, the entry is committed; if its accumulated size exceeds
maxEntrySize, the stream is destroyed and nothing is stored. Writing a new
entry whose key matches an existing one replaces that entry.

`memoryCacheStore.delete(key)`

  • key {CacheKey} The request whose cached responses should be removed. See
    CacheKey.
  • Returns: {undefined}

Removes every cached response stored for key.origin and key.path. Throws a
TypeError if key is not an object.

Event: `'maxSizeExceeded'`

  • data {Object}
    • size {number} The current total size, in bytes, of all stored responses.
    • maxSize {number} The configured maxSize limit.
    • count {number} The current number of stored responses.
    • maxCount {number} The configured maxCount limit.

Emitted when the store exceeds its maxSize or maxCount limit, immediately
before entries are evicted. The event fires only once per overflow; it is not
emitted again until the store drops back below both limits.

Class: `SqliteCacheStore`

Stores cached responses in a SQLite database using the node:sqlite API.
The constructor throws when node:sqlite is not available.

mjs
import { interceptors, cacheStores, Agent, setGlobalDispatcher } from 'undici'

const store = new cacheStores.SqliteCacheStore({ location: './cache.db' })

setGlobalDispatcher(
  new Agent().compose(interceptors.cache({ store }))
)

`new SqliteCacheStore([options])`

  • options {Object} (optional)
    • location {string} The location of the SQLite database. Use ':memory:'
      for an in-memory database. Default: ':memory:'.
    • maxCount {number} The maximum number of responses to store. Default:
      Infinity.
    • maxEntrySize {number} The maximum size, in bytes, of a single response
      body. Responses whose body exceeds this value are not cached. Must not
      exceed 2000000000 (2 GB). Default: 2000000000 (2 GB).

maxCount and maxEntrySize must be non-negative integers; a TypeError is
thrown otherwise, or if maxEntrySize is greater than 2 GB.

`sqliteCacheStore.close()`

  • Returns: {undefined}

Closes the connection to the underlying SQLite database.

`sqliteCacheStore.size`

  • Type: {number}

The number of responses currently stored in the database.

`sqliteCacheStore.get(key)`

  • key {CacheKey} The request to look up. See CacheKey.
  • Returns: {GetResult|undefined} The matching cached response, or undefined
    when there is no fresh entry for key. The returned body is a {Buffer}.
    See GetResult.

Looks up a cached response for key, comparing the request method and every
header named in the stored vary map.

`sqliteCacheStore.set(key, value)`

  • key {CacheKey} The request the response is being cached for. See
    CacheKey.
  • value {CacheValue} The response to store, with its body set to a {Buffer},
    an {Array} of {Buffer}, or null. See CacheValue.
  • Returns: {undefined}

Writes a response into the database directly, without going through a stream. If
the body exceeds maxEntrySize, nothing is stored. When an entry already exists
for key it is overwritten; otherwise a new row is inserted and old entries are
pruned to honour maxCount. This method is used internally by
sqliteCacheStore.createWriteStream().

`sqliteCacheStore.createWriteStream(key, value)`

  • key {CacheKey} The request the response is being cached for. See
    CacheKey.
  • value {CacheValue} The response metadata to store. See
    CacheValue.
  • Returns: {Writable|undefined} A writable stream that the response body is
    written to, or undefined when the response cannot be cached.

Returns a {Writable} stream used to write the response body into the store. When
the stream finishes, the buffered body is committed via
sqliteCacheStore.set(). If the body exceeds
maxEntrySize, the stream is destroyed and nothing is stored.

`sqliteCacheStore.delete(key)`

  • key {CacheKey} The request whose cached responses should be removed. See
    CacheKey.
  • Returns: {undefined}

Removes every cached response stored for the URL derived from key.origin and
key.path. Throws a TypeError if key is not an object.

Implementing a custom cache store

A cache store is any object that implements the CacheStore interface:

  • get(key) โ€” Look up a response. Returns a GetResult,
    undefined, or a {Promise} resolving to either.
  • createWriteStream(key, value) โ€” Return a {Writable} to receive the response
    body, or undefined if the response cannot be stored.
  • delete(key) โ€” Remove all responses for key. May return a {Promise}.

Returning a {Promise} from get or delete lets a store be backed by an
asynchronous resource; the cache interceptor awaits these values, including on
the revalidation and stale-while-revalidate paths.

`CacheKey`

The request a response is being looked up or stored for.

  • origin {string} The request origin.
  • method {string} The request method.
  • path {string} The request path.
  • headers {Record<string, string|string[]>} The request headers, used to
    satisfy Vary. (optional)

`CacheValue`

The metadata of a cached response, excluding its body.

  • statusCode {number} The response's HTTP status code.
  • statusMessage {string} The response's HTTP status message.
  • headers {Record<string, string|string[]>} The response headers.
  • vary {Record<string, string|string[]|null>} The header names listed in the
    response's Vary header mapped to the values they had in the original
    request. A value is null when that header was absent from the original
    request. (optional)
  • etag {string} The response's entity tag. (optional)
  • cacheControlDirectives {Object} The parsed Cache-Control directives of the
    response. (optional)
  • cachedAt {number} The time, in milliseconds, at which the response was
    cached.
  • staleAt {number} The time, in milliseconds, at which the response becomes
    stale.
  • deleteAt {number} The time, in milliseconds, at which the response must be
    evicted. A store must not return a response once this time has passed.

The vary map drives response selection. For a response such as:

http
Vary: content-encoding, accept
content-encoding: utf8
accept: application/json

the recorded map is:

js
{
  'content-encoding': 'utf8',
  accept: 'application/json'
}

If the original request did not include the accept header, its value is
recorded as null:

js
{
  'content-encoding': 'utf8',
  accept: null
}

`GetResult`

The value returned by get. It contains every field of
CacheValue plus the response body.

  • body {Readable|Iterable|AsyncIterable|Buffer|string} The cached response
    body. (optional)

2. Official Technical Reference & Guides (nodejs/docs)

docs

Hi there! This repo serves as a central place for Node.js documentation
coordination, but not documentation itself. The documentation for node can be
found at http://nodejs.org/en/docs/. The source material is found in the
node core repo.

Current Documentation WG Members