Skip to main content

workflow

Build a durable multi-step pipeline on Convex where each step runs in order and is retried independently on failure (transcribe→summarize→email, ETL, order fulfillment, any 'do A then B then C, retry each' job). Use @convex-dev/workflow — do NOT hand-roll a chain of scheduler calls or a custom jobs table. TRIGGER on multi-step / pipeline / 'retry each step' / long-running orchestration requests.

跳到安装

来源信息

仓库
get-convex/convex-backend-skill
最近来源活动
2026年7月22日 23:18
检测到的 SKILL.md 语言
英语
星标
6
分支
8

安装方式

默认使用会先检查来源的 Prompt;你也可以切换为直接命令,或下载本地副本。

检查来源文件

决定是否安装前,请先阅读 SKILL.md,以及 SkillsMP 当前展示的配套文件。

正在显示 SKILL.md

SKILL.md
来源说明 · 只读预览
name
workflow
description
Build a durable multi-step pipeline on Convex where each step runs in order and is retried independently on failure (transcribe→summarize→email, ETL, order fulfillment, any 'do A then B then C, retry each' job). Use @convex-dev/workflow — do NOT hand-roll a chain of scheduler calls or a custom jobs table. TRIGGER on multi-step / pipeline / 'retry each step' / long-running orchestration requests.
# Durable multi-step workflows → `@convex-dev/workflow` When the task is "do step A, then B, then C, and retry each step independently if it fails" — a pipeline, ETL, or orchestration that must survive crashes — use the **workflow component**. Do NOT hand-roll it with a `jobs` table + chained `ctx.scheduler.runAfter` calls: that reinvents durability, loses per-step retry/backoff, and (measured) scores *worse* than a plain implementation. Copy this pattern. ## Wire the component ```ts // convex/convex.config.ts import { defineApp } from "convex/server"; import workflow from "@convex-dev/workflow/convex.config"; const app = defineApp(); app.use(workflow); export default app; ``` ## Define the workflow — one `step.run*` call per stage, retried independently ```ts // convex/workflows.ts import { WorkflowManager } from "@convex-dev/workflow"; import { components, internal } from "./_generated/api"; import { v } from "convex/values"; export const workflow = new WorkflowManager(components.workflow, { // Per-step default: retry each failed step independently with backoff. defaultRetryBehavior: { maxAttempts: 4, initialBackoffMs: 1000, base: 2 }, retryActionsByDefault: true, }); export const transcribeAndSummarize = workflow.define({ args: { url: v.string(), userEmail: v.string() }, handler: async (step, args): Promise<void> => { // Each step.runAction is durable + independently retried. If summarize fails // 3× then succeeds, transcribe is NOT re-run — completed steps are memoized. const transcript = await step.runAction(internal.youtube.transcribe, { url: args.url }); const summary = await step.runAction(internal.llm.summarize, { transcript }); await step.runAction(internal.email.sendSummary, { to: args.userEmail, summary }); }, }); ``` - **The handler's first arg is `step`, not `ctx`.** Call `step.runAction` / `step.runMutation` / `step.runQuery` with a codegen'd `internal.*` reference — never `ctx.run*` inside a workflow (that breaks durability/memoization). - **Each `step.run*` is a durable checkpoint.** On crash or retry, completed steps are replayed from their stored result, not re-executed — so steps must target `internalAction`/`internalMutation`s that do the real work. - **Override retry per step** when one stage is flakier: `step.runAction(ref, args, { retry: { maxAttempts: 6, initialBackoffMs: 500, base: 2 } })`. Set `{ retry: false }` for a step that must not repeat (already-idempotent external charge). - The actual work (the YouTube fetch, the LLM call, the email send) lives in ordinary `internalAction`s — external APIs go in actions (see `convex-external-apis`), email via `@convex-dev/resend` (see `crons`). ## Start it (and optionally track status) ```ts // from a public mutation/action the client calls: const workflowId = await workflow.start( ctx, internal.workflows.transcribeAndSummarize, { url, userEmail }, ); // status later: await workflow.status(ctx, workflowId) → cleanup: workflow.cleanup(ctx, workflowId) ``` ## Don't - ❌ A custom `jobs`/`pipeline` table + `ctx.scheduler.runAfter` chain to fake retries/ordering — that's what the component exists to replace. - ❌ `ctx.runAction` inside the workflow handler — use `step.runAction` or you lose durability. - ❌ Long synchronous work in one action to dodge steps — you lose independent retry and the 10-min action ceiling still applies per step.
在 GitHub 查看