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

> Understand browserFetch vs serverFetch in ego-lite. Learn how each handles HTTP client context, cookies, and authentication for Node.js or browser execution.

- Repository: [CitroLabs/ego-lite](https://github.com/citrolabs/ego-lite)
- Tags: deep-dive
- Published: 2026-08-01

---

**`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`](https://github.com/citrolabs/ego-lite/blob/main/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`](https://github.com/citrolabs/ego-lite/blob/main/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

```typescript
// 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`](https://github.com/citrolabs/ego-lite/blob/main/src/http.ts), `browserFetch` leverages the `evaluate` helper from [`src/cdp-eval.ts`](https://github.com/citrolabs/ego-lite/blob/main/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

```typescript
// 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`](https://github.com/citrolabs/ego-lite/blob/main/src/http.ts) wraps Node's native fetch with minimal overhead:

```typescript
// 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`:

```typescript
// 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`](https://github.com/citrolabs/ego-lite/blob/main/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`](https://github.com/citrolabs/ego-lite/blob/main/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`](https://github.com/citrolabs/ego-lite/blob/main/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.