Skip to content

temporal-contractType-safe contracts for Temporal.io

End-to-end type safety ยท Runtime validation ยท Explicit error handling

temporal-contract

Quick Example โ€‹

Define your contract once โ€” get type safety everywhere:

typescript
import { defineContract } from "@temporal-contract/contract";
import { z } from "zod";

export const orderContract = defineContract({
  taskQueue: "orders",
  workflows: {
    processOrder: {
      input: z.object({
        orderId: z.string(),
        customerId: z.string(),
        amount: z.number(),
      }),
      output: z.object({
        status: z.enum(["success", "failed"]),
        transactionId: z.string().optional(),
      }),
      activities: {
        processPayment: {
          input: z.object({ customerId: z.string(), amount: z.number() }),
          output: z.object({ transactionId: z.string() }),
        },
        sendNotification: {
          input: z.object({ customerId: z.string(), message: z.string() }),
          output: z.void(),
        },
      },
    },
  },
});
typescript
import { ResultAsync } from "neverthrow";
import { declareActivitiesHandler, ApplicationFailure } from "@temporal-contract/worker/activity";
import { orderContract } from "./contract";

export const activities = declareActivitiesHandler({
  contract: orderContract,
  activities: {
    processOrder: {
      processPayment: ({ customerId, amount }) =>
        ResultAsync.fromPromise(paymentService.charge(customerId, amount), (e) =>
          ApplicationFailure.create({
            type: "PAYMENT_FAILED",
            message: e instanceof Error ? e.message : "Payment failed",
            cause: e instanceof Error ? e : undefined,
          }),
        ).map((tx) => ({ transactionId: tx.id })),
      sendNotification: ({ customerId, message }) =>
        ResultAsync.fromPromise(notificationService.send(customerId, message), (e) =>
          ApplicationFailure.create({
            type: "NOTIFICATION_FAILED",
            message: e instanceof Error ? e.message : "Notification failed",
            cause: e instanceof Error ? e : undefined,
          }),
        ),
    },
  },
});
typescript
import { declareWorkflow } from "@temporal-contract/worker/workflow";
import { orderContract } from "./contract";

export const processOrder = declareWorkflow({
  workflowName: "processOrder",
  contract: orderContract,
  activityOptions: { startToCloseTimeout: "1 minute" },
  implementation: async ({ activities }, { orderId, customerId, amount }) => {
    const { transactionId } = await activities.processPayment({ customerId, amount });
    await activities.sendNotification({ customerId, message: `Order ${orderId} confirmed!` });
    return { status: "success", transactionId };
  },
});
typescript
import { TypedClient } from "@temporal-contract/client";
import { Connection, Client } from "@temporalio/client";
import { orderContract } from "./contract";

const connection = await Connection.connect({ address: "localhost:7233" });
const temporalClient = new Client({ connection });
const client = TypedClient.create(orderContract, temporalClient);

const future = client.executeWorkflow("processOrder", {
  workflowId: "order-123",
  args: { orderId: "ORD-123", customerId: "CUST-456", amount: 99.99 },
});

const result = await future;

result.match(
  (output) => console.log(output.status), // โœ… 'success' | 'failed'
  (error) => console.error("Failed:", error),
);

Released under the MIT License.