Restate Durable Execution Framework
Restate is a durable execution framework that makes applications resilient to failures. Use this skill when building:
-
Durable workflows with automatic retries
-
Services with persisted state (Virtual Objects)
-
Microservice orchestration with transactional guarantees
-
Event processing with exactly-once semantics
-
Long-running tasks that survive crashes
When to Use Restate
Use Restate when:
-
Building workflows that must complete despite failures
-
Need automatic retry and recovery without manual retry logic
-
Building stateful services (shopping carts, user sessions, payment processing)
-
Orchestrating multiple services with saga/compensation patterns
-
Processing events with exactly-once delivery guarantees
-
Scheduling durable timers and cron jobs
Core Concepts
Service Types
Restate supports three service types:
Services - Stateless handlers with durable execution
-
Use for: microservice orchestration, sagas, idempotent requests
Virtual Objects - Stateful handlers with K/V state isolated per key
-
Use for: entities (shopping cart), state machines, actors, stateful event processing
-
Only one handler runs at a time per object key (consistency guarantee)
Workflows - Special Virtual Objects where run handler executes exactly once
- Use for: order processing, human-in-the-loop, long-running provisioning
See Services Concepts for detailed comparison.
Durable Building Blocks
Restate provides these building blocks through the SDK context:
-
Journaled actions (ctx.run() ) - Persist results of side effects
-
State (ctx.get/set/clear ) - K/V state for Virtual Objects
-
Timers (ctx.sleep() ) - Durable sleep that survives restarts
-
Service calls (ctx.serviceClient() ) - RPC with automatic retries
-
Awakeables - Wait for external events/signals
See Durable Building Blocks.
TypeScript SDK Quick Reference
Installation
npm install @restatedev/restate-sdk
Basic Service
import * as restate from "@restatedev/restate-sdk";
const myService = restate.service({ name: "MyService", handlers: { greet: async (ctx: restate.Context, name: string) => { return "Hello, " + name + "!"; }, }, });
restate.endpoint().bind(myService).listen(9080);
Virtual Object (Stateful)
const counter = restate.object({ name: "Counter", handlers: { add: async (ctx: restate.ObjectContext, value: number) => { const current = (await ctx.get<number>("count")) ?? 0; ctx.set("count", current + value); return current + value; }, get: restate.handlers.object.shared( async (ctx: restate.ObjectSharedContext) => { return (await ctx.get<number>("count")) ?? 0; } ), }, });
Workflow
const paymentWorkflow = restate.workflow({ name: "PaymentWorkflow", handlers: { run: async (ctx: restate.WorkflowContext, payment: Payment) => { // Step 1: Reserve funds const reservation = await ctx.run("reserve", () => reserveFunds(payment) );
// Step 2: Wait for approval (awakeable)
const approved = await ctx.promise<boolean>("approval");
if (!approved) {
await ctx.run("cancel", () => cancelReservation(reservation));
return { status: "cancelled" };
}
// Step 3: Complete payment
await ctx.run("complete", () => completePayment(reservation));
return { status: "completed" };
},
approve: async (ctx: restate.WorkflowSharedContext) => {
ctx.promise<boolean>("approval").resolve(true);
},
reject: async (ctx: restate.WorkflowSharedContext) => {
ctx.promise<boolean>("approval").resolve(false);
},
}, });
Key SDK Patterns
// Journaled action - result persisted, replayed on retry const result = await ctx.run("action-name", async () => { return await callExternalApi(); });
// Durable timer - survives restarts await ctx.sleep(60_000); // 60 seconds
// Call another service const client = ctx.serviceClient(OtherService); const response = await client.handler(input);
// Async call (fire and forget) ctx.serviceSendClient(OtherService).handler(input);
// Delayed call ctx.serviceSendClient(OtherService, { delay: 60_000 }).handler(input);
// Awakeable - wait for external signal
const { id, promise } = ctx.awakeable<string>();
// Give id to external system, then:
const result = await promise;
// Random (deterministic) const value = ctx.rand.random(); const uuid = ctx.rand.uuidv4();
Running Locally
- Start Restate server:
npx @restatedev/restate-server
- Run your service:
npx ts-node src/app.ts
- Register service with Restate:
npx @restatedev/restate deployments register http://localhost:9080
- Invoke handlers via HTTP:
Service handler
curl localhost:8080/MyService/greet -H 'content-type: application/json' -d '"World"'
Virtual Object handler (with key)
curl localhost:8080/Counter/user123/add -H 'content-type: application/json' -d '5'
Start workflow
curl localhost:8080/PaymentWorkflow/order-456/run -H 'content-type: application/json' -d '{"amount": 100}'
Documentation References
Concepts
-
Services - Service types and use cases
-
Invocations - How invocations work
-
Durable Execution - Execution guarantees
-
Durable Building Blocks - SDK primitives
TypeScript SDK
-
Overview - SDK setup and service definitions
-
State - K/V state management
-
Journaling Results - Side effects and ctx.run()
-
Durable Timers - Sleep and scheduling
-
Service Communication - Calling other services
-
Awakeables - External events
-
Workflows - Workflow implementation
-
Error Handling - Error patterns
-
Serving - Running services
-
Testing - Testing strategies
-
Clients - Client SDK
Guides
-
Error Handling - Comprehensive error handling
-
Sagas - Saga pattern with compensations
-
Cron Jobs - Scheduled tasks
-
Parallelizing Work - Fan-out patterns
-
Databases - Database integration
-
Lambda Deployment - AWS Lambda deployment
Use Cases
-
Workflows
-
Async Tasks
-
Event Processing
-
Microservice Orchestration
Operations
-
HTTP Invocation
-
Service Registration
-
Versioning
-
Architecture
Important Guidelines
Side effects must be wrapped in ctx.run()
-
External calls, random values, timestamps must go through the context to be journaled and replayed correctly.
State access only in Virtual Objects/Workflows - Plain Services don't have state access.
Handlers must be deterministic - Same inputs should produce same outputs. Use ctx.rand for randomness.
One handler per Virtual Object key at a time - Restate ensures consistency by queuing concurrent requests to the same key.
Workflows run handler executes exactly once - Use other handlers to query/signal the workflow.
Register services after code changes - Run restate deployments register to update handler definitions.
Generated from Restate documentation. Run scripts/sync-docs.sh to update.
License
The content in the references/ directory is derived from the Restate documentation and TypeScript SDK.