Adapters

Fetch Adapter

The Fetch adapter provides cross-platform HTTP support using the standard Fetch API. It works in browsers, Deno, Bun, Node.js 18+, Cloudflare Workers, Vercel Edge, and any runtime that implements globalThis.fetch. It is the recommended adapter for client-side applications and edge runtimes.

import rezo from 'rezo/adapters/fetch';

Import and Setup

import rezo, {
  Rezo,
  RezoError,
  RezoHeaders,
  RezoFormData
} from 'rezo/adapters/fetch';

// Default instance
const { data } = await rezo.get('https://api.example.com/users');

// Custom instance
const client = new Rezo({
  baseURL: 'https://api.example.com',
  timeout: 10000,
  headers: { 'Authorization': 'Bearer my-token' }
});

Environment Detection

The Fetch adapter automatically detects its runtime environment to adjust behavior:

EnvironmentDetectionCookie Jar
Node.jsprocess.versions.nodeYes (manual headers)
BunglobalThis.BunYes (manual headers)
DenoglobalThis.DenoYes (manual headers)
Browserwindow + documentNo (browser-controlled)
Web WorkerWorkerGlobalScopeNo (browser-controlled)
Cloudflare Workersnavigator.userAgent === 'Cloudflare-Workers'Yes (manual headers)
Vercel EdgeglobalThis.EdgeRuntimeYes (manual headers)

In server-side environments (Node.js, Bun, Deno, Cloudflare Workers), the adapter can set the Cookie header manually for jar integration. In browser environments, document.cookie and the Cookie header are controlled by the browser, so the cookie jar is not available.

Redirect Handling

The Fetch adapter uses redirect: 'manual' internally to intercept redirects and handle them itself. This allows Rezo to:

  • Track redirect chains and the final URL
  • Apply cookie jar updates at each redirect step
  • Detect redirect cycles
  • Enforce maxRedirects limits
const response = await rezo.get('https://example.com/short-link', {
  maxRedirects: 5,
  followRedirects: true
});

// Access the final URL after redirects
console.log(response.config.url); // https://example.com/actual-page

Timeout with AbortController

Timeouts are implemented using AbortController, which is the standard mechanism for canceling Fetch requests:

const { data } = await rezo.get('https://slow-api.example.com/data', {
  timeout: 5000 // 5 second timeout
});

You can also provide your own AbortSignal for manual cancellation:

const controller = new AbortController();

// Cancel the request after 3 seconds
setTimeout(() => controller.abort(), 3000);

try {
  const { data } = await rezo.get('https://example.com/data', {
    signal: controller.signal
  });
} catch (error) {
  if (error.code === 'ECONNABORTED') {
    console.log('Request was cancelled');
  }
}

Response Streaming

The Fetch adapter supports response streaming via response.body.getReader():

const response = await rezo.stream('https://example.com/events');

// response.data is a ReadableStream
const reader = response.data.getReader();
const decoder = new TextDecoder();

while (true) {
  const { done, value } = await reader.read();
  if (done) break;
  console.log(decoder.decode(value, { stream: true }));
}

Server-Sent Events

Streaming works well for consuming Server-Sent Events (SSE):

const response = await rezo.stream('https://api.example.com/events', {
  headers: { 'Accept': 'text/event-stream' }
});

const reader = response.data.getReader();
const decoder = new TextDecoder();
let buffer = '';

while (true) {
  const { done, value } = await reader.read();
  if (done) break;

  buffer += decoder.decode(value, { stream: true });
  const lines = buffer.split('
');
  buffer = lines.pop() || '';

  for (const line of lines) {
    if (line.startsWith('data: ')) {
      const event = JSON.parse(line.slice(6));
      handleEvent(event);
    }
  }
}

Download Progress

The adapter tracks download progress using the reader API:

const client = new Rezo({
  hooks: {
    onDownloadProgress: [({ loaded, total, percent }) => {
      if (total) {
        console.log(`Downloaded ${percent.toFixed(1)}%`);
      } else {
        console.log(`Downloaded ${loaded} bytes`);
      }
    }]
  }
});

const { data } = await client.get('https://example.com/large-response');

Note that total is only available when the server sends a Content-Length header.

Retry Logic

The Fetch adapter includes the same retry mechanism as other adapters:

const { data } = await rezo.get('https://flaky-api.example.com/data', {
  retry: {
    limit: 3,
    delay: 1000,
    backoff: 'exponential',
    statusCodes: [429, 500, 502, 503, 504]
  }
});

Rate Limit Wait

Automatic Retry-After header handling for 429 responses:

const client = new Rezo({ rateLimitWait: true });

// Automatically waits if the server returns 429 with Retry-After
const { data } = await client.get('https://rate-limited-api.com/data');

Response Caching

const client = new Rezo({
  cache: {
    maxAge: 30000,   // 30 seconds
    maxEntries: 200
  }
});

const { data: first } = await client.get('https://api.example.com/config');
const { data: second } = await client.get('https://api.example.com/config');
// second is served from cache

Content Type Detection

The adapter automatically detects and parses response content types:

// JSON responses are parsed automatically
const { data } = await rezo.get('https://api.example.com/users');
// data is already a parsed JavaScript object

// Text responses
const { data: html } = await rezo.get('https://example.com', {
  responseType: 'text'
});

// Binary data
const { data: buffer } = await rezo.get('https://example.com/image.png', {
  responseType: 'arraybuffer'
});

Debug Mode

const { data } = await rezo.get('https://api.example.com/users', {
  debug: true
});
// [Rezo Debug] GET https://api.example.com/users
// [Rezo Debug] Adapter: fetch
// [Rezo Debug] Response: 200 OK (95.42ms)

Limitations

The Fetch adapter has several limitations compared to the HTTP and HTTP/2 adapters, mostly due to browser security restrictions:

FeatureStatusReason
Proxy supportNot availableBrowsers do not expose proxy configuration to JavaScript
Streaming requestsNot availableFetch does not support streaming request bodies in all runtimes
HTTP/2 configurationNot availableThe browser/runtime negotiates protocols transparently
DNS cachingNot availableHandled by the runtime’s resolver
TLS configurationNot availableManaged by the runtime’s TLS stack
Cookie jar (browser)Not availableCookie header is controlled by the browser
Upload progressNot availableFetch does not provide upload progress events

While browser environments cannot use the cookie jar, server-side runtimes (Node.js, Bun, Deno, Cloudflare Workers) can:

// This works in Node.js, Bun, Deno, CF Workers
import rezo, { RezoCookieJar } from 'rezo/adapters/fetch';

const jar = new RezoCookieJar();
const client = new Rezo({ jar });

await client.post('https://api.example.com/login', { user: 'admin' });
const { data } = await client.get('https://api.example.com/profile');
// Cookies are sent via manual Cookie header

When to Use This Adapter

The Fetch adapter is the right choice when:

  • You are building a browser application and want the smallest bundle size
  • You are targeting edge runtimes (Cloudflare Workers, Vercel Edge)
  • You are running on Deno
  • You do not need proxy, TLS configuration, or cookie jar support (in browsers)
  • You want maximum runtime compatibility across environments

For server-side Node.js applications that need full features, use the HTTP adapter. For HTTP/2 multiplexing, use the HTTP/2 adapter. For upload progress in browsers, use the XHR adapter.