Type-safe contracts for Temporal.io
End-to-end type safety and automatic validation for workflows and activities
- ✅ End-to-end type safety — From contract to client, workflows, and activities
- ✅ Automatic validation — Zod schemas validate at all network boundaries
- ✅ Compile-time checks — TypeScript catches missing or incorrect implementations
- ✅ Better DX — Autocomplete, refactoring support, inline documentation
- ✅ Child workflows — Type-safe child workflow execution with unthrown's
AsyncResult - ✅ Result pattern — Explicit error handling without exceptions, powered by unthrown
- 🚧 Nexus support — Cross-namespace operations (planned)
// Define contract once
const contract = defineContract({
taskQueue: "orders",
workflows: {
processOrder: {
input: z.object({ orderId: z.string() }),
output: z.object({ success: z.boolean() }),
activities: {
processPayment: {
input: z.object({ orderId: z.string() }),
output: z.object({ transactionId: z.string() }),
},
},
},
},
});
// Implement activities with unthrown's AsyncResult
import { declareActivitiesHandler, qualify } from "@temporal-contract/worker/activity";
import { fromPromise } from "unthrown";
const activities = declareActivitiesHandler({
contract,
activities: {
processPayment: ({ orderId }) =>
// `qualify` wraps a rejection in an ApplicationFailure of that type
fromPromise(paymentService.process(orderId), qualify("PAYMENT_FAILED")).map((txId) => ({
transactionId: txId,
})),
},
});
// Call from client - fully typed everywhere
const result = await client.executeWorkflow("processOrder", {
workflowId: "order-123",
args: { orderId: "ORD-123" }, // ✅ TypeScript knows!
});# Core packages
pnpm add @temporal-contract/contract @temporal-contract/worker @temporal-contract/client
# Required peer dependencies — install these too. `unthrown` (^4) supplies the
# Result/AsyncResult types you use directly; plus the Temporal SDK and a
# Standard Schema library (zod shown here).
pnpm add unthrown @temporalio/client @temporalio/common @temporalio/worker @temporalio/workflow zodInstall
unthrownexplicitly even if your package manager auto-installs peers: your own code imports itsResult/AsyncResulttypes, so it belongs in yourpackage.json, and it must resolve to v4 (v4 is not compatible with v3). Requires Node.js ≥ 22.19 and is developed against TypeScript 6.0. See Installation for details.
📖 Read the full documentation →
| Package | Description |
|---|---|
| @temporal-contract/contract | Contract builder and type definitions |
| @temporal-contract/worker | Type-safe worker with automatic validation |
| @temporal-contract/client | Type-safe client for consuming workflows |
| @temporal-contract/testing | Testing utilities for integration tests |
temporal-contract uses unthrown end-to-end (workflows, activities, and the typed client) for explicit error handling via Result and AsyncResult, with a separate defect channel for unanticipated failures. Migrating from a previous release that used neverthrow? See Migrating to unthrown.
The contract API (defineContract, declareWorkflow, declareActivitiesHandler, TypedClient) is stable — earlier major bumps were migrations of the underlying Result library, now settled on unthrown. Going forward:
- unthrown is the committed error-handling foundation. There are no plans to switch to a different Result library. The
unthrownpeer range tracks its latest major (currently^4); each bump is documented in the changelog with migration notes. - All four packages version together (a fixed release group), so a single version number describes a compatible set.
See CONTRIBUTING.md.
MIT