# How to Work with Web APIs Like fetch, Request, and Response in Bun

> Learn to use Bun's native Fetch API, including Request and Response objects. Explore browser-standard compliance and Node.js conveniences for seamless web development.

- Repository: [Bun/bun](https://github.com/oven-sh/bun)
- Tags: how-to-guide
- Published: 2026-02-28

---

**Bun provides a native, browser-standard Fetch API implementation built in Zig that exposes global `fetch`, `Request`, `Response`, and `Headers` objects, with a JavaScript compatibility shim in [`src/js/thirdparty/node-fetch.ts`](https://github.com/oven-sh/bun/blob/main/src/js/thirdparty/node-fetch.ts) that adds Node.js-specific conveniences while maintaining Web standards compliance.**

The `oven-sh/bun` runtime ships with a high-performance, native implementation of the Web Fetch API, enabling browser-compatible HTTP requests without external dependencies. Unlike Node.js, which historically required polyfills or third-party packages like `node-fetch`, Bun exposes these APIs globally while providing a compatibility layer to ensure seamless integration with existing Node.js libraries.

## Architecture of Bun's Fetch Implementation

Bun's Fetch API operates through two distinct layers: a core runtime written in Zig and a thin JavaScript shim that ensures Node.js ecosystem compatibility.

### Native Runtime Layer in Zig

The low-level networking logic resides in `src/bun.js/webcore/fetch.zig`, which implements the global `fetch` function and native constructors for **Response**, **Request**, and **Headers**. This layer handles TLS, HTTP/2, connection pooling, and streaming directly within the runtime, exposing these capabilities to JavaScript via internal bindings loaded through `$cpp("NodeFetch.cpp", "createNodeFetchInternalBinding")`. The native constructors—`WebResponse`, `WebRequest`, and `WebHeaders`—provide the foundation for all HTTP operations.

### JavaScript Compatibility Shim

The file [`src/js/thirdparty/node-fetch.ts`](https://github.com/oven-sh/bun/blob/main/src/js/thirdparty/node-fetch.ts) re-exports the native objects while adding Node.js-specific utilities. The shim defines wrapper classes that extend the native implementations: `class Response extends WebResponse` and `class Request extends WebRequest`. These wrappers handle stream conversion, relaxed URL parsing, and the addition of methods like `Headers.raw()` for compatibility with legacy Node.js code. When you invoke `fetch()` in Bun, the shim forwards the call to `nativeFetch.$call` and patches the returned prototype to match the wrapper classes.

## Working with the Response Object

The **Response** class in [`src/js/thirdparty/node-fetch.ts`](https://github.com/oven-sh/bun/blob/main/src/js/thirdparty/node-fetch.ts) extends the native `WebResponse` to provide seamless integration with Node.js streaming libraries. When you access `response.body`, the shim lazily converts the underlying Web Streams API readable stream into a Node.js `Readable` stream using `Readable.toWeb(stream)`. This automatic conversion allows existing Node.js libraries—such as `form-data` or file parsers—to consume Bun fetch responses without modification.

The shim also overrides the `clone()` method and adds Node-specific getters to ensure that `response.body` behaves identically to traditional Node.js HTTP responses while maintaining Web standards compliance for properties like `status`, `statusText`, and `headers`.

## Handling Request Construction and URL Parsing

Bun's **Request** implementation relaxes URL validation to match `node-fetch` behavior. If you pass a string that is not a valid URL (such as a path without a protocol), the constructor in [`src/js/thirdparty/node-fetch.ts`](https://github.com/oven-sh/bun/blob/main/src/js/thirdparty/node-fetch.ts) (lines 20-33) creates a temporary `http://localhost/` placeholder and stores the original string in a private symbol. The public `url` getter retrieves this stored value, allowing relative paths and non-standard URLs to work transparently with middleware and routing libraries.

This approach ensures that code relying on `node-fetch`-style URL handling works in Bun without modification, while the underlying native implementation still operates on valid URL objects for actual network operations.

## Practical Code Examples

The following examples demonstrate how to use Bun's Fetch API patterns, all of which execute through the native Zig implementation while benefiting from the JavaScript shim's compatibility features.

### Simple GET Request

```typescript
// fetch-get.ts
const response = await fetch("https://api.bun.sh/health");
console.log("Status:", response.status);           // → 200
console.log("Body:", await response.text());       // → JSON string

```

This example uses the global `fetch` function exposed by `src/bun.js/webcore/fetch.zig` and processed through the shim's exported wrapper (lines 48-55 of [`src/js/thirdparty/node-fetch.ts`](https://github.com/oven-sh/bun/blob/main/src/js/thirdparty/node-fetch.ts)).

### POST with JSON Payload

```typescript
// fetch-post.ts
const payload = { message: "Hello from Bun!" };
const response = await fetch("https://api.bun.sh/echo", {
  method: "POST",
  headers: { "Content-Type": "application/json" },
  body: JSON.stringify(payload),
});

const data = await response.json();               // ← parses JSON automatically
console.log(data);                                // { message: "Hello from Bun!" }

```

The **Request** object is constructed internally by the shim, which forwards the `init` options directly to the native `Bun.fetch` binding.

### Streaming Large File Uploads

```typescript
// fetch-stream.ts
import { createReadStream } from "node:fs";

const stream = createReadStream("./large-file.bin");   // Node readable stream
const response = await fetch("https://api.bun.sh/upload", {
  method: "PUT",
  body: stream,                     // shim converts Node stream → Web ReadableStream
  headers: { "Content-Type": "application/octet-stream" },
});

console.log("Uploaded, server says:", await response.text());

```

The shim automatically detects Node.js streams (lines 44-48 of [`src/js/thirdparty/node-fetch.ts`](https://github.com/oven-sh/bun/blob/main/src/js/thirdparty/node-fetch.ts)) and converts them to Web Streams using `Readable.toWeb(stream)` before passing them to the native fetch implementation.

### Aborting Requests with AbortController

```typescript
// fetch-abort.ts
const controller = new AbortController();
const timeout = setTimeout(() => controller.abort(), 5_000); // 5 s timeout

try {
  const response = await fetch("https://slow.api/big", {
    signal: controller.signal,
  });
  console.log(await response.text());
} catch (e) {
  if (e.name === "AbortError") console.error("Request aborted");
}
clearTimeout(timeout);

```

The **AbortError** class is defined in the compatibility shim (lines 77-81) and matches the Web API specification for canceled requests.

### Accessing Raw Headers

```typescript
// fetch-raw-headers.ts
const response = await fetch("https://api.bun.sh/metadata");
const rawHeaders = response.headers.raw();   // ← returns { headerName: [value] }
console.log(rawHeaders);

```

The `Headers.raw()` method, implemented in the shim (lines 13-25), returns header values as arrays to maintain compatibility with `node-fetch` patterns.

## Summary

- **Bun implements the Fetch API natively in Zig** via `src/bun.js/webcore/fetch.zig`, exposing global `fetch`, `Request`, `Response`, and `Headers` objects without requiring external packages.
- **A compatibility shim in [`src/js/thirdparty/node-fetch.ts`](https://github.com/oven-sh/bun/blob/main/src/js/thirdparty/node-fetch.ts)** extends native `WebResponse` and `WebRequest` classes to add Node.js-specific features like stream conversion and relaxed URL parsing.
- **Automatic stream conversion** allows you to pass Node.js `Readable` streams directly to `fetch()`; the shim handles translation to Web Streams internally.
- **Non-standard URLs are supported** through private symbol storage in the `Request` constructor, ensuring compatibility with routing libraries that pass relative paths.
- **Raw header access** via `headers.raw()` and proper `AbortError` handling ensure that existing `node-fetch` codebases migrate to Bun without modification.

## Frequently Asked Questions

### Does Bun use the same Fetch API as browsers?

Yes. Bun's implementation follows the Web Fetch API specification exactly, exposing identical global objects: `fetch`, `Request`, `Response`, `Headers`, and `AbortController`. The runtime implements these in `src/bun.js/webcore/fetch.zig` for performance, with a thin JavaScript layer in [`src/js/thirdparty/node-fetch.ts`](https://github.com/oven-sh/bun/blob/main/src/js/thirdparty/node-fetch.ts) that ensures Node.js compatibility without breaking Web standards compliance.

### Can I pass Node.js streams directly to Bun's fetch?

Absolutely. When you provide a Node.js `Readable` stream as the `body` parameter, the compatibility shim detects the stream type and automatically converts it to a Web Streams API `ReadableStream` using `Readable.toWeb(stream)`. This happens transparently in [`src/js/thirdparty/node-fetch.ts`](https://github.com/oven-sh/bun/blob/main/src/js/thirdparty/node-fetch.ts) (lines 44-48), allowing you to use `fs.createReadStream()` or other Node.js streaming utilities directly with `fetch()`.

### How does Bun handle invalid URLs in Request objects?

Bun's `Request` constructor in the compatibility shim (lines 20-33 of [`src/js/thirdparty/node-fetch.ts`](https://github.com/oven-sh/bun/blob/main/src/js/thirdparty/node-fetch.ts)) implements relaxed URL parsing. If you provide a string that is not a valid absolute URL, the constructor creates a dummy `http://localhost/` base URL internally while storing your original string in a private symbol. The public `url` property returns your original value, maintaining compatibility with libraries that pass relative paths or protocol-less URLs.

### Where are the TypeScript definitions for Bun's Fetch API located?

The TypeScript definitions reside in [`packages/bun-types/fetch.d.ts`](https://github.com/oven-sh/bun/blob/main/packages/bun-types/fetch.d.ts) within the `oven-sh/bun` repository. These definitions provide full type support for the Web-standard Fetch API as implemented in Bun, including generic type parameters for `Response.json()` and proper `AbortSignal` integration for request cancellation.