Workflows-as-code
Lightweight, flexible, durable.
Combine the resiliency of workflows with the speed and flexibility of regular functions.
Restate orchestrates and manages their execution till completion, whether it’s millis or months.
Just code
Low latency
Deploy on FaaS
Workflows with Restate
Implement the run
function of your workflow, using the Restate SDK.
- TypeScript
- Java
- Kotlin
Run once, idempotently
A workflow runs exactly one time. Restate makes sure that duplicate requests do not lead to duplicate execution.
Persist progress
Store results of intermediate steps, interaction with external systems, or non-deterministic actions. Restate makes sure that on retries, the code does not get re-executed and the previous result is returned. This lets you execute the steps of your workflows durably.
Workflow state
Use Restate’s built-in key-value store to store workflow state. Restate guarantees that it is consistent and persistent, since state updates are tracked together with the rest of the execution progress.
Query workflow state
Retrieve the current state of the workflow from within other handlers and expose it to external clients.
Wait on external signals
Make Promises/Futures resilient by registering them in Restate. Share them and wait until other functions resolve them.
Signal in-flight workflows
Notify the workflow of external signals, callbacks or Kafka events. Resolve or reject shared promises on which the workflow is waiting. The workflow handles the outcome.
Flexible failure handling
Implement sagas and compensations in code, as per your requirements.
const signUpWorkflow = restate.workflow({name: "sign-up-workflow",handlers: {run: async (ctx: WorkflowContext, user: User) => {const { id, name, email } = user;ctx.set("stage", "Creating User");await ctx.run(() => createUserEntry({ id, name }));ctx.set("stage", "Email Verification");const secret = ctx.rand.uuidv4();await ctx.run(() => sendEmailWithLink({ email, secret }));const clickSecret = await ctx.promise<string>("email-link");if (clickSecret !== secret) {ctx.set("stage", `Verification failed`);throw new TerminalError("Wrong secret from email link");}ctx.set("stage", "User verified");return true;},getStage: (ctx: WorkflowSharedContext) => ctx.get("stage"),approveEmail: (ctx: WorkflowSharedContext, secret: string) =>ctx.promise<string>("email-link").resolve(secret),rejectEmail: (ctx: WorkflowSharedContext) =>ctx.promise<string>("email-link").reject("Abort verification"),},});
Run once, idempotently
A workflow runs exactly one time. Restate makes sure that duplicate requests do not lead to duplicate execution.
Persist progress
Store results of intermediate steps, interaction with external systems, or non-deterministic actions. Restate makes sure that on retries, the code does not get re-executed and the previous result is returned. This lets you execute the steps of your workflows durably.
Workflow state
Use Restate’s built-in key-value store to store workflow state. Restate guarantees that it is consistent and persistent, since state updates are tracked together with the rest of the execution progress.
Query workflow state
Retrieve the current state of the workflow from within other handlers and expose it to external clients.
Wait on external signals
Make Promises/Futures resilient by registering them in Restate. Share them and wait until other functions resolve them.
Signal in-flight workflows
Notify the workflow of external signals, callbacks or Kafka events. Resolve or reject shared promises on which the workflow is waiting. The workflow handles the outcome.
Flexible failure handling
Implement sagas and compensations in code, as per your requirements.
@Workflowpublic class SignupWorkflow {private final StateKey<String> STAGE = StateKey.of("stage", JsonSerdes.STRING);private final DurablePromiseKey<String> EMAIL_LINK =DurablePromiseKey.of("email-link", JsonSerdes.STRING);@Workflowpublic boolean run(WorkflowContext ctx, User user) {ctx.set(STAGE, "Creating user");ctx.run(() -> createUserEntry(ctx.key(), user.getName()));ctx.set(STAGE, "Email verification");String secret = ctx.random().nextUUID().toString();ctx.run(() -> sendEmailWithLink(user.getEmail(), secret));String clickSecret = ctx.promise(EMAIL_LINK).awaitable().await();if (!clickSecret.equals(secret)) {ctx.set(STAGE, "Verification failed");throw new TerminalException("Wrong secret from email link");}ctx.set(STAGE, "User verified");return true;}@Handlerpublic String getStage(SharedWorkflowContext ctx) {return ctx.get(STAGE).orElse("Unknown");}@Handlerpublic void approveEmail(SharedWorkflowContext ctx, String secret) {ctx.promiseHandle(EMAIL_LINK).resolve(secret);}@Handlerpublic void rejectEmail(SharedWorkflowContext ctx) {ctx.promiseHandle(EMAIL_LINK).reject("Abort verification");}}
Run once, idempotently
A workflow runs exactly one time. Restate makes sure that duplicate requests do not lead to duplicate execution.
Persist progress
Store results of intermediate steps, interaction with external systems, or non-deterministic actions. Restate makes sure that on retries, the code does not get re-executed and the previous result is returned. This lets you execute the steps of your workflows durably.
Workflow state
Use Restate’s built-in key-value store to store workflow state. Restate guarantees that it is consistent and persistent, since state updates are tracked together with the rest of the execution progress.
Query workflow state
Retrieve the current state of the workflow from within other handlers and expose it to external clients.
Wait on external signals
Make Promises/Futures resilient by registering them in Restate. Share them and wait until other functions resolve them.
Signal in-flight workflows
Notify the workflow of external signals, callbacks or Kafka events. Resolve or reject shared promises on which the workflow is waiting. The workflow handles the outcome.
Flexible failure handling
Implement sagas and compensations in code, as per your requirements.
@Workflowclass SignupWorkflow {companion object {private val STAGE = KtStateKey.json<String>("stage")private val EMAIL_LINK = KtDurablePromiseKey.json<String>("email-link")}@Workflowsuspend fun run(ctx: WorkflowContext, user: User): Boolean {ctx.set(STAGE, "Creating user")ctx.runBlock { createUserEntry(ctx.key(), user.name) }ctx.set(STAGE, "Email verification")val secret = ctx.random().nextUUID().toString()ctx.runBlock { sendEmailWithLink(user.email, secret) }val clickSecret = ctx.promise(EMAIL_LINK).awaitable().await()if (clickSecret != secret) {ctx.set(STAGE, "Verification failed")throw TerminalException("Wrong secret from email link")}ctx.set(STAGE, "User verified")return true}@Handlersuspend fun getStage(ctx: SharedWorkflowContext): String? {return ctx.get(STAGE)}@Handlersuspend fun approveEmail(ctx: SharedWorkflowContext, secret: String) {ctx.promiseHandle(EMAIL_LINK).resolve(secret)}@Handlersuspend fun rejectEmail(ctx: SharedWorkflowContext) {ctx.promiseHandle(EMAIL_LINK).reject("Abort verification")}}
LOW-LATENCY
Restate’s event-driven foundation built in Rust lets you put workflows in the latency-sensitive path of user interaction.
DURABLE EXECUTION
Restate handles retries and recovers your code to the exact point before the crash. No more coarse per-step retries. State changes take part in durable execution, so the state never gets out of sync.
- TypeScript
- Java
- Kotlin
Latch on to a workflow
A workflow runs exactly one time. If the caller loses the connection to the workflow, he can latch on again to retrieve the result.
// import * as clients from "@restatedev/restate-sdk-clients";const rs = clients.connect({ url: "http://localhost:8080" });await rs.workflowClient<SignUpWorkflow>({ name: "sign-up-workflow" }, user.id).workflowSubmit(user);// do something else, with workflow running in the background// attach back to the workflowconst result = await rs.workflowClient<SignUpWorkflow>({ name: "sign-up-workflow" }, user.id).workflowAttach();
Latch on to a workflow
A workflow runs exactly one time. If the caller loses the connection to the workflow, he can latch on again to retrieve the result.
// import dev.restate.sdk.client.Client;Client rs = Client.connect("http://localhost:8080");SignupWorkflowClient.fromClient(rs, user.getId()).submit(user);// do something else, with workflow running in the background// attach back to the workflowboolean result =SignupWorkflowClient.fromClient(rs, user.getId()).workflowHandle().attach();
Latch on to a workflow
A workflow runs exactly one time. If the caller loses the connection to the workflow, he can latch on again to retrieve the result.
// import dev.restate.sdk.client.Client;val rs = Client.connect("http://localhost:8080")SignupWorkflowClient.fromClient(rs, user.id).submit(user)// do something else, with workflow running in the background// attach back to the workflowval result =SignupWorkflowClient.fromClient(rs, user.id).workflowHandle().attach()
Workflows as regular, lightweight functions
Restate’s workflow functionality is integrated in its core.
Workflows run like any other function in your infrastructure: on K8S, FaaS, or mix-and-match.
No need to spin up extra infrastructure or dedicated workers.
What you can build with Workflows and Restate
User sign-up workflow
Create user in the system, wait until email confirmation, schedule a reminder, send welcome email, etc.
Order processing and logistics
Handle the payment, request the order preparation, wait for driver acceptance callback, etc.
Infrastructure provisioning
Go through a set of steps to provision a setup. Retry until resources are up, handle timeouts, rollbacks, etc.
Workflow interpreters
Dynamically compose workflows based on user input. For example, an image transformer that lets users specify the steps to be taken.