Skip to main content
The Fetch API provides a modern interface for making HTTP requests and handling responses. Workerd implements the standard Fetch API with extensions for serverless environments.

Overview

The Fetch API consists of several core interfaces:
  • Request - Represents an HTTP request
  • Response - Represents an HTTP response
  • Headers - Manages HTTP headers
  • fetch() - Executes HTTP requests
Implementation: src/workerd/api/http.h and http.c++

Making requests

The global fetch() function sends HTTP requests:
const response = await fetch('https://api.example.com/data');
const json = await response.json();

Request options

Customize requests with options:
const response = await fetch('https://api.example.com/data', {
  method: 'POST',
  headers: {
    'Content-Type': 'application/json',
    'Authorization': 'Bearer token'
  },
  body: JSON.stringify({ key: 'value' })
});

Request object

Create reusable Request objects:
const request = new Request('https://api.example.com/data', {
  method: 'POST',
  headers: new Headers({
    'Content-Type': 'application/json'
  }),
  body: JSON.stringify({ key: 'value' })
});

const response = await fetch(request);

Working with responses

Reading response data

The Response object provides multiple methods to read the body:
// Read as text
const text = await response.text();

// Read as JSON
const json = await response.json();

// Read as ArrayBuffer
const buffer = await response.arrayBuffer();

// Read as Blob
const blob = await response.blob();

// Read as FormData
const formData = await response.formData();

Response properties

Access response metadata:
const response = await fetch('https://example.com');

console.log(response.status);      // 200
console.log(response.statusText);  // "OK"
console.log(response.ok);          // true
console.log(response.headers);     // Headers object
console.log(response.url);         // Final URL after redirects

Creating responses

Return custom responses from your worker:
export default {
  async fetch(request) {
    return new Response('Hello World', {
      status: 200,
      headers: {
        'Content-Type': 'text/plain'
      }
    });
  }
};

Headers

The Headers interface manages HTTP headers:
const headers = new Headers();
headers.set('Content-Type', 'application/json');
headers.append('X-Custom-Header', 'value');

if (headers.has('Content-Type')) {
  console.log(headers.get('Content-Type'));
}

headers.delete('X-Custom-Header');

// Iterate over headers
for (const [key, value] of headers) {
  console.log(`${key}: ${value}`);
}

Request body types

Workerd supports multiple body types:
// String body
const response1 = new Response('Hello World');

// ArrayBuffer body
const bytes = new Uint8Array([72, 101, 108, 108, 111]);
const response2 = new Response(bytes);

// ReadableStream body
const stream = new ReadableStream({
  start(controller) {
    controller.enqueue(new TextEncoder().encode('Hello '));
    controller.enqueue(new TextEncoder().encode('World'));
    controller.close();
  }
});
const response3 = new Response(stream);

// Blob body
const blob = new Blob(['Hello World']);
const response4 = new Response(blob);

// FormData body
const formData = new FormData();
formData.append('key', 'value');
const response5 = new Response(formData);

Generator bodies

Workerd supports async generators as request/response bodies:
async function* generateData() {
  yield new TextEncoder().encode('Hello ');
  yield new TextEncoder().encode('World');
}

const response = new Response(generateData());
const text = await response.text(); // "Hello World"
Source: src/workerd/api/tests/fetch-test.js:26

Body mixin

The Body class (defined in src/workerd/api/http.h:30) is a mixin that provides body-related functionality to both Request and Response.

Body types

using Initializer = kj::OneOf<jsg::Ref<ReadableStream>,
    kj::String,
    kj::Array<byte>,
    jsg::Ref<Blob>,
    jsg::Ref<FormData>,
    jsg::Ref<URLSearchParams>,
    jsg::Ref<url::URLSearchParams>,
    jsg::AsyncGeneratorIgnoringStrings<jsg::Value>>;
This allows bodies to be constructed from various sources while maintaining the ability to retransmit (important for redirects).

Advanced features

Request cloning

Clone requests for multiple uses:
const request = new Request('https://example.com');
const clone = request.clone();

// Both can be used independently
await fetch(request);
await fetch(clone);

Response cloning

Clone responses to read the body multiple times:
const response = await fetch('https://example.com');
const clone = response.clone();

const text = await response.text();
const buffer = await clone.arrayBuffer();

Redirect handling

Control redirect behavior:
const response = await fetch('https://example.com', {
  redirect: 'follow'  // 'follow', 'error', or 'manual'
});

Method normalization

With the upper_case_all_http_methods compatibility flag, method names are converted to uppercase:
const request = new Request('https://example.com', {
  method: 'patch'
});

console.log(request.method); // "PATCH"
Source: src/workerd/api/tests/fetch-test.js:10

Error handling

Handle fetch errors gracefully:
try {
  const response = await fetch('https://example.com');
  
  if (!response.ok) {
    throw new Error(`HTTP error! status: ${response.status}`);
  }
  
  const data = await response.json();
  return new Response(JSON.stringify(data));
} catch (error) {
  return new Response(`Error: ${error.message}`, {
    status: 500
  });
}

Best practices

Always check response.ok or response.status before processing the body:
const response = await fetch(url);
if (!response.ok) {
  throw new Error(`HTTP ${response.status}`);
}
Use response.body as a ReadableStream for large responses:
const response = await fetch(url);
const reader = response.body.getReader();

while (true) {
  const { done, value } = await reader.read();
  if (done) break;
  // Process chunk
}
Create reusable request templates:
const template = new Request('https://api.example.com', {
  headers: { 'Authorization': 'Bearer token' }
});

const response = await fetch(template);

Implementation details

The Fetch API implementation is located in:
  • src/workerd/api/http.h - Header definitions (1300+ lines)
  • src/workerd/api/http.c++ - Implementation (3500+ lines)
  • src/workerd/api/headers.h / headers.c++ - Headers implementation
  • src/workerd/api/blob.h / blob.c++ - Blob implementation
The Body class provides the extraction algorithm from the Fetch spec and handles various body types through the Initializer OneOf type.

Build docs developers (and LLMs) love