# browserFetch vs serverFetch in ego-lite: Execution Context and Use Cases Explained

> Understand browserFetch vs serverFetch in ego-lite. Learn how each helper function executes requests from the browser or Node.js agent and their key use cases.

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

---

**The `fetch.browser()` helper runs HTTP requests inside the active browser page (inheriting cookies and origin), while `fetch.server()` executes requests directly from the Node.js agent process outside the browser context.**

In **citrolabs/ego-lite**, these two complementary helpers solve distinct automation challenges. Understanding when to use each prevents authentication failures, CORS errors, and unexpected behavior in your agent scripts. Both functions are implemented in [`src/http.ts`](https://github.com/citrolabs/ego-lite/blob/main/src/http.ts) and exposed through the helpers registry in [`src/helpers.ts`](https://github.com/citrolabs/ego-lite/blob/main/src/helpers.ts).

## What browserFetch Does: In-Page Request Execution

`fetch.browser()` executes `fetch()` **inside the currently loaded page** using Chrome DevTools Protocol (CDP) evaluation.

### How It Works

According to the source code in [`src/http.ts`](https://github.com/citrolabs/ego-lite/blob/main/src/http.ts) (lines 35-52), `browserFetch` sends a script via `evaluate` that runs the native `fetch` API within the page's JavaScript context. This gives it three critical properties:

- **Cookie inheritance**: Requests include the page's stored cookies automatically
- **Origin respect**: Same-origin policy applies as if the user clicked a button
- **Relative URL resolution**: Paths like `/api/data` resolve against the page URL

### When to Use browserFetch

Use `fetch.browser()` when you need to interact with endpoints that depend on the page state:

- Authenticated API endpoints requiring session cookies
- CSRF-protected forms
- Relative URLs that only make sense in the page context
- Endpoints that check `Origin` or `Referer` headers

```javascript
// POST to a relative endpoint with automatic cookie handling
const response = await fetch.browser('/api/v1/user/profile', {
  method: 'POST',
  headers: { 'Content-Type': 'application/json' },
  body: JSON.stringify({ name: 'updated' })
});

```

## What serverFetch Does: Agent-Side Request Execution

`fetch.server()` runs HTTP requests **from the Node.js process** driving the browser, completely outside the page context.

### How It Works

The `serverFetch` implementation in [`src/http.ts`](https://github.com/citrolabs/ego-lite/blob/main/src/http.ts) (lines 11-27) calls `globalThis.fetch` with a browser-like User-Agent header and timeout controller. Since it runs in Node, it operates without browser security constraints.

### Key Characteristics

- **No cookie jar**: Requests are stateless unless you manually attach headers
- **No origin restrictions**: Can reach any URL regardless of CORS policies
- **Direct network access**: Bypasses any page-level proxies or interceptors

### When to Use serverFetch

Use `fetch.server()` for operations independent of page state:

- Downloading static resources (images, PDFs, binaries)
- Calling external APIs unrelated to the current session
- Fetching data before navigating to a target page
- Bypassing CORS restrictions that block browser requests

```javascript
// Fetch a resource directly from the agent
const html = await fetch.server('https://cdn.example.com/data.json');
console.log('Fetched bytes:', html.length);

```

## Side-by-Side Comparison

| Aspect | `fetch.browser()` | `fetch.server()` |
|--------|-------------------|------------------|
| **Execution location** | Inside active browser page (`evaluate`) | Node.js agent process |
| **Source file** | [`src/http.ts`](https://github.com/citrolabs/ego-lite/blob/main/src/http.ts) lines 35-52 | [`src/http.ts`](https://github.com/citrolabs/ego-lite/blob/main/src/http.ts) lines 11-27 |
| **Cookie handling** | Automatic (page's cookie jar) | None (manual header required) |
| **Relative URLs** | Resolve against page URL | Invalid (requires absolute URL) |
| **CORS policy** | Enforced by browser | Ignored |
| **Use case** | Page-dependent API calls | External resource fetching |

## Implementation Details from Source Code

Both helpers are registered in [`src/helpers.ts`](https://github.com/citrolabs/ego-lite/blob/main/src/helpers.ts) where the documentation string explains their API surface (lines 818-819). Their public signatures are declared in [`src/format.ts`](https://github.com/citrolabs/ego-lite/blob/main/src/format.ts) for the `help()` system:

- `"fetch.server": { signature: "fetch.server(url, options?) => Promise<string>", ... }` (lines 886-904)
- `"fetch.browser": { signature: "fetch.browser(url, options?) => Promise<string>", ... }` (lines 906-925)

Both return `Promise<string>` containing the response body, not a Response object. The `options` parameter accepts standard `fetch` init properties (`method`, `headers`, `body`, etc.).

## Common Mistakes to Avoid

**Using `fetch.server()` for authenticated endpoints**
: Server requests lack the page's session cookies. APIs requiring login will return 401 errors.

**Using `fetch.browser()` for cross-origin external APIs**
: Browser CORS policies block requests to unrelated domains unless the server allows them.

**Passing relative URLs to `fetch.server()`**
: Server fetch has no page context; always use absolute URLs.

## Summary

- **`fetch.browser()`** runs inside the loaded page via CDP `evaluate`, inheriting cookies, origin, and security policies—ideal for authenticated page APIs.
- **`fetch.server()`** executes from the Node agent with direct network access, bypassing CORS and browser constraints—ideal for external resources and pre-navigation fetching.
- Both helpers live in **[`src/http.ts`](https://github.com/citrolabs/ego-lite/blob/main/src/http.ts)**, are registered in **[`src/helpers.ts`](https://github.com/citrolabs/ego-lite/blob/main/src/helpers.ts)**, and documented in **[`src/format.ts`](https://github.com/citrolabs/ego-lite/blob/main/src/format.ts)**.
- Both return `Promise<string>` and accept standard fetch options.

## Frequently Asked Questions

### Can I use `fetch.server()` to bypass CORS restrictions?

Yes. Since `fetch.server()` runs in Node.js outside the browser context, it is not subject to CORS policies. This makes it useful for accessing APIs that deny cross-origin browser requests. However, any authentication or session state must be manually provided in the `headers` option.

### Why does `fetch.browser()` fail with a relative URL error on some pages?

`fetch.browser()` resolves relative URLs against the current page URL. If no page is loaded (or the page URL is `about:blank`), resolution fails. Ensure you've navigated to a valid page with `browser.goto()` before calling `fetch.browser()` with relative paths.

### Do these helpers support streaming responses?

No. According to the signature declarations in [`src/format.ts`](https://github.com/citrolabs/ego-lite/blob/main/src/format.ts), both helpers return `Promise<string>`—the full response body as a string. For large binary downloads, use `fetch.server()` and consider the memory implications, or implement chunked handling at the agent level.