Skip to content
sproutboat
Esc
↑↓navigate↵open⌘Jpreview
On this page

The handler

The fetch handler contract, and what JavaScript actually runs in production.

The entry point is src/index.js by default; main can also point to a TypeScript file such as src/index.ts. It must default-export an object literal with a fetch method. Durable Object classes and helper functions can be declared above it. The CLI bundles the handler and its imports with Bun, which strips TypeScript syntax before Porffor compiles the JavaScript. To see type errors, run a separate type check.

export default {
  fetch(request) {
    const url = new URL(request.url);
    if (url.pathname === "/health") return new Response("ok");
    return Response.json({ method: request.method, path: url.pathname });
  },
};

Handler arguments

The arguments depend on compatibility_date in sproutboat.jsonc:

compatibility_date Signature
2026-09-28 or later fetch(request, env, ctx), the Workers signature. scheduled and queue also receive env second.
Earlier fetch(request, ctx). env is only a global.

sproutboat init writes 2026-08-26, so a new project starts with fetch(request, ctx). Set the date to 2026-09-28 or later to use the Workers signature.

env is also available as a global in both modes.

Extra handlers

The same default export can also carry scheduled and queue:

export default {
  fetch(request) { /* ... */ },
  scheduled(event) { /* event.cron, event.scheduledTime */ },
  queue(batch) { /* batch.queue, batch.messages, message.ack(), message.retry() */ },
};

What works

JavaScript and Web APIs. Standard synchronous JavaScript, plus URL, URLSearchParams, Request, Response, Response.json, Response.redirect, Headers, FormData, TextEncoder and TextDecoder, JSON, Math, regular expressions, Date.now() and new Date().toISOString(), atob and btoa.

Request and response bodies. Bodies are buffered. text(), json(), arrayBuffer(), bytes(), blob() and formData() return their value directly; formData() parses URL-encoded and multipart bodies.

structuredClone copies through JSON, so Map, Set, Date and typed arrays do not survive it.

Crypto. crypto.randomUUID, crypto.getRandomValues, and part of crypto.subtle: digest (SHA-256, SHA-384, SHA-512) and HMAC importKey, sign and verify. That covers HS256 JWTs and signed session cookies. crypto.scryptVerify(password, salt, expected, { N, r, p }) is a Sproutboat extension for checking scrypt hashes created by Node or Bun, for example during a migration.

Cache API. caches.default and caches.open(name) with match, put and delete. Calls return directly. Entries are keyed on the URL, only GET requests can be stored, and expiry comes from s-maxage or max-age. Vary is ignored. Store text bodies only.

request.cf. clientIp is the client’s address. When the connection comes from a proxy listed in SB_TRUSTED_PROXIES, clientIp comes from X-Forwarded-For, and httpProtocol, tlsVersion and tlsCipher are filled from the proxy’s headers.

ctx.waitUntil(promise) runs work after the response is sent, in the same process, for up to 25 seconds.

Node built-ins. node:path, node:url and node:querystring work through built-in shims. See Runtime compatibility for tested npm packages.

What does not

Not available Notes
Dynamic import(), require() Static imports only: everything has to resolve at build time.
process, Bun, Deno, Buffer Runtime globals are unavailable in the handler.
Other Node built-ins Only the three shims above.

| Streams | ReadableStream, WritableStream and TransformStream work in memory. HTTP bodies remain buffered, and stream BodyInit is unsupported. No server-sent events or chunked responses; poll instead. For large files, use R2’s direct transfer URLs. | | Most of crypto.subtle | ECDSA, RSA, AES and key wrapping are unavailable, so JWT algorithms other than HS256 and libraries such as better-auth do not work yet. | | WebSocket, XMLHttpRequest | Not in the http-sync-v0 capability profile. | | fetch() to a private address | Loopback, private networks and the cloud metadata endpoint are refused; see Outbound fetch. | | Filesystem | No fs. Serve files through static assets or store them in R2. | | Parsing date strings | new Date("2024-01-02") is unreliable on the current Porffor version. Use timestamps. |

Next

Proxy and iterators

CLI 0.17.0 uses Porffor alpha-17 and accepts new Proxy and Proxy.revocable. Native regression checks cover common object traps, callable and constructible proxies, revocation and a frozen-property invariant. URLSearchParams and FormData provide live keys, values and entries iterators. See Runtime compatibility for tested packages and remaining iterator limits.

Was this page helpful?