Temporal workflow orchestration. Use when working with durable workflows, activities, or the order processing pipeline in packages/workflows.
Temporal is a durable execution platform that makes workflows survive failures automatically. This skill provides guidance for building Temporal applications.
The Temporal Cluster is the central orchestration backend. It maintains three key subsystems: the Event History (a durable log of all workflow state), Task Queues (which route work to the right workers), and a Visibility store (for searching and listing workflows). There are three ways to run a Cluster:
temporal server start-dev. Suitable for development and testing only, not production.Workers are long-running processes that you run and manage. They poll Task Queues for work and execute your code. You might run a single Worker process on one machine during development, or run many Worker processes across a large fleet of machines in production. Each Worker hosts two types of code:
Workers communicate with the Cluster via a poll/complete loop: they poll a Task Queue for tasks, execute the corresponding Workflow or Activity code, and report results back.
Temporal achieves durability through history replay:
If Commands don't match Events = Non-determinism Error = Workflow blocked
| Workflow Code | Command | Event |
|---|---|---|
| Execute activity | ScheduleActivityTask |
ActivityTaskScheduled |
| Sleep/timer | StartTimer |
TimerStarted |
| Child workflow | StartChildWorkflowExecution |
ChildWorkflowExecutionStarted |
See references/core/determinism.md for detailed explanation.
references/typescript/typescript.mdcore and language-specific references for the task at hand.references/core/determinism.md - Why determinism matters, replay mechanics, basic concepts of activitiesreferences/typescript/determinism.mdreferences/core/patterns.md - Conceptual patterns (signals, queries, saga)references/typescript/patterns.mdreferences/core/gotchas.md - Anti-patterns and common mistakesreferences/typescript/gotchas.mdreferences/core/versioning.md - Versioning strategies and concepts - how to safely change workflow code while workflows are runningreferences/typescript/versioning.mdreferences/core/troubleshooting.md - Decision trees, recovery proceduresreferences/core/error-reference.md - Common error types, workflow status referencereferences/core/interactive-workflows.md - Testing signals, updates, queriesreferences/core/dev-management.md - Dev cycle & management of server and workersreferences/core/ai-patterns.md - AI/LLM pattern conceptsreferences/typescript/ai-patterns.md, if available. Currently Python only.references/typescript/observability.md - See for language-specific implementation guidance on observability in Temporalreferences/typescript/advanced-features.md - See for language-specific guidance on advanced Temporal features and language-specific featuresTemporal workflows and activities are located inside the NestJS apps in this monorepo.
apps/<NESTJS_APP>/
├── src/
│ ├── workflows/ # Workflow definitions
│ ├── main.ts # Export activities to be used in workflows (export * from "./app/activities/activities.service")
│ ├── config
│ │ └── temporal.config.ts # Temporal configuration (host, namespace, taskQueue)
│ └── app/
│ ├── activities/ # Activity implementations
│ │ ├── activities.module.ts # Module to export services
│ │ └── activities.service.ts # Service to define the activities used in the workflows
│ └── app.module.ts # Import WorkflowsModule.registerAsync to configure workflows and activities
└── nest-cli.json # compilerOptions.assets include the path src/workflows/**/*
Workflows are deterministic functions that orchestrate activities.
// apps/order/src/workflows/order.workflow.ts
import {
proxyActivities,
sleep,
defineSignal,
setHandler,
condition,
} from '@temporalio/workflow';
import type * as activities from '../activities';
const { processPayment, sendOrderConfirmation, updateInventory, notifyShipping } =
proxyActivities<typeof activities>({
startToCloseTimeout: '5 minutes',
retry: {
maximumAttempts: 3,
initialInterval: '1 second',
backoffCoefficient: 2,
},
});
export const cancelOrderSignal = defineSignal('cancelOrder');
export interface OrderWorkflowInput {
orderId: string;
customerId: string;
items: Array<{ productId: string; quantity: number }>;
total: number;
}
export async function orderWorkflow(input: OrderWorkflowInput): Promise<string> {
let cancelled = false;
setHandler(cancelOrderSignal, () => {
cancelled = true;
});
// Step 1: Process payment
const paymentResult = await processPayment({
orderId: input.orderId,
amount: input.total,
customerId: input.customerId,
});
if (!paymentResult.success) {
return 'PAYMENT_FAILED';
}
// Check for cancellation
if (cancelled) {
// Refund payment
await refundPayment({ paymentId: paymentResult.paymentId });
return 'CANCELLED';
}
// Step 2: Update inventory
await updateInventory(input.items);
// Step 3: Send confirmation email
await sendOrderConfirmation({
orderId: input.orderId,
customerId: input.customerId,
});
// Step 4: Wait for shipping (with timeout)
await sleep('1 hour');
await notifyShipping({ orderId: input.orderId });
return 'COMPLETED';
}
Activities are the building blocks that perform actual work (Steps of the workflows).
// apps/<NESTJS_APP>/src/app/activities/aproctivities.services.ts
import { Inject, Injectable } from "@nestjs/common";
import { StripeService } from "@projectx/payment";
export interface ProcessPaymentInput {
orderId: string;
amount: number;
customerId: string;
}
export interface ProcessPaymentResult {
success: boolean;
paymentId?: string;
error?: string;
}
@Injectable()
export class ActivitiesService {
constructor(
@Inject(StripeService) public readonly stripeService: StripeService,
) {}
async function processPayment(
input: ProcessPaymentInput
): Promise<ProcessPaymentResult> {
try {
const paymentIntent = await this.stripeService.createPaymentIntent({
amount: Math.round(input.amount * 100),
currency: 'usd',
metadata: {
orderId: input.orderId,
customerId: input.customerId,
},
});
return {
success: true,
paymentId: paymentIntent.id,
};
} catch (error) {
return {
success: false,
error: error instanceof Error ? error.message : 'Payment failed',
};
}
}
async function refundPayment(input: { paymentId: string }): Promise<void> {
await this.stripeService.refundPayment(input.paymentId);
}
}
The project includes Temporal in docker-compose.yml:
# Start Temporal server + UI
docker-compose up -d temporal temporal-ui
# Access Temporal UI
open http://localhost:8080