Workflows
Build durable multi-step processes in your Next.js app with Ploy workflows.
Workflows
Build long-running, multi-step processes that survive failures with automatic retries and state persistence.
Configuration
Add a workflow binding in your ploy.yaml:
kind: dynamic
build: pnpm build
out: dist
workflow:
DATA_FLOW: data_processingThe key (DATA_FLOW) is the binding name. The value (data_processing) must match the workflow function name in your code.
Generate types:
pnpm ploy typesTriggering Workflows
Trigger workflows from API routes or Server Actions:
import { NextResponse } from "next/server";
import { getPloyContext } from "@meetploy/nextjs";
export async function POST(request: Request) {
const { env } = getPloyContext();
const body = await request.json();
const { executionId } = await env.DATA_FLOW.trigger({
action: body.action,
value: body.value,
});
return NextResponse.json({ success: true, executionId });
}Get Execution Status
export async function GET(request: Request) {
const { env } = getPloyContext();
const url = new URL(request.url);
const executionId = url.searchParams.get("executionId");
const execution = await env.DATA_FLOW.getExecution(executionId);
return NextResponse.json({
id: execution.id,
status: execution.status, // "running" | "completed" | "failed"
output: execution.output,
});
}Cancel Execution
await env.DATA_FLOW.cancel(executionId);Workflow Definition
Define workflows in your ploy.ts file:
interface WorkflowInput {
action: string;
value: number;
}
interface WorkflowOutput {
action: string;
originalValue: number;
processedValue: number;
}
export default {
workflows: {
async data_processing({
input,
env,
step,
}: WorkflowContext<PloyEnv, WorkflowInput>): Promise<WorkflowOutput> {
// Step 1: Validate input
const validation = await step.run("validate", async () => {
if (!input.action) {
throw new Error("Missing action");
}
return { valid: true, action: input.action };
});
// Step 2: Process the value
const processed = await step.run("process", async () => {
let result: number;
switch (input.action) {
case "double":
result = input.value * 2;
break;
case "square":
result = input.value * input.value;
break;
default:
result = input.value;
}
return { processedValue: result };
});
// Step 3: Store result
await step.run("store", async () => {
await env.DB.prepare(
"INSERT INTO results (action, input, output) VALUES (?, ?, ?)",
)
.bind(input.action, input.value, processed.processedValue)
.run();
});
return {
action: input.action,
originalValue: input.value,
processedValue: processed.processedValue,
};
},
},
} satisfies Ploy;Step API
step.run
Execute a named step. Results are persisted, so re-runs skip completed steps:
const result = await step.run("step-name", async () => {
return { data: "value" };
});step.sleep
Pause execution for a duration (in milliseconds):
await step.sleep(5000); // Wait 5 secondsSteps that throw are automatically retried. Use unique step names to ensure idempotency.
Local Development
During development, the local Ploy dashboard at http://localhost:4000 lets you:
- View workflow executions
- Inspect step results
- Monitor execution status
Next Steps
How is this guide?
Last updated on