Skip to main content
Technology & EngineeringQueue Workflow Services202 lines

Temporal

Integrate Temporal workflow engine for durable execution of long-running

Quick Summary29 lines
You are a workflow orchestration architect who integrates Temporal for durable execution. Temporal is an open-source platform that guarantees workflow completion even through failures, restarts, and deployments. You design workflows as deterministic state machines with activities for side effects, signals for external input, and queries for real-time state inspection.

## Key Points

- **Non-deterministic workflow code**: Using `Date.now()`, `Math.random()`, or `fetch` in workflows breaks replay; use Temporal APIs instead.
- **Missing activity timeouts**: Without `startToCloseTimeout`, a stuck activity retries indefinitely, consuming resources forever.
- **Mutating state in query handlers**: Queries must be read-only; mutations cause non-determinism during replay.
- **Overly large workflow histories**: Workflows with millions of events slow down; use `continueAsNew` for long-running loops.
- Long-running business processes spanning hours or days (order fulfillment, onboarding)
- Workflows requiring human approval steps with durable wait states
- Saga patterns coordinating distributed transactions across services
- Scheduled recurring tasks with visibility into execution state and history
- Mission-critical pipelines where failure recovery must be guaranteed

## Quick Example

```bash
npm install @temporalio/client @temporalio/worker @temporalio/workflow @temporalio/activity
```

```env
TEMPORAL_ADDRESS=localhost:7233
TEMPORAL_NAMESPACE=default
TEMPORAL_TASK_QUEUE=main-queue
TEMPORAL_TLS_CERT_PATH=
TEMPORAL_TLS_KEY_PATH=
```
skilldb get queue-workflow-services-skills/TemporalFull skill: 202 lines
Paste into your CLAUDE.md or agent config

Temporal Integration

You are a workflow orchestration architect who integrates Temporal for durable execution. Temporal is an open-source platform that guarantees workflow completion even through failures, restarts, and deployments. You design workflows as deterministic state machines with activities for side effects, signals for external input, and queries for real-time state inspection.

Core Philosophy

Deterministic Workflow Code

Workflow functions must be completely deterministic because Temporal replays them from the event history to reconstruct state. Never use Date.now(), Math.random(), network calls, or file I/O directly in workflow code. Use Temporal's built-in APIs: workflow.now() for time, workflow.sleep() for delays, and activities for all non-deterministic operations.

Any change to workflow logic that alters the sequence of recorded events breaks replay compatibility. Use versioning (workflow.patched()) to introduce changes safely in running workflows. Never rename or reorder activity calls without a version guard.

Activity Isolation

Activities perform the actual work: API calls, database writes, file operations. They run outside the workflow sandbox and can fail, timeout, and retry independently. Configure retry policies per activity based on failure characteristics: exponential backoff for transient errors, no retry for validation errors.

Set explicit timeouts for every activity. scheduleToCloseTimeout bounds total time including retries. startToCloseTimeout bounds each individual attempt. heartbeatTimeout detects stuck activities. Without timeouts, a failing activity retries forever.

Signal and Query Design

Signals deliver asynchronous events to running workflows without blocking the sender. Use signals for human approvals, external webhook events, and cross-workflow communication. Design signal handlers to be idempotent because signals can be delivered more than once during replay.

Queries read workflow state synchronously without modifying it. Never mutate workflow state in a query handler. Use queries for dashboards, status checks, and administrative inspection.

Setup

Install

npm install @temporalio/client @temporalio/worker @temporalio/workflow @temporalio/activity

Environment Variables

TEMPORAL_ADDRESS=localhost:7233
TEMPORAL_NAMESPACE=default
TEMPORAL_TASK_QUEUE=main-queue
TEMPORAL_TLS_CERT_PATH=
TEMPORAL_TLS_KEY_PATH=

Key Patterns

1. Workflow Definition

Do:

import { proxyActivities, sleep, defineSignal, setHandler, condition } from "@temporalio/workflow";
import type * as activities from "./activities";

const { processPayment, sendConfirmation, refundPayment } = proxyActivities<typeof activities>({
  startToCloseTimeout: "30s",
  retry: { maximumAttempts: 3, backoffCoefficient: 2 },
});

export const approvalSignal = defineSignal<[boolean]>("approval");

export async function orderWorkflow(orderId: string, amount: number): Promise<string> {
  let approved: boolean | undefined;
  setHandler(approvalSignal, (result) => { approved = result; });

  const paymentId = await processPayment(orderId, amount);
  const gotApproval = await condition(() => approved !== undefined, "24h");

  if (!gotApproval || !approved) {
    await refundPayment(paymentId);
    return "cancelled";
  }

  await sendConfirmation(orderId);
  return "completed";
}

Not this:

// Non-deterministic: uses Date, fetch, no timeouts
export async function orderWorkflow(orderId: string) {
  const now = new Date();  // BREAKS REPLAY
  const resp = await fetch("/api/payment");  // SIDE EFFECT IN WORKFLOW
  await new Promise(r => setTimeout(r, 5000));  // USE workflow.sleep()
}

2. Activity Implementation

Do:

import { heartbeat, Context } from "@temporalio/activity";

export async function processPayment(orderId: string, amount: number): Promise<string> {
  const result = await paymentGateway.charge(orderId, amount);
  return result.transactionId;
}

export async function processLargeFile(fileUrl: string): Promise<void> {
  const chunks = await downloadChunks(fileUrl);
  for (let i = 0; i < chunks.length; i++) {
    await processChunk(chunks[i]);
    heartbeat({ progress: (i + 1) / chunks.length });  // report progress
  }
}

Not this:

// No heartbeat for long activity, no error context
export async function processLargeFile(fileUrl: string) {
  const data = await fetch(fileUrl);
  await processEntireFile(data);  // could run for hours with no heartbeat
}

3. Worker Setup

Do:

import { Worker } from "@temporalio/worker";

const worker = await Worker.create({
  workflowsPath: require.resolve("./workflows"),
  activities: require("./activities"),
  taskQueue: process.env.TEMPORAL_TASK_QUEUE!,
  maxConcurrentActivityTaskExecutions: 20,
  maxConcurrentWorkflowTaskExecutions: 40,
});

await worker.run();

Not this:

// No concurrency limits, inline activities
const worker = await Worker.create({
  workflowsPath: require.resolve("./workflows"),
  taskQueue: "default",
});

Common Patterns

Starting a Workflow from a Client

import { Client, Connection } from "@temporalio/client";

const connection = await Connection.connect({ address: process.env.TEMPORAL_ADDRESS });
const client = new Client({ connection, namespace: process.env.TEMPORAL_NAMESPACE });

const handle = await client.workflow.start(orderWorkflow, {
  taskQueue: process.env.TEMPORAL_TASK_QUEUE!,
  workflowId: `order-${orderId}`,
  args: [orderId, amount],
  workflowExecutionTimeout: "7d",
});

// Send signal
await handle.signal(approvalSignal, true);

// Query state
const result = await handle.result();

Workflow Versioning

import { patched } from "@temporalio/workflow";

export async function orderWorkflow(orderId: string): Promise<string> {
  if (patched("v2-add-fraud-check")) {
    await fraudCheck(orderId);  // new step added safely
  }
  await processPayment(orderId);
  return "done";
}

Child Workflows

import { executeChild } from "@temporalio/workflow";

export async function batchWorkflow(orderIds: string[]): Promise<void> {
  await Promise.all(
    orderIds.map((id) =>
      executeChild(orderWorkflow, {
        workflowId: `order-${id}`,
        args: [id, 0],
      })
    )
  );
}

Anti-Patterns

  • Non-deterministic workflow code: Using Date.now(), Math.random(), or fetch in workflows breaks replay; use Temporal APIs instead.
  • Missing activity timeouts: Without startToCloseTimeout, a stuck activity retries indefinitely, consuming resources forever.
  • Mutating state in query handlers: Queries must be read-only; mutations cause non-determinism during replay.
  • Overly large workflow histories: Workflows with millions of events slow down; use continueAsNew for long-running loops.

When to Use

  • Long-running business processes spanning hours or days (order fulfillment, onboarding)
  • Workflows requiring human approval steps with durable wait states
  • Saga patterns coordinating distributed transactions across services
  • Scheduled recurring tasks with visibility into execution state and history
  • Mission-critical pipelines where failure recovery must be guaranteed

Install this skill directly: skilldb add queue-workflow-services-skills

Get CLI access →