Hello world — your first durable workflow

A minimal workflow that orchestrates two leaf functions, registered and invoked through Resonate.

Hello World banner

The smallest example that exercises the durable execution loop. A registered workflow function calls two leaf functions through ctx.run, and Resonate checkpoints each return value.

SDK versions

TypeScript: @resonatehq/sdk v0.10.1 (current). Python: resonate-sdk v0.6.x. Rust: v0.1.0, in active development.

The problem#

Before any "real" example makes sense, you need to see the wiring: a function gets registered, the workflow runtime invokes it, sub-calls go through a Context, and each step's result is checkpointed. Without that mental model, every more elaborate example reads as magic.

Resonate's solution#

Two things, both small. Register a function with the Resonate instance — that's how Resonate knows it can dispatch the function and durably checkpoint its result. Invoke it through the run/RPC API — that's how the function moves from "regular code" to "durable workflow." The rest of every example in this section is variations on those two moves.

Code walkthrough#

The workflow is a generator (TypeScript and Python) or #[resonate::function] async fn (Rust). It receives a Context and calls leaf functions through ctx.run. Leaves can be plain functions — Resonate handles the dispatch and the checkpointing.

index.ts
import { Resonate } from "@resonatehq/sdk";
import type { Context } from "@resonatehq/sdk";

const resonate = new Resonate();

async function bar(_: Context, greetee: string) {
  return `Hello ${greetee} from bar!`;
}

async function baz(_: Context, greetee: string) {
  return `Hello ${greetee} from baz!`;
}

function* foo(ctx: Context, greetee: string): Generator<any, string, any> {
  // Each ctx.run is a checkpoint — its return value is durably stored.
  const barGreeting = yield* ctx.run(bar, greetee);
  const bazGreeting = yield* ctx.run(baz, greetee);
  return `Hello ${greetee} from foo! ${barGreeting} ${bazGreeting}`;
}

const fooR = resonate.register("foo", foo);

const result = await fooR.run("greeting-workflow", "World");
console.log(result);
resonate.stop();

The TypeScript and Python examples invoke the workflow in-process — no server needed for this minimal demo. The Rust example registers the function with a long-running worker and waits for an invocation from the Resonate CLI.

Run it locally#

Clone the repo:

code
git clone https://github.com/resonatehq-examples/example-hello-world-ts
cd example-hello-world-ts
npm install

Run the workflow in-process:

code
npx tsx index.ts

You should see three log lines (running foo, running bar, running baz, possibly with foo repeating as the workflow replays after each yield) followed by the assembled greeting.

Try the recovery story (Rust)#

With the worker running in Rust, kill it mid-execution and restart. Resonate replays the workflow from the last checkpointed step rather than from the beginning. resonate tree greet-1 renders the durable call graph as it grows.