How fetch Connects JavaScript Runtimes to Durable Compute
- Durable Compute
Durable Compute
JavaScript is a common execution target; Web Platform APIs provide a shared
interface across its runtimes. fetch connects browsers and servers, while the
same Request=/=Response model also reaches named, stateful actors. The important
shift is what the host can recover after a request—or an entire process—ends.
Consider a workflow that fetches input, calls an AI service, waits hours for human approval, then publishes a result. Its lifetime exceeds any one HTTP request or server process. JavaScript runtimes and Web APIs supply a familiar programming model; durability lets that work outlive the execution that started it.1
Runtimes & Web Platform APIs
An engine executes JavaScript; a runtime adds I/O. The shared Web API core:
fetch, Request, Response, Headers, streams, URL and Web Crypto.
| Runtime | Typical environment | Web API support |
|---|---|---|
| Browser | Client | Core + DOM |
| Node.js | Server / cloud VM | Core; HTTP server adapters |
| Deno | Server / cloud VM | Core; Deno.serve |
| Bun | Server / cloud VM | Core; Bun.serve |
| QuickJS | Embedded engine | Web APIs supplied by host |
| workerd | Cloudflare / self-hosted | Worker subset; fetch handler |
| celld | Self-hosted fleet | Workers-compatible subset |
Support varies by runtime/version; DOM, CORS, cookies and streaming are not uniform.
Client: fetch(request) -> Promise.
Server: handler(Request) -> Response | Promise; startup APIs differ.
This common boundary is where the pieces connect: a fetch call can sit inside a
durable workflow step, a Request can be routed to a named stateful object, and a
host can suspend a program while external work completes. The HTTP interface
stays familiar while the host adds persistence, scheduling and recovery—not
because Fetch itself is durable, but because durability can be built around it.
Deno's team announced one year of bug/security fixes in October 2026, then ending its runtime development; Deno remains open source.
Durable execution
- Workflows: persist step results; after interruption, replay reuses completed steps and retries unfinished work. Make external effects idempotent.
- dquickts: snapshot the JS heap and suspended stack at safepoints; restore and resume in another process using the same engine build. Host I/O is separate.
dquickts: save the running program, not just a step result
The QuickJS extension demonstrates a deeper form of interruption and resumption: a host can pause JavaScript even in the middle of a loop, serialize the runtime heap and suspended call stack, and write that snapshot to disk. Objects, closures, local variables, pending promise jobs and the saved instruction position are part of the recoverable execution state.2
The original host process can then exit. A new process loads the snapshot into a compatible engine build and resumes the program at the saved instruction, rather than rerunning the workflow to reconstruct its progress. This makes hibernation and long waits possible without keeping the original process resident.
The connection to workflow I/O is equally important: JS_SuspendCall() can park a
host call, and JS_ResumeWith() can supply its result after restoring the runtime.
The project's example uses a fetch-like host function to illustrate this boundary;
it is not a built-in implementation of the standard Fetch API. The host still
owns external I/O and safe handling of repeated side effects.3
%%{init: {"themeVariables": {"fontSize": "18px"}, "flowchart": {"htmlLabels": false, "curve": "linear", "nodeSpacing": 24, "rankSpacing": 32}}}%%
flowchart TB
subgraph WF["Workflow"]
direction TB
W1["Run a step"] --> W2[("Persist step result")]
W2 -->|"process restarts"| W3["Replay workflow
Reuse saved results"]
end
subgraph JS["dquickts"]
direction TB
R1["Suspend inside a loop"] --> R2[("Heap + stack snapshot
Written to disk")]
R2 -->|"new process"| R3["Restore + resume
Saved instruction"]
end
classDef running fill:#f8fafc,stroke:#64748b,color:#0f172a
classDef saved fill:#eff6ff,stroke:#2563eb,color:#0f172a
classDef resumed fill:#ecfdf5,stroke:#059669,color:#0f172a
class W1,R1 running
class W2,R2 saved
class W3,R3 resumed
style WF fill:#fff,stroke:#cbd5e1,color:#0f172a
style JS fill:#fff,stroke:#cbd5e1,color:#0f172aDurable Objects
A named, single-threaded actor owns strongly consistent SQLite/KV storage.
Cloudflare layers distributed routing, storage, alarms and WebSocket hibernation
onto workerd's Web API model. fetch is the HTTP seam, not durability itself;
RPC is another entry point. Persisted state survives eviction; JS call stacks do not.
For workflow-like workloads, these are complementary building blocks: one object can own a job or agent's state, persist progress, and schedule an alarm to continue later. Cloudflare's WebSocket hibernation can keep clients connected while the object is out of memory. This recovers an actor from stored state rather than restoring its running stack as dquickts does.4
%%{init: {"themeVariables": {"fontSize": "18px"}, "flowchart": {"htmlLabels": false, "curve": "linear", "nodeSpacing": 24, "rankSpacing": 32}}}%%
flowchart TB
C["Browser or server
fetch(request)"]
W["Worker
Request → Response"]
D["Durable Object / cell
Named fetch handler"]
S[("Private SQLite / KV
Survives eviction")]
E["Wake-up events
Alarm / WebSocket"]
C <-->|"Request / Response"| W
W <-->|"Object ID + stub.fetch"| D
D <-->|"read / write"| S
E -.->|wake| D
classDef handler fill:#f8fafc,stroke:#64748b,color:#0f172a
classDef actor fill:#ecfdf5,stroke:#059669,color:#0f172a
classDef saved fill:#eff6ff,stroke:#2563eb,color:#0f172a
class C,W,E handler
class D actor
class S savedcelld: provider-neutral deployment
celld brings the Workers/Durable Objects model to your infrastructure: V8 + per-cell SQLite + a shared object-storage bucket; compatibility gaps apply.
Following the Deno team's move to Cloudflare, the announced plan is to merge celld's distributed self-hosting code and ideas into workerd—not a completed integration.
Languages that compile to JavaScript
A language choice need not be a hosting choice
ClojureScript, ReScript and TypeScript compile to JavaScript rather than requiring
a separate production VM. Their interop/bindings expose fetch, streams and other
host APIs: choosing a language need not mean abandoning JavaScript's runtime ecosystem.
ClojureScript's data structures or ReScript's types do not change the
Request=/=Response boundary. Compatible output can target browsers, servers or
durable hosts. This is interface portability, not a guarantee that every bundle
runs unchanged: emitted JS, modules and dependencies must fit the host. DOM-only
code and Node-specific libraries remain platform constraints.
FastTwitch: ClojureScript maps, native Fetch
FastTwitch expresses HTTP applications as Ring-style request/response maps and
ordinary handler functions and middleware. Its adapters bridge those maps to
native Request=/=Response objects; its clients use native fetch, body streams
and request options. Node HTTP, Deno.serve and Bun.serve differ underneath,
but the application-facing handler contract stays consistent.
The practical benefit: reuse routing, authentication and application logic when changing runtimes instead of rebuilding the HTTP boundary. Runtime-specific features still require matching adapters and tests. ReScript can bind to the same Web APIs; FastTwitch is the ClojureScript example, not a requirement for other languages.
FastTwitch Celld: from HTTP handlers to named, stateful cells
Consider a chat application: give each room its own cell instead of coordinating all rooms inside one server process. FastTwitch Celld adds ClojureScript cell declarations, HTTP/RPC entry points, storage, alarms and WebSocket bindings. HTTP still uses the Fetch model; the platform routes each room to its named cell, and application code persists message history in that cell's SQLite/KV storage.
After eviction or restart, a new activation can reconstruct the room from that
stored history. An ordinary ClojureScript atom is not automatically durable;
neither is the call stack. This is Durable Objects-style state recovery, not
dquickts-style execution snapshots. The extension is not yet release-ready.
%%{init: {"themeVariables": {"fontSize": "18px"}, "flowchart": {"htmlLabels": false, "curve": "linear", "nodeSpacing": 24, "rankSpacing": 32}}}%%
flowchart TB
H["ClojureScript application
Handlers + middleware"]
F["FastTwitch
Native Fetch adapters"]
N["Node · Deno · Bun
HTTP application"]
X["FastTwitch Celld
Cell entry points"]
C["Cell: room-42
HTTP · RPC · events"]
S[("Private SQLite / KV
Message history")]
H --> F
F --> N
F -->|extend| X
X --> C
C <-->|"persist / restore"| S
classDef application fill:#f8fafc,stroke:#64748b,color:#0f172a
classDef bridge fill:#eff6ff,stroke:#2563eb,color:#0f172a
classDef durable fill:#ecfdf5,stroke:#059669,color:#0f172a
class H,N application
class F,X bridge
class C,S durableThe through-line: shared runtimes and Web APIs make this a common application model, not a separate networking stack for every language or host. Workflow journals preserve completed work, runtime snapshots preserve execution state, and durable objects preserve an addressable unit's data and lifecycle. These are different ways to let modern workloads survive beyond a single request or process.
References
- Event description
- Fetch API
- Web APIs: Node.js, Deno, Bun
- Engines: QuickJS, dquickts
- workerd; Workers Web APIs
- Durable Objects; Workflow sleep/retries
- celld; Compatibility
- Deno's transition; celld/workerd integration plan
- Languages: ClojureScript; ReScript
- FastTwitch: Documentation; Source; Celld extension

