browserFetch vs serverFetch in ego-lite: HTTP Client Context Explained

serverFetch runs HTTP requests from the Node.js host process, while browserFetch executes requests inside the active browser page, inheriting cookies, authentication, and same-origin policies.

The ego-lite browser automation framework provides a unified fetch façade that conceals two fundamentally different execution strategies. Understanding when to use browserFetch versus serverFetch is essential for building reliable web agents that handle both external API calls and authenticated page interactions.

Execution Context: The Core Distinction

The primary difference between these two methods is where the HTTP request actually executes.

  • serverFetch — Runs inside Node.js on the host machine. It uses Node's native fetch (available in Node 16+) to perform server-side requests with no browser context.

  • browserFetch — Runs inside the active page's JavaScript environment. The request executes as if the user clicked a button or submitted a form, inheriting all browser state.

According to the ego-lite source code in package/ego-browser/src/http.ts, this distinction determines everything from cookie handling to URL resolution.

serverFetch: Server-Side HTTP Requests

Located at lines 11-27 in src/http.ts, serverFetch is designed for retrieving external resources independent of any page state.

Key Characteristics

Feature Implementation
URL handling Requires absolute URLs only
Headers Merges caller headers with default User-Agent: Mozilla/5.0
Timeout Uses native AbortSignal.timeout
Error behavior Throws on non-OK HTTP statuses

Code Example: Scraping External HTML

// Retrieve raw HTML from a third-party site
const html = await fetch.server('https://example.com/docs', {
  timeout: 10,  // seconds
  headers: { 
    'Accept': 'text/html',
    'Authorization': 'Bearer token123'
  },
});
// Returns response body as string

Use serverFetch when you need to download files, query external APIs, or scrape content that doesn't require page authentication.

browserFetch: Authenticated Page Context Requests

Located at lines 35-52 in src/http.ts, browserFetch leverages the evaluate helper from src/cdp-eval.ts to run code directly inside the controlled browser tab.

Key Characteristics

Feature Implementation
URL handling Supports relative URLs resolved against current page
Headers & cookies Automatically includes the page's native browser headers and cookies
Timeout Manual implementation via setTimeout + AbortController
Context Respects same-origin policies and CSRF protections

Code Example: Calling Authenticated APIs

// Current page is logged into a web application
const userData = await fetch.browser('/api/v1/user/profile', {
  method: 'GET',
  headers: { 'Accept': 'application/json' },
  timeout: 5,
});
// Request includes the page's session cookies automatically

Use browserFetch when interacting with endpoints that require the user's active session, CSRF tokens, or same-origin protections.

Technical Implementation Differences

serverFetch Implementation Details

The serverFetch function in src/http.ts wraps Node's native fetch with minimal overhead:

// Simplified from src/http.ts lines 11-27
async function serverFetch(url: string, options: FetchOptions): Promise<string> {
  const response = await fetch(url, {
    ...options,
    headers: {
      'User-Agent': 'Mozilla/5.0 ...', // Default browser-like UA
      ...options.headers,
    },
    signal: AbortSignal.timeout(options.timeout * 1000),
  });
  
  if (!response.ok) throw new Error(`HTTP ${response.status}`);
  return response.text();
}

browserFetch Implementation Details

The browserFetch function constructs an async IIFE that runs inside the page via evaluate:

// Simplified from src/http.ts lines 35-52
async function browserFetch(url: string, options: FetchOptions): Promise<string> {
  return evaluate(`
    (async () => {
      const controller = new AbortController();
      const timeoutId = setTimeout(
        () => controller.abort(), 
        ${options.timeout * 1000}
      );
      
      try {
        const response = await fetch('${url}', {
          method: '${options.method || 'GET'}',
          headers: ${JSON.stringify(options.headers)},
          signal: controller.signal
        });
        
        clearTimeout(timeoutId);
        
        if (!response.ok) throw new Error('HTTP ' + response.status);
        return await response.text();
      } catch (e) {
        clearTimeout(timeoutId);
        throw e;
      }
    })()
  `);
}

When to Use Each Method

Choose your fetch strategy based on these criteria:

  1. Use serverFetch when:

    • Accessing external APIs or third-party websites
    • No authentication or page context is needed
    • You need reliable timeout handling via AbortSignal.timeout
  2. Use browserFetch when:

    • The endpoint requires the page's cookies or session
    • CSRF tokens or same-origin policies apply
    • You need to call APIs relative to the current page URL

Summary

  • serverFetch executes in Node.js with browser-like User-Agent headers; ideal for external API calls and scraping
  • browserFetch runs inside the active page via evaluate, preserving authentication, cookies, and origin constraints
  • Both methods throw errors on non-OK HTTP statuses and support configurable timeouts
  • Relative URLs work only with browserFetch; serverFetch requires absolute URLs
  • The unified fetch façade is exposed through src/helpers.ts lines 28-31

Frequently Asked Questions

Can I use browserFetch without an active page?

No. browserFetch requires an active browser tab controlled by ego-lite because it relies on the evaluate function from src/cdp-eval.ts to inject JavaScript into the page context. Without a loaded page, there's no execution environment for the fetch call.

Does serverFetch share cookies with the browser?

No. serverFetch maintains completely separate cookie storage from the browser. According to the implementation in src/http.ts, it uses Node's native fetch with only a hardcoded User-Agent header. Any cookies must be manually provided in the headers option.

Why does browserFetch use a manual timeout instead of AbortSignal.timeout?

Browser compatibility. The browserFetch implementation targets the JavaScript environment inside the controlled page, which may run on older browser versions without native AbortSignal.timeout support. The manual setTimeout + AbortController pattern ensures consistent behavior across all Chrome versions.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →