Getting Started
Install Ryte and have a working workflow in under 2 minutes.
Installation
Ryte has no required peer dependencies — pick whichever Standard Schema validator you prefer:
# Zod
pnpm add @rytejs/core zod
# Valibot
pnpm add @rytejs/core valibot
# ArkType
pnpm add @rytejs/core arktypeThe examples in this guide use Zod, but the same DSL works identically with any Standard Schema validator. See the README for a Valibot example.
Define a Workflow
A workflow has states (schemas), commands (intents that trigger logic), events (side effects), and errors (typed domain failures).
const taskWorkflow = defineWorkflow("task", {
states: {
Todo: z.object({ title: z.string(), assignee: z.string().optional() }),
Done: z.object({ title: z.string(), completedAt: z.coerce.date() }),
},
commands: {
Complete: z.object({}),
},
events: {
TaskCompleted: z.object({ taskId: z.string() }),
},
errors: {
NotAssigned: z.object({}),
},
});All four config keys — states, commands, events, errors — are required. Errors define your domain failures upfront so they're part of the contract, not hidden inside handlers.
Create a Router and Handle Commands
All methods return this, so you can chain .state() and .on() calls fluently:
const router = new WorkflowRouter(taskWorkflow).state("Todo", ({ on }) => {
on("Complete", ({ data, error, transition, emit, workflow }) => {
if (!data.assignee) {
error("NotAssigned", {});
}
transition("Done", {
title: data.title,
completedAt: new Date(),
});
emit("TaskCompleted", { taskId: workflow.id });
});
});error() halts execution and rolls back all mutations. The error code and data are validated against the schema you defined.
Dispatch and Check the Result
(async () => {
const task = taskWorkflow.createWorkflow("task-1", {
initialState: "Todo",
data: { title: "Read the docs", assignee: "alice" },
});
const result = await router.dispatch(task, "Complete", {});
if (result.ok) {
console.log(result.workflow.state); // "Done"
console.log(result.events[0]?.type); // "TaskCompleted"
} else if (result.error.category === "domain") {
console.log(result.error.code); // "NotAssigned"
}
})();result.ok is true when the command succeeds. The returned workflow is the updated snapshot, and events contains all events emitted during the dispatch. When result.ok is false, result.error tells you what went wrong — validation failure, domain error, or missing handler.
Next Steps
- Concepts — understand the mental model
- Defining Workflows — schemas, states, commands in depth
- Routing Commands — handlers, wildcards, multi-state