Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
81 changes: 81 additions & 0 deletions docs/codedocs/api-reference/client.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
---
title: "Client"
description: "Trigger, cancel, notify, and inspect workflow runs via the workflow client."
---

`Client` is the main control-plane API for workflows. It wraps the QStash client and provides methods for triggering workflows, canceling runs, notifying events, listing waiters, and fetching logs. The implementation lives in `src/client/index.ts`.

**Constructor**
```typescript
new Client(config: ConstructorParameters<typeof QStashClient>[0])
```

**Parameters**
| Parameter | Type | Default | Description |
|-----------|------|---------|-------------|
| `config` | `@upstash/qstash` client config | — | QStash configuration including `token` and optional `baseUrl`. |

**Methods**

`trigger` creates one or more workflow runs:
```typescript
client.trigger(options: TriggerOptions): Promise<{ workflowRunId: string }>
client.trigger(options: TriggerOptions[]): Promise<{ workflowRunId: string }[]>
```

`cancel` stops workflow runs:
```typescript
client.cancel(id: string | string[] | WorkflowRunCancelFilters): Promise<{ cancelled: number }>
```

`notify` resumes workflows waiting for an event:
```typescript
client.notify({ eventId, eventData, workflowRunId? }): Promise<NotifyResponse[]>
```

`getWaiters` lists workflows waiting for an event:
```typescript
client.getWaiters({ eventId }): Promise<Required<Waiter>[]>
```

`logs` fetches workflow run logs:
```typescript
client.logs(params?): Promise<WorkflowRunLogs>
```

**TriggerOptions**
| Parameter | Type | Default | Description |
|-----------|------|---------|-------------|
| `url` | `string` | — | Workflow URL to trigger. |
| `body` | `unknown` | — | Initial payload. |
| `headers` | `Record<string, string>` | — | Headers forwarded to the workflow. |
| `workflowRunId` | `string` | Random | Optional run ID suffix. Final ID is `wfr_${id}`. |
| `retries` | `number` | `3` | Retry count for first invocation. |
| `retryDelay` | `string` | Exponential | Custom retry delay expression. |
| `flowControl` | `FlowControl` | — | QStash flow control settings. |
| `delay` | <code>number &#124; string</code> | — | Delay before first invocation. |
| `notBefore` | `number` | — | Absolute Unix timestamp to delay execution. |
| `label` | `string` | — | Label for filtering logs. |
| `disableTelemetry` | `boolean` | `false` | Disable telemetry headers. |
| `failureUrl` | `string` | — | URL to call on failure. |
| `redact` | <code>&#123; body?: true; header?: true &#124; string[] &#125;</code> | — | Redact fields in logs. |

**Example**
```typescript filename="scripts/trigger.ts"
import { Client } from "@upstash/workflow";

const client = new Client({ token: process.env.QSTASH_TOKEN! });

const { workflowRunId } = await client.trigger({
url: "https://example.com/api/workflow",
body: { task: "sync" },
retries: 5,
});

await client.notify({ eventId: "sync.done", eventData: { ok: true } });
```

**Related**
- `src/client/index.ts`
- `src/client/types.ts`
- `src/client/filter-types.ts`
87 changes: 87 additions & 0 deletions docs/codedocs/api-reference/dlq.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
---
title: "DLQ"
description: "Inspect and manage workflow messages in the Dead Letter Queue."
---

`DLQ` is accessed via `client.dlq` and provides methods to list, resume, restart, retry failure callbacks, and delete workflow messages that ended up in the dead letter queue. The implementation lives in `src/client/dlq.ts`.

**Accessing DLQ**
```typescript
const client = new Client({ token: process.env.QSTASH_TOKEN! });
const dlq = client.dlq;
```

**Methods**

`list` lists DLQ entries:
```typescript
dlq.list({ cursor?, count?, filter? })
```

`resume` resumes failed workflow runs from the point of failure:
```typescript
dlq.resume(dlqId | dlqId[] | WorkflowDLQActionFilters, options?)
```

`restart` restarts workflow runs from the beginning:
```typescript
dlq.restart(dlqId | dlqId[] | WorkflowDLQActionFilters, options?)
```

`retryFailureFunction` replays a failure callback:
```typescript
dlq.retryFailureFunction({ dlqId })
```

`delete` removes DLQ entries:
```typescript
dlq.delete(dlqId | dlqId[] | WorkflowDLQActionFilters)
```

**Parameters**
| Parameter | Type | Default | Description |
|-----------|------|---------|-------------|
| `dlqId` | `string` | — | DLQ message ID. |
| `filter` | `WorkflowDLQListFilters` | — | Filter by workflow URL, run ID, date range, label, and more. |
| `count` | `number` | `100` | Max items per call for bulk operations. |
| `cursor` | `string` | — | Cursor for pagination. |
| `options` | `{ flowControl?, retries? }` | — | Overrides for resume/restart. |

**Usage patterns**
- **Resume** when you want to continue from the failed step using the same payload and workflow state.
- **Restart** when you want to run the workflow from the beginning with the original payload.
- **Retry failure function** when the failure callback itself failed.

**Example: list and resume**
```typescript filename="scripts/dlq.ts"
import { Client } from "@upstash/workflow";

const client = new Client({ token: process.env.QSTASH_TOKEN! });

const { messages, cursor } = await client.dlq.list({
count: 20,
filter: { label: "billing" },
});

if (messages.length > 0) {
await client.dlq.resume(messages[0].dlqId, { retries: 5 });
}
```

**Example: process all DLQ items**
```typescript filename="scripts/dlq-bulk.ts"
import { Client } from "@upstash/workflow";

const client = new Client({ token: process.env.QSTASH_TOKEN! });

let cursor: string | undefined;

do {
const result = await client.dlq.restart({ all: true, count: 100, cursor });
cursor = result.cursor;
} while (cursor);
```

**Related**
- `src/client/dlq.ts`
- `src/client/filter-types.ts`
72 changes: 72 additions & 0 deletions docs/codedocs/api-reference/errors.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
---
title: "Errors"
description: "Workflow-specific error classes and how they influence execution."
---

Upstash Workflow uses specialized error classes to control execution flow and communicate failure semantics. These are defined in `src/error.ts` and are used internally by `serveBase`, `AutoExecutor`, and step handling logic.

**WorkflowError**
```typescript
class WorkflowError extends QstashError
```
Used for workflow-specific validation and runtime errors. Examples include invalid step names, incompatible parallel steps, and invalid workflow IDs.

**WorkflowAbort**
```typescript
class WorkflowAbort extends Error
```
Thrown internally to stop the current invocation after a step submission. This is expected behavior and should not be swallowed by `try/catch` in user code.

**WorkflowAuthError**
```typescript
class WorkflowAuthError extends WorkflowAbort
```
Thrown during the dry-run authorization pass when a step is detected in `DisabledWorkflowContext` (`src/serve/authorization.ts`).

**WorkflowCancelAbort**
```typescript
class WorkflowCancelAbort extends WorkflowAbort
```
Thrown when you call `context.cancel()` to stop a workflow run.

**WorkflowNonRetryableError**
```typescript
class WorkflowNonRetryableError extends WorkflowAbort
```
Signals that a workflow should not be retried. `serve` responds with status `489` and sets `Upstash-NonRetryable-Error: true`.

**WorkflowRetryAfterError**
```typescript
class WorkflowRetryAfterError extends WorkflowAbort
```
Signals that a workflow should be retried after a specific delay. `serve` responds with status `429` and a `Retry-After` header.

**Example: non-retryable failure**
```typescript filename="app/api/workflow/route.ts"
import { serve, WorkflowNonRetryableError } from "@upstash/workflow";

export const { POST } = serve(async (context) => {
await context.run("validate", () => {
throw new WorkflowNonRetryableError("invalid input");
});
});
```

**Example: retry after delay**
```typescript filename="app/api/workflow/route.ts"
import { serve, WorkflowRetryAfterError } from "@upstash/workflow";

export const { POST } = serve(async (context) => {
await context.run("rate-limit", () => {
throw new WorkflowRetryAfterError("try later", "60s");
});
});
```

**Related**
- `src/error.ts`
- `src/serve/options.ts`
- `src/workflow-requests.ts`

**Implementation notes**
`formatWorkflowError` converts unknown errors into a consistent `FailureFunctionPayload`, and `isInstanceOf` performs prototype-chain checks to handle errors across runtime boundaries. These helpers make error handling more reliable when exceptions cross worker boundaries or are serialized and rehydrated.
71 changes: 71 additions & 0 deletions docs/codedocs/api-reference/middleware.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
---
title: "Middleware"
description: "Define workflow middleware for lifecycle and debug events."
---

The middleware API lets you intercept workflow lifecycle events and debug signals. It is defined in `src/middleware/middleware.ts` and managed by `src/middleware/manager.ts`. Middleware is attached via the `middlewares` option to `serve` or added automatically when `verbose: true` is enabled.

**WorkflowMiddleware**
```typescript
new WorkflowMiddleware<TInitialPayload, TResult>({
name: string,
callbacks?: MiddlewareCallbacks<TInitialPayload, TResult>,
init?: MiddlewareInitCallbacks<TInitialPayload, TResult>
})
```

**Parameters**
| Parameter | Type | Default | Description |
|-----------|------|---------|-------------|
| `name` | `string` | — | Middleware name. |
| `callbacks` | `MiddlewareCallbacks` | — | Direct callbacks for events. |
| `init` | `() => MiddlewareCallbacks` | — | Async initializer for callbacks. |

**Lifecycle events**
- `beforeExecution` and `afterExecution` are fired around step submission (`src/qstash/submit-steps.ts`).
- `runStarted` and `runCompleted` are fired at workflow boundaries (`src/serve/index.ts`).

**Debug events**
- `onInfo`, `onWarning`, and `onError` are emitted during request parsing and execution (`src/serve/index.ts`, `src/workflow-parser.ts`, `src/workflow-requests.ts`).

**Example**
```typescript filename="src/middleware/metrics.ts"
import { WorkflowMiddleware } from "@upstash/workflow";

export const metrics = new WorkflowMiddleware({
name: "metrics",
callbacks: {
beforeExecution: ({ stepName }) => console.log("start", stepName),
afterExecution: ({ stepName }) => console.log("end", stepName),
onWarning: ({ warning }) => console.warn(warning),
},
});
```

**Example: async init**
```typescript filename="src/middleware/init.ts"
import { WorkflowMiddleware } from "@upstash/workflow";

export const tracing = new WorkflowMiddleware({
name: "tracing",
init: async () => {
const client = await createTracingClient();
return {
runStarted: () => client.startSpan("workflow"),
runCompleted: ({ result }) => client.endSpan({ result }),
onError: ({ error }) => client.capture("error", error.message),
};
},
});
```

**loggingMiddleware**
`loggingMiddleware` is a built-in middleware that emits structured logs. It is defined in `src/middleware/logging.ts` and is automatically included when `verbose: true` is passed to `serve`.

**Related**
- `src/middleware/middleware.ts`
- `src/middleware/manager.ts`
- `src/middleware/types.ts`

**Implementation notes**
`MiddlewareManager` ensures every middleware is initialized once per request via `ensureInit`, and it wraps callback execution with a safe error path. If a middleware throws, the manager attempts to call its `onError` callback, falling back to console logging. This prevents broken middleware from crashing workflow execution while still surfacing diagnostics.
68 changes: 68 additions & 0 deletions docs/codedocs/api-reference/platform-adapters.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
---
title: "Platform Adapters"
description: "Framework-specific entrypoints that wrap serveBase for common runtimes."
---

Platform adapters are thin wrappers around `serveBase` that adapt to framework handler signatures. They live under `platforms/` and are exported via package subpaths such as `@upstash/workflow/nextjs`. Each adapter sets framework-specific telemetry and passes framework runtime details to `serveBase`.

**Available adapters**
- `@upstash/workflow/nextjs` (`platforms/nextjs.ts`)
- `@upstash/workflow/cloudflare` (`platforms/cloudflare.ts`)
- `@upstash/workflow/hono` (`platforms/hono.ts`)
- `@upstash/workflow/express` (`platforms/express.ts`)
- `@upstash/workflow/astro` (`platforms/astro.ts`)
- `@upstash/workflow/svelte` (`platforms/svelte.ts`)
- `@upstash/workflow/solidjs` (`platforms/solidjs.ts`)
- `@upstash/workflow/h3` (`platforms/h3.ts`)
- `@upstash/workflow/tanstack` (`platforms/tanstack.ts`)
- `@upstash/workflow/react-router` (`platforms/react-router.ts`)

**Common exports**
Most adapters export the same helpers:
| Export | Description |
|--------|-------------|
| `serve` | Build a handler for a single workflow. |
| `createWorkflow` | Create an invokable workflow definition. |
| `serveMany` | Route multiple workflows based on URL. |

**Handler shapes by platform**
- Next.js App Router: `{ POST }` handler (Request in, Response out).
- Cloudflare Workers and Pages: `{ fetch }` handler.
- Express: middleware function `(req, res)`.
- Hono and other adapters: framework-specific handler function.

**Next.js extras**
The Next.js adapter also exports `servePagesRouter`, `createWorkflowPagesRouter`, and `serveManyPagesRouter` for the pages router API (`platforms/nextjs.ts`).

**Example: Cloudflare**
```typescript filename="src/index.ts"
import { serve } from "@upstash/workflow/cloudflare";

export default serve(async (context) => {
await context.run("hello", () => "world");
});
```

**Example: Express**
```typescript filename="server.ts"
import express from "express";
import { serve } from "@upstash/workflow/express";

const app = express();
app.use(express.json());

const { handler } = serve(async (context) => {
await context.run("hello", () => "world");
});

app.post("/workflow", handler);
```

**How adapters map to core execution**
Each adapter calls `serveBase` from `src/serve/index.ts` and only adapts the handler signature. This keeps feature parity between frameworks while letting you choose the runtime that fits your deployment. If you ever need a custom runtime, you can build your own adapter by calling `serveBase` directly and translating requests to the standard `Request` shape used by the SDK.

**Related**
- `platforms/nextjs.ts`
- `platforms/cloudflare.ts`
- `platforms/astro.ts`
- `platforms/express.ts`
Loading