SDK Reference
The Duraflow SDK provides a type-safe way to define workflows and interact with the engine.
Installation
npm install @duraflow/sdk @duraflow/protoCore Functions
workflow<TInput, TOutput>(name, handler)
Defines a new workflow with a unique name.
import { workflow } from '@duraflow/sdk';
interface MyInput {
userId: string;
}
interface MyOutput {
result: string;
}
const myWorkflow = workflow<MyInput, MyOutput>('my-workflow', async (ctx) => {
// ctx.input is typed as MyInput
const { userId } = ctx.input;
const result = await ctx.step.run('process', async () => {
return `Processed user ${userId}`;
});
return { result };
});Parameters:
| Parameter | Type | Description |
|---|---|---|
name | string | Unique workflow name (alphanumeric, dashes, underscores, max 100 chars) |
handler | (ctx: WorkflowContext) => Promise<TOutput> | Async function that executes the workflow |
options | WorkflowOptions? | Optional - { compensations } map keyed by step key (see WorkflowOptions) |
Returns: Workflow<TOutput>
ctx.step.run<T>(name, fn, options)
Executes a step within a workflow.
const result = await ctx.step.run<string>(
'my-step',
async () => {
return 'step output';
},
{
retries: 3,
timeout: 30000,
},
);Compensations are not a step option. They are declared on the workflow via the
compensationsmap (seeWorkflowOptions).
Parameters:
| Parameter | Type | Description |
|---|---|---|
name | string | Unique step name within the workflow |
fn | () => Promise<T> | Async function that executes the step |
options | StepOptions<T>? | Optional configuration |
Returns: Promise<T> - The step's output
Types
WorkflowContext
interface WorkflowContext<TInput = unknown> {
// Unique identifier for this workflow run
runId: string;
// The workflow's name
workflowName: string;
// Input data passed to the workflow
input: TInput;
// Step runner for executing steps
step: StepRunner;
}StepRunner
interface StepRunner {
run<T>(name: string, fn: () => Promise<T>, options?: StepOptions<T>): Promise<T>;
}StepOptions
interface StepOptions<T = unknown> {
// Number of retry attempts on failure (default: 0)
retries?: number;
// Timeout in milliseconds (default: no timeout)
timeout?: number;
// Optional rate-limit gate for this step
rateLimit?: RateLimitOptions;
}WorkflowOptions
interface WorkflowOptions {
// Compensations keyed by step key. Each is a PURE function of that step's
// SAVED output (no closure over handler state). Registered at module load so
// a rollback in any process can resolve them; run in LIFO order on failure.
compensations?: Record<string, (output: unknown) => Promise<void>>;
}WorkflowHandler
type WorkflowHandler<TInput = unknown, TOutput = unknown> = (
ctx: WorkflowContext<TInput>,
) => Promise<TOutput>;Compensation
registerCompensation(name, fn)
Low-level API to register a compensation by its full ${workflowName}:${stepKey} key. Most code should use the compensations map on workflow(...) instead - it registers for you. Use this directly only if you need a stable, hand-managed key.
import { registerCompensation } from '@duraflow/sdk';
registerCompensation('workflow:step-name', async (output) => {
await api.cancel(output.id);
});compensationRegistry.get(name)
Retrieve a registered compensation.
import { compensationRegistry } from '@duraflow/sdk';
const compensation = compensationRegistry.get('workflow:step-name');Serialization
serialize(value)
Serialize a value to JSON string (uses SuperJSON for type preservation).
import { serialize, deserialize } from '@duraflow/sdk';
const json = serialize({ date: new Date(), map: new Map([['key', 'value']]) });
// SuperJSON preserves types that regular JSON.stringify loses
const data = deserialize<MyType>(json);Note: Maximum payload size is 1MB. Throws SerializationError if exceeded.
deserialize<T>(json)
Deserialize a JSON string back to a value.
const value = deserialize<MyType>(jsonString);SerializationError
import { SerializationError } from '@duraflow/sdk';
try {
serialize(veryLargeObject);
} catch (e) {
if (e instanceof SerializationError) {
console.log('Payload too large:', e.message);
}
}Task Status
import { taskStatus } from '@duraflow/sdk';
// Enum values
taskStatus.PENDING; // Waiting in queue
taskStatus.RUNNING; // Currently executing
taskStatus.COMPLETED; // Finished successfully
taskStatus.FAILED; // Failed with error
taskStatus.CANCELLED; // Manually cancelled
taskStatus.ROLLED_BACK; // All compensations succeeded
taskStatus.PARTIAL_ROLLBACK; // Some compensations failedError Handling
StepRetryError
Thrown when a step needs to be retried (handled automatically by the SDK).
import { StepRetryError } from '@duraflow/sdk';
// This is thrown internally during retry
// You don't typically need to handle it directlyComplete Example
import {
workflow,
step,
taskStatus,
registerCompensation,
serialize,
deserialize,
} from '@duraflow/sdk';
// Define input/output types
interface OrderInput {
orderId: string;
customerEmail: string;
}
interface OrderOutput {
confirmationNumber: string;
}
// Define the workflow
const orderWorkflow = workflow<OrderInput, OrderOutput>('process-order', async (ctx) => {
// Step 1: Validate order
await ctx.step.run('validate', async () => {
if (!ctx.input.customerEmail.includes('@')) {
throw new Error('Invalid email');
}
return { valid: true };
});
// Step 2: Process with retry
const result = await ctx.step.run(
'process',
async () => {
// Simulate processing
return {
confirmationNumber: 'CONF-' + Date.now(),
};
},
{
retries: 3,
timeout: 30000,
},
);
return result;
});
// Export for use in engine
export { orderWorkflow };Loading Workflows into the Engine
The engine loads your workflow modules by path, via the DURAFLOW_WORKFLOWS env var (comma-separated if you have more than one file):
export const myWorkflow = workflow("my-workflow", async (ctx) => {
// ...
});DURAFLOW_WORKFLOWS=./path/to/workflows.ts npm run devImporting the file is enough - workflow() registers itself, there's no separate registration step to wire up.