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 thefetchgrant (8.5). Without the grant, or to a disallowed host, it failscapability_deniedbefore any I/O.- Timers —
setTimeout/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.- Encoding —
TextEncoder/TextDecoder,atob/btoa,Buffer(from/alloc/concat/toStringin utf8|base64|hex). URL/URLSearchParams.AbortController/AbortSignal(signals never fire — fetch is synchronous).crypto.getRandomValues/crypto.randomUUID.process—env,nextTick,version(v22.x),platform: "rs2".- Web types —
Blob/File/FormData(text-based),Event/EventTarget/CustomEvent. RS2Socket— gated raw TCP/TLS sockets for non-HTTP protocols, behind asocketgrant (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, usefetch.
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 →