fetch & HTTP client
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.
fetch(url, init?)
Works, with caveatsIssues 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.
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- 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
ExactThe 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.
Returnsnumber | 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 falseResponse.text()
Works, with caveatsReads the body to completion and resolves to it as a string. Returns a real Promise<string> — await it, or chain .then().
ReturnsPromise<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()- 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 caveatsReads 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[].
ReturnsPromise<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()- 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 intoT(theJSON.parse-style projection carve-out). - Only
any/unknowndynamic 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 caveatsReads 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().
ReturnsPromise<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 caveatsA 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.
ReturnsReadableStream<Uint8Array> | null — The streaming body.
new Response(body, init) / Response.json() / .redirect() / .error()
Works, with caveatsBuilds 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.
ReturnsResponse — 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 })- A stream body must be a
ReadableStream<Uint8Array>: a stream ofanychunks is rejected at compile time.
Response headers
Not supportedReading 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 caveatsA 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.
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)new Headers()
ExactThe 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.
ReturnsHeaders — 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' ]Headers.append(name, value)
ExactAdds 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.
void — Nothing.
headers.append('X-Trace', 'a')
headers.append('X-Trace', 'b')
console.log(headers.get('x-trace')) // a, b