Bun implements the WHATWG fetch standard with extensions tailored for server-side use. The fetch global is available everywhere without imports.
GET request
const response = await fetch ( "https://example.com" );
console . log ( response . status ); // 200
console . log ( response . headers . get ( "content-type" )); // text/html; charset=UTF-8
const text = await response . text ();
Common HTTP methods
const res = await fetch ( "https://api.example.com/users" );
const users = await res . json ();
Pass headers as a plain object or a Headers instance:
Plain object
Headers instance
const res = await fetch ( "https://api.example.com/data" , {
headers: {
Authorization: "Bearer my-token" ,
"Accept-Language" : "en-US" ,
},
});
Reading response bodies
Method Returns response.text()Promise<string>response.json()Promise<any>response.bytes()Promise<Uint8Array>response.arrayBuffer()Promise<ArrayBuffer>response.blob()Promise<Blob>response.formData()Promise<FormData>
const res = await fetch ( "https://api.example.com/data" );
// Read as JSON
const json = await res . json ();
// Read as text
const text = await res . text ();
// Write directly to a file
await Bun . write ( "output.json" , res );
Streaming response bodies
Use response.body (a ReadableStream) or an async iterator to process large responses without buffering them entirely in memory:
Async iterator
ReadableStream reader
const res = await fetch ( "https://example.com/large-file" );
for await ( const chunk of res . body ) {
process . stdout . write ( chunk );
}
Streaming request bodies
Send a ReadableStream as a request body. Bun streams it directly to the network without buffering the entire body in memory:
const stream = new ReadableStream ({
start ( controller ) {
controller . enqueue ( "Hello" );
controller . enqueue ( ", " );
controller . enqueue ( "world!" );
controller . close ();
},
});
const res = await fetch ( "https://api.example.com/upload" , {
method: "POST" ,
body: stream ,
headers: { "Content-Type" : "text/plain" },
});
const form = new FormData ();
form . append ( "name" , "Alice" );
form . append ( "avatar" , Bun . file ( "./avatar.png" ), "avatar.png" );
const res = await fetch ( "https://api.example.com/profile" , {
method: "POST" ,
body: form ,
// Content-Type with multipart boundary is set automatically
});
Timeouts and cancellation
Timeout with AbortSignal.timeout()
try {
const res = await fetch ( "https://slow.example.com" , {
signal: AbortSignal . timeout ( 5000 ), // abort after 5 seconds
});
const text = await res . text ();
} catch ( err ) {
if ( err . name === "TimeoutError" ) {
console . error ( "Request timed out" );
}
}
Manual cancellation with AbortController
const controller = new AbortController ();
// Cancel after 3 seconds
setTimeout (() => controller . abort (), 3000 );
const res = await fetch ( "https://api.example.com/stream" , {
signal: controller . signal ,
});
Proxy support
Route requests through an HTTP or HTTPS proxy:
// Simple proxy
const res = await fetch ( "https://api.example.com/data" , {
proxy: "http://proxy.internal:8080" ,
});
Send custom headers to the proxy (for example, Proxy-Authorization):
const res = await fetch ( "https://api.example.com/data" , {
proxy: {
url: "http://proxy.internal:8080" ,
headers: {
"Proxy-Authorization" : "Bearer proxy-token" ,
},
},
});
You cannot use proxy and unix together in the same request.
Unix socket support
Fetch from a server listening on a Unix domain socket:
const res = await fetch ( "http://localhost/api/status" , {
unix: "/var/run/my-app.sock" ,
});
const data = await res . json ();
TLS options
Use a client certificate for mutual TLS authentication:
const res = await fetch ( "https://secure.example.com" , {
tls: {
key: Bun . file ( "/path/to/client.key" ),
cert: Bun . file ( "/path/to/client.crt" ),
ca: Bun . file ( "/path/to/ca.crt" ),
},
});
Disable certificate verification (development only):
const res = await fetch ( "https://self-signed.example.com" , {
tls: {
rejectUnauthorized: false ,
},
});
Setting rejectUnauthorized: false disables TLS validation entirely. Never use this in production.
Custom server identity check:
const res = await fetch ( "https://example.com" , {
tls: {
checkServerIdentity ( hostname , cert ) {
if ( hostname !== "expected.example.com" ) {
return new Error ( "Hostname mismatch" );
}
},
},
});
Additional protocols
Beyond HTTP and HTTPS, Bun’s fetch supports several other URL schemes:
// Uses AWS_ACCESS_KEY_ID / AWS_SECRET_ACCESS_KEY / AWS_REGION env vars
const res = await fetch ( "s3://my-bucket/path/to/object" );
// Or pass credentials explicitly
const res2 = await fetch ( "s3://my-bucket/path/to/object" , {
s3: {
accessKeyId: "ACCESS_KEY" ,
secretAccessKey: "SECRET_KEY" ,
region: "us-east-1" ,
},
});
const res = await fetch ( "file:///home/user/data.json" );
const data = await res . json ();
const res = await fetch ( "data:text/plain;base64,SGVsbG8sIFdvcmxkIQ==" );
const text = await res . text (); // "Hello, World!"
const blob = new Blob ([ "Hello, World!" ], { type: "text/plain" });
const url = URL . createObjectURL ( blob );
const res = await fetch ( url );
DNS prefetching
Warm up the DNS cache before you need to make a request:
import { dns } from "bun" ;
dns . prefetch ( "api.example.com" );
// ... later, when the request is made, DNS is already resolved
const res = await fetch ( "https://api.example.com/data" );
Preconnect
Start the TCP/TLS handshake before the request is issued:
import { fetch } from "bun" ;
fetch . preconnect ( "https://api.example.com" );
// Or at startup via CLI
// bun --fetch-preconnect https://api.example.com server.ts
Connection pooling
Bun automatically pools and reuses connections to the same host (HTTP keep-alive). To opt out for a single request:
const res = await fetch ( "https://api.example.com" , {
keepalive: false ,
});
The default maximum number of simultaneous connections is 256. Raise it with:
BUN_CONFIG_MAX_HTTP_REQUESTS = 512 bun server.ts
Debugging
Set verbose: true to print request and response headers to the terminal:
const res = await fetch ( "https://example.com" , {
verbose: true ,
});
[fetch] > HTTP/1.1 GET https://example.com/
[fetch] > Connection: keep-alive
[fetch] > User-Agent: Bun/1.3.3
[fetch] > Accept: */*
[fetch] > Host: example.com
[fetch] < 200 OK
[fetch] < Content-Type: text/html; charset=UTF-8
[fetch] < Content-Length: 648
Pass "curl" for more detailed output mimicking curl -v.