8.3 The supported API surface (the npm-compat prelude)

Custom JavaScript runs against an explicit, supported API list — a compatibility prelude injected into every bundle — not full Node.js. Knowing what's present (and what isn't) saves you from deploy-time surprises.

What's available

  • console.* — logging (also routed to the tenant's logs).
  • fetch / Headers / Request / Response — outbound HTTP, through the fetch grant (8.5). Without the grant, or to a disallowed host, it fails capability_denied before any I/O.
  • TimerssetTimeout / clearTimeout / setInterval / clearInterval, but virtual: when the handler is otherwise idle, pending timers fast-forward. An SDK's retry backoff completes instantly, costing no wall time.
  • queueMicrotask, structuredClone.
  • EncodingTextEncoder/TextDecoder, atob/btoa, Buffer (from/alloc/concat/toString in utf8|base64|hex).
  • URL / URLSearchParams.
  • AbortController / AbortSignal (signals never fire — fetch is synchronous).
  • crypto.getRandomValues / crypto.randomUUID.
  • processenv, nextTick, version (v22.x), platform: "rs2".
  • Web typesBlob/File/FormData (text-based), Event/EventTarget/ CustomEvent.
  • RS2Socket — gated raw TCP/TLS sockets for non-HTTP protocols, behind a socket grant (8.5). RS2Socket.connect(host, port, { tls }){ write, read, close }, synchronous. This is how loadable storage adapters (8.9) talk to databases; for plain HTTP, use fetch.

Presence-only stubs (throw on real use)

WebSocket and ReadableStream exist so feature-detecting libraries don't crash on load, but they throw if you actually use them. Don't rely on them.

Not available in v1

Real wall-clock timers and an event loop, WebSocket connections, binary multipart uploads, and node: built-ins. The Web ReadableStream global is not the streaming API; opt-in request/response streaming uses the RS2-specific ctx methods in 8.2.

Real SDKs that run unmodified

The compat layer is validated against the actual npm packages (bundled with esbuild). These run unmodified:

stripe, openai, @anthropic-ai/sdk, @octokit/core, @supabase/supabase-js, resend, @google/generative-ai, @mistralai/mistralai, groq-sdk.

The known exception is @slack/web-api, whose axios transport imports node:os/node:path. The pattern to take away: SDKs built on fetch work; SDKs that ship a Node-specific transport may not. When in doubt, prefer the fetch-based client or a lighter SDK.

Bundle dependencies at build time

Module imports do not resolve at runtime. Your dependencies must be bundled into the single file at build time — rs2 deploy --bundle runs npx esbuild --bundle --format=esm --platform=browser for you (8.6). Native addons fail at build time, which is the right place to find out.

The takeaway: write to fetch and the Web-standard APIs above, bundle your deps, and keep work inside the handler. Next, the WebAssembly contract for when you'd rather write Rust.


Previous: 8.2 The JavaScript service contract · Manual home · Next: 8.4 The WebAssembly contract →