# serverFetch vs browserFetch in ego-lite: Choosing the Right HTTP Context

> Learn when to use serverFetch vs browserFetch in ego-lite for Node.js or browser HTTP requests. Understand context specific needs like cookies and credentials.

- Repository: [CitroLabs/ego-lite](https://github.com/citrolabs/ego-lite)
- Tags: api-reference
- Published: 2026-07-28

---

**Use `serverFetch` when executing HTTP requests from the Node.js runtime without browser state, and `browserFetch` when requests must preserve page cookies, credentials, and same-origin policy.**

The `ego-browser` package in the **citrolabs/ego-lite** repository exposes a unified fetch façade through `helperContext()` that enables agents to perform HTTP requests from either the server or browser context. Choosing between `serverFetch` and `browserFetch` determines whether your request executes within the Node process or inside the active browser page via Chrome DevTools Protocol (CDP).

## Execution Environment Comparison

The fundamental difference lies in where the HTTP request is executed and what environment-specific data it carries.

### serverFetch (Node Context)

**`serverFetch`** runs inside the Node.js process that hosts the ego-lite runtime. According to the implementation in [`src/http.ts`](https://github.com/citrolabs/ego-lite/blob/main/src/http.ts), it binds to the native `globalThis.fetch` available to the server and automatically injects a browser-like **User-Agent** header (`"Mozilla/5.0"`).

**Best for:**
- Accessing third-party APIs that do not depend on the current page's cookies or origin
- Performing large-scale data pulls with custom headers
- Fetching **absolute URLs** regardless of the displayed page

### browserFetch (Browser Context)

**`browserFetch`** executes inside the active browser page via CDP's `evaluate()` method. The implementation packages request parameters into a JSON payload and runs an **async IIFE** within the page's JavaScript engine, using the page's native `fetch` implementation.

**Best for:**
- Calling endpoints that rely on the page's **session cookies** or **CSRF tokens**
- Fetching **relative URLs** (e.g., `/api/data`) that resolve against the current base URL
- Respecting the **same-origin policy** enforced by the browser

## Implementation Details

In [`src/http.ts`](https://github.com/citrolabs/ego-lite/blob/main/src/http.ts), the two implementations diverge in their timeout and execution strategies:

- **`serverFetch`**: Uses `AbortSignal.timeout` for configurable timeouts and returns the response body as a string. It throws a plain `Error` if the response status is not `ok`, including the HTTP method and status code in the message.

- **`browserFetch`**: Creates its own `AbortController` timer and executes the fetch within the page context. Because it runs inside the page, CORS failures and network errors surface as JavaScript errors from the `evaluate()` call.

The fetch façade is registered in [`src/helpers.ts`](https://github.com/citrolabs/ego-lite/blob/main/src/helpers.ts) (lines 818‑820), exposing `fetch.server` and `fetch.browser` to agent scripts.

## Practical Code Examples

### Server-Side Requests with serverFetch

Use `fetch.server` when you need a fast, deterministic request without browser overhead:

```typescript
// Retrieve HTML from an external site without involving the browser page
const html = await fetch.server('https://example.com', {
  timeout: 10,               // seconds
  headers: { 'Accept': 'text/html' },
});
console.log('Fetched HTML length:', html.length);

```

This request originates from the Node runtime using a static User-Agent. No cookies from the current page are transmitted, making it ideal for third-party API calls.

### Browser-Context Requests with browserFetch

Use `fetch.browser` when the request requires browser state:

```typescript
// Call a relative API endpoint that requires session cookies
const data = await fetch.browser('/api/user/profile', {
  method: 'GET',
  headers: { 'Accept': 'application/json' },
  timeout: 5,
});
console.log('User profile:', JSON.parse(data));

```

Because this executes inside the active page, the request automatically includes the page's cookies and resolves `/api/user/profile` against the current page's base URL.

### Combining Both fetch Modes

You can securely mix both approaches to extract tokens from the browser and use them server-side:

```typescript
// Obtain CSRF token from the page, then post data from the server
const csrf = await fetch.browser('/api/csrf-token');
await fetch.server('https://api.example.com/submit', {
  method: 'POST',
  headers: {
    'Content-Type': 'application/json',
    'X-CSRF-Token': csrf,
  },
  body: JSON.stringify({ foo: 'bar' }),
});

```

This pattern leverages `browserFetch` to access page-stored credentials, then uses `serverFetch` for the subsequent submission, maintaining security boundaries while enabling flexible data gathering.

## Error Handling and Timeouts

Both methods enforce identical error semantics: they throw a plain `Error` if the response is not `ok` (status outside 200-299). The error message includes the HTTP status code and method used.

With `browserFetch`, additional error types may surface from the page context, such as CORS violations or network failures specific to the browser's environment, which are captured from the CDP `evaluate()` execution.

## Summary

- **Use `serverFetch`** (via `fetch.server`) for Node-side requests that require custom headers, absolute URLs, or no browser state. Implemented in [`src/http.ts`](https://github.com/citrolabs/ego-lite/blob/main/src/http.ts) using `globalThis.fetch`.
- **Use `browserFetch`** (via `fetch.browser`) for requests that must execute within the page context to preserve cookies, handle relative URLs, and respect same-origin policy. Implemented using CDP `evaluate()`.
- **Both methods** support configurable timeouts (`AbortSignal.timeout` for server, `AbortController` for browser) and throw errors on non-ok responses.
- The façade is registered in [`src/helpers.ts`](https://github.com/citrolabs/ego-lite/blob/main/src/helpers.ts), making both methods available through the helper context provided to agent scripts.

## Frequently Asked Questions

### What is the primary difference between serverFetch and browserFetch in ego-lite?

**`serverFetch`** executes in the Node.js runtime using the server's native `fetch`, while **`browserFetch`** runs inside the active browser page via Chrome DevTools Protocol's `evaluate()` method. The key distinction is that `browserFetch` preserves the page's cookies, origin, and credentials, whereas `serverFetch` operates independently of browser state.

### Can I use relative URLs with serverFetch?

**No.** `serverFetch` requires absolute URLs because it executes in the Node context without a base URL reference. Use `browserFetch` for relative URLs (e.g., `/api/data`), as it resolves paths against the current page's location within the browser environment.

### How does error handling compare between the two methods?

Both methods throw a plain `Error` if the HTTP response status is not `ok`, including the status code and method in the error message. However, `browserFetch` may also surface page-specific errors such as CORS violations or network failures from the browser's JavaScript engine, while `serverFetch` errors originate from the Node runtime.

### When should I combine serverFetch and browserFetch in a single script?

Combine them when you need to extract authentication tokens or CSRF values from the browser state using `browserFetch`, then transmit data to external APIs using `serverFetch`. This pattern keeps sensitive credentials within the browser context while allowing server-side requests to third-party endpoints that don't require browser sessions.