Skip to content
sproutboat
Esc
navigateopen⌘Jpreview
On this page

The handler

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

src/index.js must default-export an object literal with a fetch method. Durable Object classes and helper functions may be declared above it. It may import from other files in the project and from your own node_modules: the CLI bundles with Bun before Porffor sees anything.

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 });
  },
};

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

Standard synchronous JavaScript and Web platform pieces: URL, Request, Response, Response.json, Headers, TextEncoder / TextDecoder, JSON, Math, String / Array methods, regular expressions, Date.now() and new Date().toISOString(), atob / btoa, structuredClone.

Crypto: crypto.randomUUID, crypto.getRandomValues, and a subset of crypto.subtle: digest (SHA-256/384/512) and HMAC (importKey, sign, verify). Enough for JWTs (HS256) and hand-rolled sessions; no ECDSA or AES yet. crypto.scryptVerify(password, salt, expected, { N, r, p }) is a Sproutboat extension for checking scrypt hashes made elsewhere (Node/Bun scrypt) during a migration.

ctx.waitUntil(promise), the second argument to fetch/scheduled/queue: work that outlives the response, drained in-process and capped at 25s.

What does not

Not available Notes
Dynamic import(), require() Static imports only: everything has to resolve at build time.
process, Bun, Deno, Buffer, node:* Rejected at check time, in your code and in dependencies alike.
new Proxy Porffor compiles it and then ignores every trap, so a trapped property reads back undefined with no error. Rejected at build time, so it fails loudly instead of silently. This is why itty-router and other Proxy-based routers do not work.
Streams ReadableStream / WritableStream are missing upstream, so a response body is one whole string. No SSE or chunked responses; poll instead.
Most of crypto.subtle digest and HMAC work (see What works); ECDSA, AES-GCM and key wrapping do not, so most JWT libraries beyond HS256 and better-auth still fall short.
WebSocket, XMLHttpRequest Not in the http-sync-v0 capability profile.
fetch() to arbitrary hosts Allowed only for hosts listed in outbound — see Outbound fetch.
Filesystem No fs. Static files go through the assets binding.
Parsing date strings new Date("2024-01-02") is unreliable on the current Porffor pin. Use timestamps.

npm packages work, within those limits. Small pure-JavaScript libraries are fine. Anything that reaches for a platform API, ships native code, or uses a Proxy is not. Note that zod does not currently run, which rules out most libraries that depend on it.

Next

Was this page helpful?