Try Live
Add Docs
Rankings
Pricing
Docs
Install
Theme
Install
Docs
Pricing
More...
More...
Try Live
Rankings
Enterprise
Create API Key
Add Docs
Temporal TypeScript SDK
https://github.com/temporalio/sdk-typescript
Admin
Temporal TypeScript SDK
Tokens:
14,425
Snippets:
101
Trust Score:
9.5
Update:
4 weeks ago
Context
Skills
Chat
Benchmark
87.7
Suggestions
Latest
Show doc for...
Code
Info
Show Results
Context Summary (auto-generated)
Raw
Copy
Link
# Temporal TypeScript SDK The Temporal TypeScript SDK enables developers to build durable, fault-tolerant applications using Workflows, Activities, and Workers. It provides a programming model where long-running business logic is expressed as deterministic Workflow functions that automatically handle failures, retries, and state persistence. The SDK connects to Temporal Server, which orchestrates workflow executions and ensures exactly-once semantics for business operations. The core architecture consists of four main packages: `@temporalio/workflow` for defining workflow logic with timers, signals, queries, and child workflows; `@temporalio/activity` for implementing side-effectful operations like HTTP calls and database access; `@temporalio/worker` for hosting and executing workflows and activities; and `@temporalio/client` for starting workflows and interacting with them from external applications. Additionally, `@temporalio/testing` provides utilities for time-skipping tests and workflow replay validation. ## Connection - Establish Connection to Temporal Server The `Connection` class creates a gRPC connection to Temporal Server, which is required for both the Client and Worker to communicate with the service. ```typescript import { Connection, Client } from '@temporalio/client'; // Connect to Temporal Server (default: localhost:7233) const connection = await Connection.connect({ address: 'localhost:7233', // For Temporal Cloud: // address: 'your-namespace.tmprl.cloud:7233', // tls: { // clientCertPair: { // crt: fs.readFileSync('client.pem'), // key: fs.readFileSync('client-key.pem'), // }, // }, }); // Create a client using the connection const client = new Client({ connection, namespace: 'default', }); // Clean up when done await connection.close(); ``` ## Client.workflow.start - Start a Workflow Execution The `WorkflowClient.start()` method initiates a new workflow execution and returns a handle for interacting with it. The workflow runs asynchronously on a Worker. ```typescript import { Client } from '@temporalio/client'; const client = new Client(); // Start a workflow and get a handle const handle = await client.workflow.start('orderProcessingWorkflow', { taskQueue: 'order-queue', workflowId: `order-${orderId}`, args: [{ orderId: '12345', items: ['item-a', 'item-b'], total: 99.99 }], // Optional: set timeouts and retry policy workflowExecutionTimeout: '1 day', workflowRunTimeout: '1 hour', retry: { maximumAttempts: 3, }, // Optional: search attributes for visibility searchAttributes: { CustomerId: ['customer-123'], }, }); console.log(`Started workflow ${handle.workflowId}`); // Wait for the workflow to complete const result = await handle.result(); console.log('Workflow completed with result:', result); ``` ## Client.workflow.execute - Execute Workflow and Wait for Result The `WorkflowClient.execute()` method is a convenience that starts a workflow and waits for it to complete, returning the result directly. ```typescript import { Client } from '@temporalio/client'; const client = new Client(); // Execute workflow and wait for result in one call const result = await client.workflow.execute('greetingWorkflow', { taskQueue: 'greeting-queue', workflowId: `greeting-${Date.now()}`, args: ['World'], }); console.log('Greeting:', result); // Output: "Hello, World!" ``` ## WorkflowHandle.signal - Send Signal to Running Workflow Signals allow external code to send data to a running workflow, which can trigger state changes or actions within the workflow. ```typescript import { Client } from '@temporalio/client'; const client = new Client(); // Get a handle to an existing workflow const handle = client.workflow.getHandle('order-12345'); // Send a signal to the workflow await handle.signal('updateOrder', { action: 'addItem', item: { sku: 'WIDGET-001', quantity: 2 }, }); // Signal can also cancel or modify workflow behavior await handle.signal('cancelOrder', { reason: 'Customer request' }); ``` ## WorkflowHandle.query - Query Workflow State Queries allow reading the current state of a workflow without affecting its execution. Query handlers must be synchronous and cannot modify workflow state. ```typescript import { Client } from '@temporalio/client'; const client = new Client(); const handle = client.workflow.getHandle('order-12345'); // Query the current workflow state const orderStatus = await handle.query<string>('getStatus'); console.log('Order status:', orderStatus); // Query with arguments const itemCount = await handle.query<number>('getItemCount', { category: 'electronics' }); console.log('Electronics items:', itemCount); ``` ## WorkflowHandle.executeUpdate - Execute Workflow Update Updates are similar to signals but return a value, allowing bidirectional communication with a running workflow. Updates can have validators and return results. ```typescript import { Client } from '@temporalio/client'; const client = new Client(); const handle = client.workflow.getHandle('shopping-cart-user-123'); // Execute an update and get the result const newTotal = await handle.executeUpdate('addToCart', { args: [{ productId: 'SKU-001', quantity: 3, price: 29.99 }], }); console.log('Cart total after adding item:', newTotal); // Updates can also validate input and reject invalid requests try { await handle.executeUpdate('applyDiscount', { args: [{ code: 'INVALID-CODE' }], }); } catch (error) { console.log('Update rejected:', error.message); } ``` ## proxyActivities - Configure and Call Activities from Workflow The `proxyActivities()` function creates a proxy object that allows workflows to call activities with specified options like timeouts and retry policies. ```typescript // workflows.ts import { proxyActivities } from '@temporalio/workflow'; import type * as activities from './activities'; // Configure activities with default options const { sendEmail, processPayment, updateInventory } = proxyActivities<typeof activities>({ startToCloseTimeout: '5 minutes', retry: { maximumAttempts: 3, initialInterval: '1 second', backoffCoefficient: 2, }, }); export async function orderWorkflow(order: Order): Promise<OrderResult> { // Activities are called like regular async functions await updateInventory(order.items); const paymentResult = await processPayment({ amount: order.total, customerId: order.customerId, }); // Override options for specific activity call await sendEmail.executeWithOptions( { startToCloseTimeout: '30 seconds' }, [order.customerEmail, 'Order Confirmed', `Order ${order.id} confirmed!`] ); return { orderId: order.id, paymentId: paymentResult.id, status: 'completed' }; } ``` ## sleep - Durable Timer in Workflow The `sleep()` function creates a durable timer that survives workflow restarts. It's deterministic and can span any duration from milliseconds to years. ```typescript import { sleep, proxyActivities } from '@temporalio/workflow'; import type * as activities from './activities'; const { sendReminder, checkStatus } = proxyActivities<typeof activities>({ startToCloseTimeout: '1 minute', }); export async function subscriptionRenewalWorkflow(userId: string): Promise<void> { // Wait for 30 days before sending renewal reminder await sleep('30 days'); await sendReminder(userId, 'Your subscription expires in 7 days'); // Wait another 7 days await sleep('7 days'); const status = await checkStatus(userId); if (status === 'not_renewed') { await sendReminder(userId, 'Your subscription has expired'); } } ``` ## condition - Wait for Condition with Optional Timeout The `condition()` function blocks workflow execution until a condition becomes true or an optional timeout expires. It's used for event-driven workflow logic. ```typescript import { condition, defineSignal, setHandler } from '@temporalio/workflow'; const approvalSignal = defineSignal<[boolean]>('approval'); export async function approvalWorkflow(requestId: string): Promise<string> { let approved: boolean | undefined; // Set up signal handler to receive approval setHandler(approvalSignal, (isApproved) => { approved = isApproved; }); // Wait up to 24 hours for approval const receivedApproval = await condition(() => approved !== undefined, '24 hours'); if (!receivedApproval) { return 'Request timed out - no approval received'; } return approved ? 'Request approved' : 'Request rejected'; } ``` ## defineSignal / defineQuery / defineUpdate - Define Workflow Handlers These functions create typed definitions for signals, queries, and updates that can be used both in workflow implementations and client code. ```typescript // shared/workflow-types.ts import { defineSignal, defineQuery, defineUpdate } from '@temporalio/workflow'; // Define typed signal export const addItemSignal = defineSignal<[{ productId: string; quantity: number }]>('addItem'); // Define typed query export const getCartQuery = defineQuery<CartItem[]>('getCart'); // Define typed update with return value export const checkoutUpdate = defineUpdate<CheckoutResult, [PaymentInfo]>('checkout'); // workflows.ts import { setHandler } from '@temporalio/workflow'; import { addItemSignal, getCartQuery, checkoutUpdate } from './shared/workflow-types'; export async function shoppingCartWorkflow(): Promise<void> { const cart: CartItem[] = []; // Register signal handler setHandler(addItemSignal, ({ productId, quantity }) => { cart.push({ productId, quantity }); }); // Register query handler (must be synchronous) setHandler(getCartQuery, () => cart); // Register update handler with validator setHandler( checkoutUpdate, async (paymentInfo) => { // Process checkout and return result const result = await processCheckout(cart, paymentInfo); return result; }, { validator: (paymentInfo) => { if (!paymentInfo.cardNumber) { throw new Error('Card number is required'); } }, } ); // Keep workflow running to handle signals await condition(() => false); } ``` ## startChild / executeChild - Child Workflows Child workflows allow decomposing complex workflows into smaller, reusable units. They inherit cancellation from parent workflows and can be awaited for completion. ```typescript import { startChild, executeChild, ParentClosePolicy } from '@temporalio/workflow'; export async function parentWorkflow(orders: Order[]): Promise<void> { // Execute child workflow and wait for result const result = await executeChild('processOrderWorkflow', { args: [orders[0]], workflowId: `order-${orders[0].id}`, }); // Start multiple child workflows concurrently const childHandles = await Promise.all( orders.slice(1).map((order) => startChild('processOrderWorkflow', { args: [order], workflowId: `order-${order.id}`, parentClosePolicy: ParentClosePolicy.PARENT_CLOSE_POLICY_ABANDON, }) ) ); // Wait for all children to complete const results = await Promise.all(childHandles.map((handle) => handle.result())); console.log('All orders processed:', results); } ``` ## continueAsNew - Continue as New Execution The `continueAsNew()` function completes the current workflow and immediately starts a new execution with a fresh event history, useful for long-running workflows that would otherwise accumulate too much history. ```typescript import { continueAsNew, sleep, proxyActivities } from '@temporalio/workflow'; import type * as activities from './activities'; const { processEvents } = proxyActivities<typeof activities>({ startToCloseTimeout: '1 minute', }); export async function eventProcessorWorkflow( lastProcessedId: string = '', iterationCount: number = 0 ): Promise<void> { // Process a batch of events const events = await processEvents(lastProcessedId, 100); if (events.length === 0) { // No more events, wait and continue await sleep('1 minute'); } const newLastId = events[events.length - 1]?.id ?? lastProcessedId; // After 1000 iterations, continue as new to prevent history from growing too large if (iterationCount >= 1000) { await continueAsNew<typeof eventProcessorWorkflow>(newLastId, 0); } // Recursively call with updated state (will become continueAsNew eventually) await continueAsNew<typeof eventProcessorWorkflow>(newLastId, iterationCount + 1); } ``` ## Worker.create - Create and Run a Worker The `Worker` class polls Temporal Server for tasks and executes workflows and activities. It's the runtime component that hosts your application logic. ```typescript import { Worker, NativeConnection } from '@temporalio/worker'; import * as activities from './activities'; async function runWorker() { // Create connection to Temporal Server const connection = await NativeConnection.connect({ address: 'localhost:7233', }); // Create and configure the Worker const worker = await Worker.create({ connection, namespace: 'default', taskQueue: 'my-task-queue', // Register workflow implementations workflowsPath: require.resolve('./workflows'), // Register activity implementations activities, // Optional: configure worker behavior maxConcurrentActivityTaskExecutions: 100, maxConcurrentWorkflowTaskExecutions: 100, maxCachedWorkflows: 1000, // Optional: graceful shutdown configuration shutdownGraceTime: '30 seconds', }); // Run the worker (blocks until shutdown) await worker.run(); } runWorker().catch((err) => { console.error('Worker failed:', err); process.exit(1); }); ``` ## Worker.runUntil - Run Worker Until Promise Completes The `runUntil()` method runs the worker until a provided promise resolves, useful for testing or running a single workflow execution. ```typescript import { Worker, NativeConnection } from '@temporalio/worker'; import { Client } from '@temporalio/client'; import * as activities from './activities'; async function runSingleWorkflow() { const connection = await NativeConnection.connect(); const worker = await Worker.create({ connection, taskQueue: 'test-queue', workflowsPath: require.resolve('./workflows'), activities, }); const client = new Client(); // Run worker only until the workflow completes const result = await worker.runUntil( client.workflow.execute('myWorkflow', { taskQueue: 'test-queue', workflowId: 'test-run-' + Date.now(), args: ['test-input'], }) ); console.log('Workflow result:', result); } ``` ## Activity Context - Access Activity Runtime Information The `Context` class provides runtime information and utilities for activities, including heartbeating, cancellation handling, and logging. ```typescript // activities.ts import { Context, heartbeat, log, sleep, activityInfo } from '@temporalio/activity'; export async function longRunningActivity(items: string[]): Promise<void> { const info = activityInfo(); log.info('Starting activity', { activityId: info.activityId, attempt: info.attempt, totalItems: items.length, }); // Resume from last checkpoint if this is a retry let startIndex = 0; if (info.heartbeatDetails) { startIndex = info.heartbeatDetails as number; log.info('Resuming from checkpoint', { startIndex }); } for (let i = startIndex; i < items.length; i++) { // Check for cancellation Context.current().cancellationSignal.throwIfAborted(); // Process item... await processItem(items[i]); // Send heartbeat with progress checkpoint heartbeat(i + 1); log.info('Processed item', { index: i, item: items[i] }); } } export async function cancellableHttpActivity(url: string): Promise<string> { const response = await fetch(url, { // Pass cancellation signal to abort fetch if activity is cancelled signal: Context.current().cancellationSignal, }); return response.text(); } ``` ## ScheduleClient - Create and Manage Scheduled Workflows The `ScheduleClient` allows creating, updating, and managing scheduled workflow executions with cron-like patterns or calendar-based specifications. ```typescript import { Client } from '@temporalio/client'; const client = new Client(); // Create a schedule that runs a workflow daily at 9 AM const schedule = await client.schedule.create({ scheduleId: 'daily-report-schedule', spec: { calendars: [ { hour: 9, minute: 0, dayOfWeek: [1, 2, 3, 4, 5], // Monday through Friday }, ], }, action: { type: 'startWorkflow', workflowType: 'generateDailyReport', taskQueue: 'reports-queue', args: [{ reportType: 'daily-summary' }], }, policies: { overlap: 'SKIP', // Skip if previous run is still running catchupWindow: '1 hour', }, }); // Get schedule handle and interact with it const handle = client.schedule.getHandle('daily-report-schedule'); // Pause the schedule await handle.pause('Maintenance window'); // Trigger immediate execution await handle.trigger(); // Update the schedule await handle.update((prev) => ({ ...prev, spec: { calendars: [{ hour: 10, minute: 0 }], // Change to 10 AM }, })); // Resume the schedule await handle.unpause(); // Delete the schedule await handle.delete(); ``` ## TestWorkflowEnvironment - Testing with Time Skipping The `TestWorkflowEnvironment` provides a testing harness with time-skipping capabilities, allowing fast execution of workflows that contain long timers. ```typescript import { TestWorkflowEnvironment } from '@temporalio/testing'; import { Worker } from '@temporalio/worker'; import { Client } from '@temporalio/client'; import * as activities from './activities'; describe('Subscription Workflow', () => { let testEnv: TestWorkflowEnvironment; beforeAll(async () => { // Create time-skipping test environment testEnv = await TestWorkflowEnvironment.createTimeSkipping(); }); afterAll(async () => { await testEnv.teardown(); }); it('sends renewal reminder after 30 days', async () => { const worker = await Worker.create({ connection: testEnv.nativeConnection, taskQueue: 'test-queue', workflowsPath: require.resolve('./workflows'), activities: { ...activities, sendReminder: jest.fn(), // Mock the activity }, }); // Run worker until workflow completes // Time skipping makes this instant despite 30-day timer const result = await worker.runUntil( testEnv.client.workflow.execute('subscriptionRenewalWorkflow', { taskQueue: 'test-queue', workflowId: 'test-subscription', args: ['user-123'], }) ); expect(activities.sendReminder).toHaveBeenCalledWith( 'user-123', expect.stringContaining('expires') ); }); }); ``` ## MockActivityEnvironment - Unit Testing Activities The `MockActivityEnvironment` allows testing activities in isolation with full control over the activity context, including heartbeat handling and cancellation. ```typescript import { MockActivityEnvironment } from '@temporalio/testing'; import { longRunningActivity } from './activities'; describe('Long Running Activity', () => { it('can be resumed from heartbeat checkpoint', async () => { const env = new MockActivityEnvironment({ attempt: 2, heartbeatDetails: 5, // Simulate resuming from item 5 }); const items = ['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h']; await env.run(longRunningActivity, items); // Verify heartbeats were recorded expect(env.heartbeats.length).toBe(3); // Items 5, 6, 7 processed expect(env.heartbeats[2]).toBe(8); // Final checkpoint }); it('handles cancellation gracefully', async () => { const env = new MockActivityEnvironment(); // Cancel the activity after 100ms setTimeout(() => env.cancel(), 100); await expect( env.run(longRunningActivity, Array(1000).fill('item')) ).rejects.toThrow('CANCELLED'); }); }); ``` ## Worker.runReplayHistory - Replay Workflow History for Testing The `runReplayHistory` method validates that workflow code is compatible with existing execution histories, essential for detecting non-determinism bugs before deployment. ```typescript import { Worker } from '@temporalio/worker'; import { Client } from '@temporalio/client'; async function validateWorkflowCode() { const client = new Client(); // Get history from a completed workflow const handle = client.workflow.getHandle('order-12345'); const history = await handle.fetchHistory(); // Replay the history against current workflow code try { await Worker.runReplayHistory( { workflowsPath: require.resolve('./workflows'), }, history, 'order-12345' ); console.log('Workflow code is compatible with history'); } catch (error) { if (error.name === 'DeterminismViolationError') { console.error('Workflow code has non-determinism issues:', error.message); } else { throw error; } } } // Batch replay multiple histories async function validateAgainstProductionHistories() { const client = new Client(); // List recent workflow executions const workflows = client.workflow.list({ query: "WorkflowType = 'orderWorkflow' AND CloseTime > '2024-01-01'", }); const histories = (async function* () { for await (const workflow of workflows) { const handle = client.workflow.getHandle(workflow.workflowId, workflow.runId); yield { workflowId: workflow.workflowId, history: await handle.fetchHistory(), }; } })(); for await (const result of Worker.runReplayHistories( { workflowsPath: require.resolve('./workflows') }, histories )) { if (result.error) { console.error(`Replay failed for ${result.workflowId}:`, result.error); } } } ``` ## CancellationScope - Handle Workflow Cancellation `CancellationScope` provides fine-grained control over cancellation propagation within workflows, allowing you to protect critical sections or handle cleanup. ```typescript import { CancellationScope, proxyActivities, sleep } from '@temporalio/workflow'; import type * as activities from './activities'; const { processPayment, sendConfirmation, rollbackPayment } = proxyActivities<typeof activities>({ startToCloseTimeout: '5 minutes', }); export async function orderWorkflow(order: Order): Promise<void> { let paymentId: string | undefined; try { // This section can be cancelled paymentId = await processPayment(order); // Protect email sending from cancellation await CancellationScope.nonCancellable(async () => { await sendConfirmation(order.email, paymentId!); }); } catch (error) { if (CancellationScope.current().consideredCancelled) { // Handle cancellation cleanup if (paymentId) { // Ensure rollback completes even if workflow is cancelled await CancellationScope.nonCancellable(async () => { await rollbackPayment(paymentId!); }); } } throw error; } } // Racing multiple operations with cancellation export async function raceWorkflow(): Promise<string> { return await CancellationScope.cancellable(async () => { const winner = await Promise.race([ sleep('10 seconds').then(() => 'timeout'), someAsyncOperation().then(() => 'completed'), ]); // Cancel sibling operations when one completes CancellationScope.current().cancel(); return winner; }); } ``` ## workflowInfo - Access Workflow Runtime Information The `workflowInfo()` function returns information about the currently executing workflow, including IDs, timestamps, and search attributes. ```typescript import { workflowInfo, log } from '@temporalio/workflow'; export async function informativeWorkflow(): Promise<void> { const info = workflowInfo(); log.info('Workflow execution info', { workflowId: info.workflowId, runId: info.runId, workflowType: info.workflowType, taskQueue: info.taskQueue, namespace: info.namespace, attempt: info.attempt, firstExecutionRunId: info.firstExecutionRunId, continuedFromExecutionRunId: info.continuedFromExecutionRunId, startTime: info.startTime, runStartTime: info.runStartTime, historyLength: info.historyLength, searchAttributes: info.searchAttributes, memo: info.memo, }); // Use workflow ID for idempotency keys const idempotencyKey = `${info.workflowId}-${info.runId}-payment`; // Check if this is a retry if (info.attempt > 1) { log.warn('Workflow is being retried', { attempt: info.attempt }); } } ``` The Temporal TypeScript SDK is designed for building reliable, long-running applications that need to survive failures, handle complex orchestration, and maintain consistency across distributed systems. Common use cases include order processing pipelines, subscription lifecycle management, multi-step approval workflows, scheduled batch processing, saga patterns for distributed transactions, and human-in-the-loop workflows that may span days or weeks. Integration typically follows a pattern where the Client package is used in API servers or CLI tools to start and interact with workflows, while Workers run as separate processes (often in Kubernetes pods or container services) polling for tasks. Workflows are pure TypeScript functions that orchestrate activities, which perform the actual side effects. This separation ensures workflows remain deterministic and replayable while activities handle real-world integrations with databases, APIs, and external services. The testing package enables fast, reliable tests by providing time-skipping environments and mock contexts for both workflows and activities.