Workflow

Run durable workflows on Cloudflare, Vercel, or OpenWorkflow.

Workflow runs durable server-side processes that can outlive a single request. Workflow fits work that needs retries, pauses, checkpoints, or a run id that you can inspect again later.

ViteHub discovers workflows from server/workflows/**.

Define a workflow with createWorkflow(options?)(handler) or defineWorkflow(handler, options?), then start runs with runWorkflow() and reattach with getWorkflowRun().

Getting started

Install the package

Terminal
pnpm add https://pkg.pr.new/nuxt-hub/agent/@vitehub/workflow@main

Configure a provider

workflow.provider selects the workflow provider. Cloudflare and Vercel are inferred from hosted presets, so set it only for OpenWorkflow or when you want to override the inferred provider. On Cloudflare, workflow.binding is only needed when you want to override the default binding or pass a binding-like object.

nuxt.config.ts
export default defineNuxtConfig({
  modules: ['@vitehub/workflow/nuxt'],
})

Define a workflow

Create a workflow in server/workflows/**. The file name becomes the workflow name.

server/workflows/publish-draft.ts
import { createWorkflow } from '@vitehub/workflow'

export default createWorkflow()(async (input?: { title?: string }) => {
  const title = input?.title?.trim() || 'Untitled draft'

  return {
    title,
    slug: title.toLowerCase().replace(/[^a-z0-9]+/g, '-'),
  }
})

Start a run

server/api/workflow/publish.post.ts
import { readBody } from 'h3'
import { runWorkflow } from '@vitehub/workflow'

export default defineEventHandler(async (event) => {
  const body = await readBody<{ title?: string }>(event)
  const run = await runWorkflow('publish-draft', {
    title: body.title,
  })

  return {
    runId: run.id,
    status: await run.status(),
  }
})

Reattach to the same run later

Persist or return the run id when you start a workflow. Use it later to check status or call provider-specific run methods.

server/api/workflow/[id].get.ts
import { getRouterParam } from 'h3'
import { getWorkflowRun } from '@vitehub/workflow'

export default defineEventHandler(async (event) => {
  const run = await getWorkflowRun(getRouterParam(event, 'id'))

  if (!run)
    return { found: false }

  return {
    found: true,
    status: await run.status(),
  }
})
Cloudflare Workflow
Configure Cloudflare Workflows and inspect runs from your app.
Vercel Workflow
Configure Vercel Workflow and run durable workflows on Vercel.
OpenWorkflow Workflow
Connect OpenWorkflow and reattach to workflow runs later.

Public API

FunctionUse it for

| createWorkflow(options?)(handler) | Register one named workflow under server/workflows/**. |

| defineWorkflow(handler, options?) | Compatibility wrapper for createWorkflow(options?)(handler). | | runWorkflow(name, payload) | Start a new workflow run and get a run handle back immediately. | | getWorkflowRun(runId) | Reattach to an existing run from a later request, task, or webhook. |

Type reference

Handler signature

The handler receives an optional input and returns the workflow result. ViteHub does not enforce a return type.

type WorkflowHandler<TInput, TResult> = (
  input?: TInput,
) => TResult | Promise<TResult>

WorkflowDefinitionOptions

The options object passed to createWorkflow() or the second argument to defineWorkflow() accepts:

OptionTypeDescription
timeoutnumberMaximum execution time in milliseconds.

WorkflowRun

runWorkflow() returns a WorkflowRun handle with:

Field / MethodTypeDescription
idstringUnique run identifier. Persist this to reattach later.
providerstringThe active provider name.
nativeobjectRaw provider-native run object.
status()Promise<WorkflowRunStatus>Poll the current run state.
stop()Promise<void>Cancel the run.

WorkflowRunStatus

FieldTypeDescription
state'queued' | 'running' | 'paused' | 'waiting' | 'completed' | 'failed' | 'cancelled' | 'terminated' | 'unknown'Current run state.
rawunknownRaw provider response.
outputunknownHandler return value when the run completes.
errorunknownError details when the run fails.

How configuration works

Workflow has one global layer and one definition layer:

  • Top-level workflow config in nuxt.config.ts or nitro.config.ts selects the provider and sets up the app-wide integration.
  • createWorkflow(options?)(handler) configures one workflow definition with portable options such as timeout. defineWorkflow(handler, options?) remains available as a compatibility wrapper.

runWorkflow() starts a new run with input for that specific execution. getWorkflowRun() reconnects to an existing run by id. Neither one replaces top-level or definition-level configuration.

nuxt.config.ts
export default defineNuxtConfig({
  modules: ['@vitehub/workflow/nuxt'],
  workflow: {
    provider: 'cloudflare',
  },
})
server/workflows/publish-draft.ts
export default createWorkflow()(async (input) => {
  return input
})
Each provider adds its own run APIs and runtime behavior. Use the provider pages in the sidebar for binding setup, backend clients, and provider-only run methods.ViteHub only requires the SDK for the provider you choose. Cloudflare stays SDK-free, Vercel needs workflow, and OpenWorkflow needs openworkflow.