> ## Documentation Index
> Fetch the complete documentation index at: https://docs.vertz.dev/llms.txt
> Use this file to discover all available pages before exploring further.

# Runtime Environment

> What globals and APIs are available inside Vertz server handlers

Vertz server handlers run in a Cloudflare-Workers-compatible JavaScript environment. The same code runs locally under `vtz dev` and on the edge under `vtz deploy` — the runtime surface is deliberately aligned with Workers so there is no "works locally, breaks in production" gap.

## What's available

**Network + data:**

* `fetch`, `Request`, `Response`, `Headers`
* `URL`, `URLSearchParams`
* `AbortController`, `AbortSignal`
* `FormData`, `Blob`, `ReadableStream`, `WritableStream`, `TransformStream`
* `TextEncoder`, `TextDecoder`

**Timers + scheduling:**

* `setTimeout`, `setInterval`, `clearTimeout`, `clearInterval`
* `queueMicrotask`, `performance.now()`

**Crypto:**

* `crypto.randomUUID()`, `crypto.getRandomValues()`, `crypto.subtle`

**Environment identification:**

* `navigator.userAgent` — identifies the Vertz server runtime (useful for `User-Agent` headers on outbound `fetch`)

**Standard ECMAScript:** `Promise`, `Map`, `Set`, `WeakMap`, `WeakSet`, `Intl`, `JSON`, etc.

**Vertz APIs:** everything in `@vertz/server` (`entity`, `service`, `rules`, etc.), `@vertz/db`, `@vertz/schema`, and `@vertz/errors`.

## What's not available

Server handlers do **not** expose browser globals:

* `window`, `document`, `HTMLElement`, `Element`, `Node`, and other DOM types
* `location`, `history`
* `localStorage`, `sessionStorage`
* `MutationObserver`, `ResizeObserver`, `IntersectionObserver`

Calling any of these from a handler throws `ReferenceError`. That's intentional — handler code has no DOM to manipulate, and the absence of these globals is what lets third-party SDKs recognize the environment as "not a browser".

## Using third-party SDKs

SDKs that gate on `typeof window !== 'undefined'` (like `@anthropic-ai/sdk`, `openai`, and `stripe`) work out of the box — no `dangerouslyAllowBrowser: true` flag required.

```ts theme={null}
import Anthropic from '@anthropic-ai/sdk';
import { service } from '@vertz/server';
import { s } from '@vertz/schema';

export default service('ai', {
  actions: {
    summarize: {
      body: s.object({ text: s.string() }),
      handler: async ({ text }) => {
        const client = new Anthropic({ apiKey: process.env.ANTHROPIC_API_KEY! });
        const message = await client.messages.create({
          model: 'claude-haiku-4-5',
          max_tokens: 256,
          messages: [{ role: 'user', content: `Summarize: ${text}` }],
        });
        return { summary: message.content };
      },
    },
  },
});
```

<Warning>
  If any examples or workarounds you've seen use `dangerouslyAllowBrowser: true` in a Vertz handler,
  remove it. That flag disables a safety check that prevents API keys from leaking into client
  bundles — it is not needed in a server handler and leaves you exposed if the same file is ever
  imported by client code.
</Warning>

## SSR vs. handlers

During server-side rendering (SSR) of a page, Vertz transiently installs a DOM shim (`document`, `window`, etc.) so the same components that run in the browser can build HTML on the server. That shim is scoped to the render — it is installed before the framework begins rendering the matched route and removed immediately after.

Handler code (entity actions, service actions, middleware, auth resolvers, etc.) invoked through the top-level HTTP/API dispatch path runs **outside** that scope. A handler triggered by a direct request from the browser to `/api/...`, a form submission, or any non-SSR entry point sees `typeof window === 'undefined'`.

### One edge case: handlers invoked via `fetch('/api/...')` during SSR

If an SSR render itself calls `fetch('/api/...')` (for example, a loader or query that runs on the server), the internal fetch interceptor dispatches that request to the handler **in-place**, while the DOM shim is still installed for the ongoing render. In that path, the handler transiently sees `window` / `document` / `location`.

For the SDK-detection problem this is usually harmless — loaders and queries typically read data and don't construct `@anthropic-ai/sdk` or `openai` clients. But if you need a guaranteed-clean environment (e.g. you instantiate an SDK inside a handler that is also reachable from SSR), prefer:

* constructing the SDK client at module scope instead of per-request, or
* keeping SDK construction in actions that are only called from the client (form submissions, button clicks) rather than SSR loaders.

The direct `/api/...` request path — which is how form submissions and client-side `fetch` calls reach handlers in production — always sees the clean environment.

## Migrating from older workarounds

If you or a library you depend on added one of the following because of `ReferenceError: window is not defined` or `@anthropic-ai/sdk: It looks like you're running in a browser-like environment`, remove it:

* `dangerouslyAllowBrowser: true` on any SDK constructor
* `globalThis.window = undefined` / `delete globalThis.window` at the top of a handler file
* `if (typeof window === 'undefined')` guards around handler-only code (no longer necessary for correctness — still safe to leave in place)
