API reference

fetch & HTTP client

3/3Coverage1/3Strict (zero caveats)

The WHATWG fetch client — fetch(), Response, Request, Headers — over libcurl, non-blocking under the event loop. The response body accessors (.text()/.json()/.arrayBuffer()) return a real Promise<T> you await — a genuine thenable, so .then() and Promise.all() work on them. The body accessors settle off the transfer's completion (ADR-00823), so a .json() parse error is a promise rejection and the download overlaps intervening work. WebSocket, EventSource, and XMLHttpRequest are separate Networking surfaces, documented elsewhere.

Exact Works, with caveats Not supported

fetch(url, init?)

Works, with caveats
fetch(url: string | Request, init?: RequestInit): Promise<Response>

Issues an HTTP request and resolves when the response headers arrive; the body is read lazily. Genuinely non-blocking — awaited inside an http.listen handler it yields to the event loop, so a slow upstream call doesn't stall other connections. A network-level failure (DNS, connection refused, TLS, timeout) throws; a non-2xx HTTP status still resolves normally — .ok distinguishes it, matching real fetch.

Parameters
urlstring | Request
The target URL, or a Request object as the sole argument.
init?RequestInit
A RequestInit: method, headers (any HeadersInit) and body (any BodyInit — a string, URLSearchParams, Blob, ArrayBuffer or TypedArray/Buffer, sent with the Content-Type undici implies), signal.
Returns

Promise<Response> — Resolves once response headers arrive.

const r = await fetch('http://127.0.0.1:8765/get')
console.log(r.status, r.ok)   // 200 true
See it in a full example →
⚠ Differences from standard
  • A transport failure other than a refused connection, a failed lookup or a bad port (TLS, timeout, reset) has an Error with libcurl's message as its cause, where undici gives its own error and code.

Response.status / .ok

Exact
readonly status: number; readonly ok: boolean

The HTTP status code, and whether it is in the 2xx range. A non-2xx response still resolves — check .ok rather than relying on fetch to throw.

Returns

number | boolean — The status code / the 2xx flag.

const missing = await fetch('http://127.0.0.1:8765/status/404')
console.log(missing.status, missing.ok)   // 404 false
See it in a full example →

Response.text()

Works, with caveats
text(): Promise<string>

Reads the body to completion and resolves to it as a string. Returns a real Promise<string> — await it, or chain .then().

Returns

Promise<string> — Resolves to the response body as text.

const r = await fetch('http://127.0.0.1:8765/get')
const body: string = await r.text()
See it in a full example →
⚠ Differences from standard
  • The result is a null-terminated string: a binary body with an embedded null byte truncates at the first one — use .arrayBuffer() for binary.

Response.json()

Works, with caveats
json(): Promise<T>

Reads and parses the body as JSON, resolving to a value projected into a declared type. Returns a real Promise<T> — await it, or chain .then(). Reuses JSON.parse's machinery — validates the body (a non-JSON body throws a catchable SyntaxError) and projects nested objects, array/object-array fields, and top-level T[].

Returns

Promise<T> — Resolves to the parsed value, projected to the annotated type.

interface Ip { origin: string }
const info: Ip = await (await fetch('http://127.0.0.1:8765/ip')).json()
See it in a full example →
⚠ Differences from standard
  • A JSON parse error rejects the promise (reachable via .then's onRejected / .catch); the body settles off the transfer's completion (ADR-00823).
  • A typed declaration const p: T = await r.json() parses straight into T (the JSON.parse-style projection carve-out).
  • Only any/unknown dynamic shapes are unsupported; every statically-typed shape projects.
  • Same embedded-null truncation as .text() — the body is read as a null-terminated string.

Response.arrayBuffer()

Works, with caveats
arrayBuffer(): Promise<ArrayBuffer>

Reads the body to completion and resolves to an ArrayBuffer, carrying the real byte count and coming back whole regardless of content. The binary-safe body accessor — use it for any body that may contain null bytes. Returns a real Promise<ArrayBuffer> — await it, or chain .then().

Returns

Promise<ArrayBuffer> — Resolves to the complete response body as bytes.

const binary = await fetch('http://127.0.0.1:8765/bytes/16')
const bytes = new Uint8Array(await binary.arrayBuffer())

Response.body

Works, with caveats
readonly body: ReadableStream<Uint8Array> | null; readonly bodyUsed: boolean

A real ReadableStream<Uint8Array> fed incrementally from libcurl's write callback with backpressure via curl pause/unpause. Every read is the same stream; a constructed Response without a body has a null body. bodyUsed turns true once a body method runs or the stream is read, and a body method on a used or locked body rejects with a TypeError ("Body is unusable"), as in Node.

Returns

ReadableStream<Uint8Array> | null — The streaming body.

new Response(body, init) / Response.json() / .redirect() / .error()

Works, with caveats
new Response(body?: string | Uint8Array | ArrayBuffer | Blob | URLSearchParams | ReadableStream<Uint8Array> | null, init?: { status?: number, statusText?: string, headers?: HeadersInit }); Response.json(data, init?); Response.redirect(url, status?); Response.error()

Builds a Response without a request: the body with the content type it implies (text/plain;charset=UTF-8 for a string, a Blob's own type) unless the headers set one, init's status, statusText and headers. Response.json serializes data as application/json, Response.redirect sets Location from the parsed URL, Response.error() is status 0 with type 'error'. text(), json(), arrayBuffer() and .body read it as they read a fetched Response; a ReadableStream body is .body itself, read to its end by a body method, and no body makes .body null.

Returns

Response — A Response with the given body and init.

const res = new Response('hello', { status: 201, headers: { 'X-Id': '7' } })
console.log(res.status, res.headers.get('content-type'), await res.text())
const j = Response.json({ ok: true })
⚠ Differences from standard
  • A stream body must be a ReadableStream<Uint8Array>: a stream of any chunks is rejected at compile time.

Response headers

Not supported
// not available

Reading response headers is not supported — no HEADERFUNCTION capture exists in this codebase yet. There is no .headers accessor on a Response, and XMLHttpRequest's .getResponseHeader()/.getAllResponseHeaders() are absent for the same reason. Request-side headers work fully.

new Request(url, init?)

Works, with caveats
new Request(url: string, init?: RequestInit): Request

A real Request class bundling a URL with method/headers/body: .url/.method/.headers, and Fetch's Body members (.body as a ReadableStream, bodyUsed, text()/json()/arrayBuffer()). method defaults to "GET" (standard methods case-normalized), headers to an empty Headers, body to null; a string body implies text/plain;charset=UTF-8, and a GET/HEAD request with a body is a TypeError. Works as fetch's sole argument, or supply a plain-object init to fetch directly.

Parameters
urlstring
The request URL.
init?RequestInit
method / headers / body.
Returns

Request — The request object.

const req = new Request('http://127.0.0.1:8765/post', {
  method: 'POST', headers: headers, body: JSON.stringify({ hello: 'world' })
})
const posted = await fetch(req)
See it in a full example →

new Headers()

Exact
new Headers(init?: HeadersInit): Headers

The Fetch standard's header list, as Node's undici has it: case-insensitive names that console.log shows as given, validated names and values, iteration sorted by name with values combined and set-cookie values listed apart (getSetCookie()). Accepted anywhere fetch, Request or Response take headers; it can be subclassed.

Returns

Headers — A header list, filled from init (a Headers, [name, value] pairs or a record).

const h = new Headers({ 'Content-Type': 'text/plain' })
h.append('Set-Cookie', 'a=1')
h.append('Set-Cookie', 'b=2')
console.log(h.getSetCookie())   // [ 'a=1', 'b=2' ]
See it in a full example →

Headers.append(name, value)

Exact
append(name: string, value: string): void

Adds a header value, combining with a comma if name is already present instead of overwriting — the one behavior that distinguishes it from .set().

Parameters
namestring
Header name (case-insensitive).
valuestring
Value to add.
Returns

void — Nothing.

headers.append('X-Trace', 'a')
headers.append('X-Trace', 'b')
console.log(headers.get('x-trace'))   // a, b
See it in a full example →