Skip to main content
The deno_fetch extension provides a standards-compliant HTTP client implementation, including the Fetch API, Request, Response, Headers, and FormData.

Location

ext/fetch/

What It Provides

Fetch API

The main interface for making HTTP requests:
// Simple GET request
const response = await fetch("https://api.example.com/data");
const data = await response.json();

// POST with JSON body
const response = await fetch("https://api.example.com/users", {
  method: "POST",
  headers: {
    "Content-Type": "application/json",
  },
  body: JSON.stringify({ name: "Alice" }),
});

// With AbortController
const controller = new AbortController();
const signal = controller.signal;

setTimeout(() => controller.abort(), 5000);

try {
  const response = await fetch("https://slow-api.com", { signal });
} catch (err) {
  if (err.name === "AbortError") {
    console.log("Request timed out");
  }
}

Request and Response

Constructing and manipulating HTTP messages:
// Create a Request
const request = new Request("https://api.example.com/data", {
  method: "POST",
  headers: {
    "Authorization": "Bearer token123",
  },
  body: "request data",
});

// Clone a request
const cloned = request.clone();

// Create a Response
const response = new Response("Hello, World!", {
  status: 200,
  headers: {
    "Content-Type": "text/plain",
  },
});

// Read response body
const text = await response.text();
const json = await response.json();
const blob = await response.blob();
const buffer = await response.arrayBuffer();

Headers

Manage HTTP headers:
const headers = new Headers();
headers.append("Content-Type", "application/json");
headers.set("Authorization", "Bearer token");
headers.delete("X-Custom-Header");

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

// Check existence
if (headers.has("Content-Type")) {
  console.log(headers.get("Content-Type"));
}

FormData

Construct multipart form data:
const formData = new FormData();
formData.append("username", "alice");
formData.append("file", new Blob(["content"]), "file.txt");

// Send as request body
const response = await fetch("https://api.example.com/upload", {
  method: "POST",
  body: formData,
});

Custom HTTP Client

Create HTTP clients with custom settings:
// Create custom client with specific settings
const client = Deno.createHttpClient({
  // Custom CA certificates
  caCerts: [await Deno.readTextFile("./ca.pem")],
  
  // Proxy configuration
  proxy: {
    url: "http://proxy.example.com:8080",
    basicAuth: {
      username: "user",
      password: "pass",
    },
  },
  
  // Connection pool settings
  poolMaxIdlePerHost: 10,
  poolIdleTimeout: 90000,
  
  // HTTP version settings
  http2: true,
  http1: true,
});

// Use with fetch
const response = await fetch("https://example.com", { client });

// Clean up
client.close();

EventSource (Server-Sent Events)

Stream events from the server:
const eventSource = new EventSource("https://api.example.com/events");

eventSource.onmessage = (event) => {
  console.log("Message:", event.data);
};

eventSource.addEventListener("custom-event", (event) => {
  console.log("Custom:", event.data);
});

eventSource.onerror = (error) => {
  console.error("Error:", error);
};

// Close connection
eventSource.close();

Supported Protocols

HTTP/HTTPS

Standard HTTP requests:
const response = await fetch("https://example.com");

File URLs

Read local files (requires --allow-read):
const response = await fetch("file:///path/to/file.txt");
const text = await response.text();

Data URLs

Inline data:
const response = await fetch(
  "data:text/plain;base64,SGVsbG8sIFdvcmxkIQ=="
);
const text = await response.text(); // "Hello, World!"

Key Operations

Rust operations exposed to JavaScript:
op_fetch              // Initiate HTTP request
op_fetch_send         // Send request and get response
op_fetch_custom_client // Create custom HTTP client
op_utf8_to_byte_string // Convert UTF-8 to byte string

Implementation Details

Extension Definition

deno_core::extension!(deno_fetch,
  deps = [ deno_webidl, deno_web ],
  ops = [
    op_fetch,
    op_fetch_send,
    op_utf8_to_byte_string,
    op_fetch_custom_client,
  ],
  esm = [
    "20_headers.js",
    "21_formdata.js",
    "22_body.js",
    "23_request.js",
    "23_response.js",
    "26_fetch.js",
    "27_eventsource.js"
  ],
  options = {
    options: Options,
  }
);

HTTP Client Architecture

Built on hyper and tokio:
pub struct Client {
  inner: Decompression<
    Retry<
      FetchRetry,
      hyper_util::client::legacy::Client<Connector, ReqBody>,
    >,
  >,
  connector: Connector,
  user_agent: HeaderValue,
}
Features:
  • Automatic decompression (gzip, brotli)
  • Connection pooling
  • Retry logic for certain errors
  • Custom DNS resolver support
  • TLS with certificate validation

Request Lifecycle

  1. JavaScript: Call fetch(url, options)
  2. op_fetch: Create request, check permissions, return request RID
  3. op_fetch_send: Send request via hyper, return response RID
  4. JavaScript: Read response body via resource ops

Resource Management

Fetch uses resources for:
FetchRequestResource   // Pending request
FetchResponseResource  // HTTP response with streaming body
FetchCancelHandle      // Cancellation handle
HttpClientResource     // Custom HTTP client

DNS Resolution

Custom DNS resolver with caching:
pub struct Resolver {
  // Uses hickory-dns for async resolution
}

Proxy Support

Supports multiple proxy types:
pub enum Proxy {
  Http { url: String, basic_auth: Option<BasicAuth> },
  Tcp { hostname: String, port: u16 },
  Unix { path: String },
  Vsock { cid: u32, port: u32 },
}

Performance Features

Connection Pooling

Reuses TCP connections:
const client = Deno.createHttpClient({
  poolMaxIdlePerHost: 32,
  poolIdleTimeout: 90000, // 90 seconds
});

Automatic Decompression

Transparent gzip/brotli decompression:
// Response is automatically decompressed
const response = await fetch(url);
const text = await response.text();

Streaming Body

Zero-copy streaming for large responses:
const response = await fetch("https://example.com/large-file");
const stream = response.body;

for await (const chunk of stream) {
  // Process chunk without loading entire file
}

Error Handling

try {
  const response = await fetch("https://example.com");
  
  if (!response.ok) {
    throw new Error(`HTTP ${response.status}: ${response.statusText}`);
  }
  
  const data = await response.json();
} catch (err) {
  if (err.name === "TypeError") {
    console.error("Network error:", err.message);
  } else {
    console.error("Error:", err);
  }
}

File Structure

ext/fetch/
├── lib.rs              # Extension definition
├── dns.rs              # DNS resolution
├── proxy.rs            # Proxy handling
├── fs_fetch_handler.rs # File URL handling
├── 20_headers.js       # Headers implementation
├── 21_formdata.js      # FormData implementation
├── 22_body.js          # Body mixin
├── 23_request.js       # Request class
├── 23_response.js      # Response class
├── 26_fetch.js         # Main fetch function
└── 27_eventsource.js   # EventSource (SSE)

Standards Compliance

Implements:

See Also

Build docs developers (and LLMs) love