ViteHub

Workflow runtime API

Runtime functions, helper exports, options, and normalized run types exported by @vitehub/workflow.

Runtime code imports from @vitehub/workflow:

import {
  createWorkflow,
  defineWorkflow,
  deferWorkflow,
  getWorkflowRun,
  readRequestPayload,
  readValidatedPayload,
  runWorkflow,
  validatePayload,
} from '@vitehub/workflow'

In Nitro, the module auto-imports defineWorkflow for discovered workflow definitions and getWorkflowRun for read-oriented server code. Invocation helpers such as runWorkflow, deferWorkflow, and createWorkflow stay explicit imports because they start or construct workflow behavior.

Vite config imports the plugin from @vitehub/workflow/vite:

import { hubWorkflow } from '@vitehub/workflow/vite'

Nitro config imports the module by name:

export default defineNitroConfig({
  modules: ['@vitehub/workflow/nitro'],
})

defineWorkflow(handler, options?)

Defines a discovered workflow.

export default defineWorkflow<Payload, Result>(async (context) => {
  return { ok: true }
})

The handler receives:

FieldTypeDescription
idstring | undefinedRun id chosen by the caller or provider.
namestringDiscovered workflow name.
payloadTPayloadPayload passed to runWorkflow() or deferWorkflow().
provider'cloudflare' | 'openworkflow' | 'vercel'Active provider.
stepunknownProvider step object when one is available.

Options:

FieldTypeDescription
idstringOptional definition id reserved for provider adapters.

runWorkflow(name, payload?, options?)

Starts a workflow and returns normalized run metadata.

const run = await runWorkflow('welcome', { email: 'ava@example.com' })

Pass id in options when you need a stable run id:

const run = await runWorkflow('welcome', { email: 'ava@example.com' }, { id: 'welcome-signup-42' })

Return shape:

type WorkflowRun<TPayload = unknown, TResult = unknown> = {
  id: string
  provider: 'cloudflare' | 'openworkflow' | 'vercel'
  status: 'queued' | 'running' | 'completed' | 'failed' | 'unknown'
  payload?: TPayload
  result?: TResult
  metadata?: unknown
}

Errors:

CodeWhen
WORKFLOW_DISABLEDworkflow: false disables the runtime.
WORKFLOW_DEFINITION_NOT_FOUNDNo discovered workflow matches name.

deferWorkflow(name, payload?, options?)

Starts a workflow and returns the start promise. Useful for fire-and-forget routes that still want to surface errors via waitUntil().

await deferWorkflow('welcome', { email: 'ava@example.com' })
return { ok: true }

deferWorkflow() uses the active request waitUntil() when the runtime exposes it.

getWorkflowRun(name, id)

Reads normalized status for a workflow run.

const run = await getWorkflowRun<WelcomePayload, WelcomeResult>('welcome', id)

Cloudflare uses the generated Workflow binding when it is available. Vercel returns generated runtime state for runs started by the same deployment process. OpenWorkflow reads the configured Postgres backend.

Payload helpers

readRequestPayload(request)

Reads JSON when the request has an application/json content type. Other content types return the request text.

const payload = await readRequestPayload<WelcomePayload>(request)

validatePayload(payload, schema)

Validates an already-read payload. The schema may be a parser function, an object with parse(), or an object with safeParse().

const payload = await validatePayload(rawPayload, schema)

readValidatedPayload(request, schema)

Reads and validates a Web Request.

const payload = await readValidatedPayload(request, schema)

createWorkflow(options)

Creates an explicit workflow handle for starting and observing runs. Framework discovery does not scan createWorkflow() calls; discovered workflow names come from workflow files or folders.

const chatReply = createWorkflow<ChatReplyPayload, ChatReplyResult>({
  name: 'chat-reply',
  handler: async ({ payload }) => {
    return await answerWithContext(payload.text)
  },
  id: ({ payload: { threadId, messageId } }) => `${threadId}:${messageId}`,
})

const run = await chatReply.run(payload)
const result = await chatReply.getRun(run.id)

id is optional. When provided, it must return a string. Passing run(payload, { id }) or defer(payload, { id }) overrides the resolver.

Config options

workflow can be configured in Vite or Nitro config:

workflow: {
  provider: 'openworkflow',
  postgres: {
    url: process.env.OPENWORKFLOW_POSTGRES_URL,
    namespaceId: 'production',
    schema: 'openworkflow',
  },
  worker: {
    concurrency: 10,
  },
}
FieldTypeDescription
provider'cloudflare' | 'openworkflow' | 'vercel'Explicit provider. Defaults from hosting: Cloudflare hosting selects Cloudflare, Nitro node/Docker hosting selects OpenWorkflow, everything else selects Vercel.
bindingstringOverride the generated Cloudflare binding name used at runtime.
namestringOverride the provider workflow name used by generated output.
postgres.urlstringOpenWorkflow Postgres URL. Defaults to OPENWORKFLOW_POSTGRES_URL or DATABASE_URL.
postgres.namespaceIdstringOpenWorkflow namespace. Defaults to OPENWORKFLOW_NAMESPACE_ID or production.
postgres.schemastringOpenWorkflow schema. Defaults to OPENWORKFLOW_SCHEMA or openworkflow.
postgres.runMigrationsbooleanWhether OpenWorkflow should run migrations when connecting.
worker.concurrencynumberOpenWorkflow worker concurrency.

Set workflow: false to disable the runtime and generated provider output.

OpenWorkflow worker helpers

Docker deployments can start a worker process from the generated registry:

import workflowRegistry from '#vitehub/workflow/registry'
import { startOpenWorkflowWorker } from '@vitehub/workflow/runtime/openworkflow-worker'

await startOpenWorkflowWorker({
  config: {
    provider: 'openworkflow',
    postgres: { url: process.env.OPENWORKFLOW_POSTGRES_URL },
  },
  registry: workflowRegistry,
})

When called inside a Nitro runtime that uses @vitehub/workflow/nitro, the helper can use the installed runtime workflow config and registry. Separate Docker worker entrypoints should pass { config, registry, concurrency } explicitly.

Helper exports

Provider naming helpers are exported for tests and advanced integrations:

import {
  getCloudflareWorkflowBindingName,
  getCloudflareWorkflowClassName,
  getCloudflareWorkflowName,
  getVercelWorkflowName,
} from '@vitehub/workflow'
Copyright © 2026