Tasks
Tasks are the building blocks of workflows. Each task represents a single unit of work that can be executed and retried independently.
Defining a Task
import { task } from "@aikirun/workflow";
const sendEmail = task({
name: "send-email",
handler(input: { email: string; message: string }) {
// Your business logic
return sendEmailToUser(input.email, input.message);
},
});Task Properties
name
A unique identifier for the task. Use descriptive names like "send-email" or "process-payment".
handler Function
The function that performs the actual work. It receives:
input- Input data for the task
const processPayment = task({
name: "process-payment",
handler(input: { paymentId: string; amount: number }) {
console.log(`Processing payment for ${input.paymentId}`);
return processPaymentWithId(input.paymentId, input.amount);
},
});Executing Tasks
Tasks are executed within workflows using .start():
const orderWorkflowV1 = orderWorkflow.v("1.0.0", {
async handler(run, input: { orderData: any }) {
const validation = await validateOrder.start(run, {
orderData: input.orderData,
});
const payment = await processPayment.start(run, {
paymentId: validation.paymentId,
amount: validation.amount,
});
return { success: true };
},
});Task Retry
Configure automatic retries for failed tasks using the retry property:
const processPayment = task({
name: "process-payment",
handler(input: { paymentId: string; amount: number }) {
return paymentService.charge(input.paymentId, input.amount);
},
retry: {
type: "exponential",
maxAttempts: 3,
baseDelayMs: 1000,
},
});For available strategies and best practices, see the Retry Strategies Guide.
Schema Validation
Define schemas to validate task input and output at runtime:
import { z } from "zod";
const processPayment = task({
name: "process-payment",
schema: {
input: z.object({
paymentId: z.string(),
amount: z.number().positive(),
}),
output: z.object({
transactionId: z.string(),
status: z.enum(["success", "failed"]),
}),
},
handler(input) {
return paymentService.charge(input);
},
});Schemas work with any validation library that implements Standard Schema (Zod, Valibot, ArkType, etc.).
Why use output schemas? When task results are cached, the schema validates cached data on replay. If the cached shape doesn't match (e.g., after refactoring), the workflow fails immediately rather than silently returning mismatched data. See Refactoring Workflows.
Task Input
The task receives input directly:
const exampleTask = task({
name: "example",
handler(input: { data: string }) {
// input: Input data for this task
console.log("Task input:", input);
return { processed: true };
},
});Task Best Practices
- Keep tasks focused - One responsibility per task
- Make tasks idempotent - A task may re-execute after a crash; running it twice with the same input should be safe
- Put side effects in tasks - API calls, database writes, and anything non-deterministic belong in tasks, keeping the workflow handler deterministic
- Use meaningful names - Clear, descriptive task names
Next Steps
- Workflows - Learn about workflow orchestration
- Determinism - Workflow determinism and task idempotency
- Reference IDs - Custom identifiers for workflows and events
- Dependency Injection - Inject dependencies into tasks