Windmill supports a 'Workflow-as-Code' pattern where you define complex logic using TypeScript. To ensure reliability during replays (e.g., after a failure or suspension), you must use step() to checkpoint results.
Core Concepts
workflow(fn): Marks an async function as a workflow entry point. The function must be deterministic. Any external state (time, random, API calls) must be wrapped in a step().step(name, fn): Executes fn and checkpoints the result. On a replay, the cached JSON-encoded result is returned instead of re-running fn.taskScript(path, options): Creates a task that dispatches to a separate Windmill script.taskFlow(path, options): Creates a task that dispatches to a separate Windmill flow.parallel(items, fn, options): Processes items in parallel with optional concurrency control.
Example Workflow
import * as wmill from 'windmill-client'
wmill.workflow(async () => {
const data = await wmill.step("fetch-data", async () => {
// This external call is checkpointed
return await someExternalApiCall();
});
const processed = await wmill.step("process", async () => {
return data.map(item => item.id);
});
const pipeline = wmill.taskFlow("f/etl/pipeline");
await pipeline({ input: processed });
});
import * as wmill from 'windmill-client'
wmill.workflow(async () => {
const data = await wmill.step("fetch-data", async () => {
return await someExternalApiCall();
});
const processed = await wmill.step("process", async () => {
return data.map(item => item.id);
});
const pipeline = wmill.taskFlow("f/etl/pipeline");
await pipeline({ input: processed });
});