Getting Started
Create your first Ablauf workflow in minutes. No external infrastructure required.
Getting Started
This guide walks you through creating your first durable workflow with Ablauf. By the end, you'll have a working Cloudflare Worker that runs workflows on the edge.
Prerequisites
Before you start, make sure you have:
- A Cloudflare Workers account (free tier works great)
- Node.js 18+ installed
wranglerCLI installed (npm install -g wrangler)
New to Cloudflare Workers? Check out their getting started guide first.
Installation
Install Ablauf alongside Hono and Zod:
npm install @der-ablauf/workflows hono zod@der-ablauf/workflows— The workflow enginehono— Lightweight web framework for Workerszod— Runtime validation (used internally; you can also usez.inferfor type extraction)
Build Your First Workflow
Define a Workflow
Create a simple greeting workflow using the defineWorkflow() API. This is the recommended way to define workflows—less boilerplate than classes, full type safety.
import { defineWorkflow } from '@der-ablauf/workflows';
export const GreetingWorkflow = defineWorkflow((t) => ({
type: 'greeting',
input: t.object({
name: t.string(),
}),
run: async (step, payload) => {
// This step is durable—if the worker crashes, it replays with cached results
const message = await step.do('create-greeting', async () => {
return `Hello, ${payload.name}! Welcome aboard.`;
});
// Simulate some work
await step.sleep('wait-a-moment', '2s');
// Return the final result
return { message, completedAt: new Date().toISOString() };
},
}));The step.do() block ensures the greeting is generated once and cached. If the workflow restarts, it won't regenerate the message—it'll use the stored result.
Set Up the Worker
Create a Hono app and initialize Ablauf with your workflow. The Ablauf class connects to your Durable Object binding and registers your workflows.
import { Hono } from 'hono';
import { Ablauf } from '@der-ablauf/workflows';
import { env } from 'cloudflare:workers';
import { GreetingWorkflow } from './workflows/greeting';
// Initialize Ablauf with the WORKFLOW_RUNNER binding
const ablauf = new Ablauf(env.WORKFLOW_RUNNER, {
workflows: [GreetingWorkflow],
});
const app = new Hono();
// Create a new greeting workflow
app.post('/greet', async (c) => {
const { name } = await c.req.json();
const workflow = await ablauf.create(GreetingWorkflow, {
id: crypto.randomUUID(),
payload: { name },
});
const status = await workflow.getStatus();
return c.json(status);
});
// Get workflow status
app.get('/workflows/:id', async (c) => {
const id = c.req.param('id');
const workflow = ablauf.get(GreetingWorkflow, { id });
const status = await workflow.getStatus();
return c.json(status);
});
// Export the Worker and Durable Object class
export default { fetch: app.fetch };
export const WorkflowRunner = ablauf.createWorkflowRunner();The createWorkflowRunner() call must be exported as WorkflowRunner to match your Durable Object binding name in wrangler.jsonc.
Configure Wrangler
Tell Cloudflare about your Durable Object. This is what makes your workflows durable—the WorkflowRunner class gets its own SQLite database.
{
"name": "my-ablauf-app",
"main": "src/index.ts",
"compatibility_date": "2025-01-01",
"compatibility_flags": ["nodejs_compat"],
"rules": [
{
"type": "Text",
"globs": ["**/*.sql"],
"fallthrough": true,
},
],
"durable_objects": {
"bindings": [
{
"class_name": "WorkflowRunner",
"name": "WORKFLOW_RUNNER",
},
],
},
"migrations": [
{
"new_sqlite_classes": ["WorkflowRunner"],
"tag": "v1",
},
],
}Key fields:
rules— Tells wrangler to bundle.sqlfiles as text modules. Ablauf uses SQL migrations internally, and without this rule wrangler won't know how to handle them.durable_objects.bindings— Registers the Durable Object class and binding namemigrations— Enables SQLite storage for the Durable Object (required for Ablauf)compatibility_flags— Enables Node.js compatibility for crypto and other APIs
Run Locally
Start the local development server:
npx wrangler devWrangler will start a local server (usually on http://localhost:8787). Your Durable Objects run in a local environment with real SQLite storage.
Test Your Workflow
Create a workflow:
curl -X POST http://localhost:8787/greet \
-H "Content-Type: application/json" \
-d '{"name": "Alice"}'Response:
{
"id": "some-uuid",
"type": "greeting",
"status": "running",
"payload": { "name": "Alice" },
"createdAt": "2025-02-14T12:00:00.000Z"
}Wait a couple seconds, then check the status:
curl http://localhost:8787/workflows/some-uuidResponse:
{
"id": "some-uuid",
"type": "greeting",
"status": "completed",
"payload": { "name": "Alice" },
"result": {
"message": "Hello, Alice! Welcome aboard.",
"completedAt": "2025-02-14T12:00:02.000Z"
},
"createdAt": "2025-02-14T12:00:00.000Z",
"completedAt": "2025-02-14T12:00:02.000Z"
}Congratulations! You've just run a durable workflow on the edge.
What Just Happened?
Let's break down what Ablauf did behind the scenes:
- Created a workflow instance — Your workflow got a unique ID and its payload was validated against the Zod schema.
- Executed the first step — The
create-greetingstep ran and its result was saved to SQLite. - Slept for 2 seconds — Ablauf set a Durable Object alarm for 2 seconds in the future, then released resources.
- Woke up and replayed — After 2 seconds, the alarm fired. Ablauf replayed the workflow, used the cached greeting, and completed.
If your worker had crashed during the sleep, the workflow would have resumed automatically. That's the magic of durable execution.
What's Next?
Now that you've built your first workflow, explore the full power of Ablauf:
- Workflows — Learn
defineWorkflow(), typed events, and SSE updates - Steps — Master
step.do()retry policies,step.sleep()/step.sleepUntil()timing, andstep.waitForEvent()timeouts - Events — Send external events to running workflows (webhooks, user actions, etc.)
- Dashboard — Monitor and debug your workflows in real-time
Stuck? Join our Discord or open an issue on GitHub. We're here to help.