Skip to main content
@restatedev/restate-sdk-gen is a preview package available since SDK v1.15.0. Its API may change in future releases. Feedback is welcome.
@restatedev/restate-sdk-gen is a composable, generator-based API for authoring Restate handlers. It builds on the standard @restatedev/restate-sdk and adds:
  • Concurrent durable tasks within a single handler, via the spawn API.
  • Fewer non-determinism pitfalls, because generators make sequencing explicit.
  • No Context to thread around: every Restate operation is a free function imported from the package.

Installation

Quick start

A handler using restate-sdk-gen wraps its body in gen(function*() { ... }) and calls execute(ctx, op):
Inside the gen body, the free functions (run, sleep, all, …) read the active scheduler from a synchronous slot — no ctx parameter to pass down.

Core concepts

Operations and Futures

  • gen(factory) — wraps a generator-body factory into an Operation<T>. Pass a factory function (() => Generator<...>), not a generator instance. The type prevents the reuse-after-exhausted trap.
  • Future<T> — an eager, memoized handle to an eventual result. Returned by run, sleep, awakeable, spawn, etc. Yielding a Future twice returns the same memoized value.

run — durable side effects

Journal-entry names must be deterministic — the same value on every replay. Do not use Date.now(), Math.random(), or any non-deterministic value in a name.

Sequential work

Concurrent work

Spawning concurrent routines

spawn registers an Operation as a separate concurrent routine and returns a Task<T>.
Spawn vs. combinators:
  • Use run + all/race for a flat set of side effects.
  • Use spawn when each unit is a multi-step sub-workflow with its own logic, branches, or retries.

Spawn lifetime

By default (onMainExit: "abandon"), execute returns as soon as the main operation settles. Any spawned routine still running is abandoned — it is never resumed and its finally blocks do not run. Fire-and-forget spawns are not guaranteed to complete. To ensure a spawned routine completes, yield* its future before returning, or pass { onMainExit: "join" }:

Interrupting a task

Interrupt cascades down the spawn subtree — every routine the task spawned is interrupted with the same error.

Cooperative cancellation

Use a Channel<void> to signal a running routine to stop:
Channels are single-shot and typed. The stop.receive Future is the same object on every access — once sent, every subsequent select takes the stop branch immediately.

Context-local storage

contextLocal() provides ambient, in-memory storage scoped to the current invocation and shared by every routine under it. Set it once near the top, read it anywhere downstream:
contextLocal is in-memory only — not durable. For state that must outlive the invocation, use state() / sharedState().

Timers and awakeables

Calling other services

Common pitfalls

Free functions (sleep, run, all, …) must be called inside a gen(function*() { ... }) body, not at module init or inside async callbacks.

See also