Vertz server handlers run in a Cloudflare-Workers-compatible JavaScript environment. The same code runs locally underDocumentation Index
Fetch the complete documentation index at: https://docs.vertz.dev/llms.txt
Use this file to discover all available pages before exploring further.
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,HeadersURL,URLSearchParamsAbortController,AbortSignalFormData,Blob,ReadableStream,WritableStream,TransformStreamTextEncoder,TextDecoder
setTimeout,setInterval,clearTimeout,clearIntervalqueueMicrotask,performance.now()
crypto.randomUUID(),crypto.getRandomValues(),crypto.subtle
navigator.userAgent— identifies the Vertz server runtime (useful forUser-Agentheaders on outboundfetch)
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 typeslocation,historylocalStorage,sessionStorageMutationObserver,ResizeObserver,IntersectionObserver
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 ontypeof window !== 'undefined' (like @anthropic-ai/sdk, openai, and stripe) work out of the box — no dangerouslyAllowBrowser: true flag required.
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.
/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 ofReferenceError: window is not defined or @anthropic-ai/sdk: It looks like you're running in a browser-like environment, remove it:
dangerouslyAllowBrowser: trueon any SDK constructorglobalThis.window = undefined/delete globalThis.windowat the top of a handler fileif (typeof window === 'undefined')guards around handler-only code (no longer necessary for correctness — still safe to leave in place)