# -- RESULTING IMAGE --

FROM gcr.io/distroless/nodejs20-debian11

COPY --from=builder /app /app
WORKDIR /app

CMD ["node", "build/worker.js"]
```

### Properly configure Node.js memory in Docker

By default, `node` configures its maximum old-gen memory to 25% of the _physical memory_ of the machine on which it is executing, with a maximum of 4 GB.
This is likely inappropriate when running Node.js in a Docker environment and can result in either underusage of available memory (`node` only uses a fraction of the memory allocated to the container) or overusage (`node` tries to use more memory than what is allocated to the container, which will eventually lead to the process being killed by the operating system).

Therefore we recommended that you always explicitly set the `--max-old-space-size` `node` argument to approximately 80% of the maximum size (in megabytes) that you want to allocate the `node` process.
You might need some experimentation and adjustment to find the most appropriate value based on your specific application.

In practice, it is generally easier to provide this argument through the [`NODE_OPTIONS` environment variable](https://nodejs.org/api/cli.html#node_optionsoptions).

### Do not use Alpine

Alpine replaces glibc with musl, which is incompatible with the Rust core of the TypeScript SDK.
If you receive errors like the following, it's probably because you are using Alpine.

```sh
Error: Error loading shared library ld-linux-x86-64.so.2: No such file or directory (needed by /opt/app/node_modules/@temporalio/core-bridge/index.node)
```

Or like this:

```sh
Error: Error relocating /opt/app/node_modules/@temporalio/core-bridge/index.node: __register_atfork: symbol not found
```

## How to run a Temporal Cloud Worker {#run-a-temporal-cloud-worker}

To run a Worker that uses [Temporal Cloud](/cloud), you need to provide additional connection and client options that include the following:

- An address that includes your [Cloud Namespace Name](/namespaces) and a port number: `<Namespace>.<ID>.tmprl.cloud:<port>`.
- mTLS CA certificate.
- mTLS private key.

For more information about managing and generating client certificates for Temporal Cloud, see [How to manage certificates in Temporal Cloud](/cloud/certificates).

For more information about configuring TLS to secure inter- and intra-network communication for a Temporal Service, see [Temporal Customization Samples](https://github.com/temporalio/samples-server).

### How to register types {#register-types}

All Workers listening to the same Task Queue name must be registered to handle the exact same Workflows Types and Activity Types.

If a Worker polls a Task for a Workflow Type or Activity Type it does not know about, it fails that Task.
However, the failure of the Task does not cause the associated Workflow Execution to fail.

In development, use [`workflowsPath`](https://typescript.temporal.io/api/interfaces/worker.WorkerOptions/#workflowspath):

<!--SNIPSTART typescript-worker-create -->
[snippets/src/worker.ts](https://github.com/temporalio/samples-typescript/blob/main/snippets/src/worker.ts)
```ts

async function run() {
  const worker = await Worker.create({
    workflowsPath: require.resolve('./workflows'),
    taskQueue: 'snippets',
    activities,
  });

  await worker.run();
}
```
<!--SNIPEND-->

In this snippet, the Worker bundles the Workflow code at runtime.

In production, you can improve your Worker's startup time by bundling in advance: as part of your production build, call `bundleWorkflowCode`:

<!--SNIPSTART typescript-bundle-workflow -->
[production/src/scripts/build-workflow-bundle.ts](https://github.com/temporalio/samples-typescript/blob/main/production/src/scripts/build-workflow-bundle.ts)
```ts

async function bundle() {
  const { code } = await bundleWorkflowCode({
    workflowsPath: require.resolve('../workflows'),
  });
  const codePath = path.join(__dirname, '../../workflow-bundle.js');

  await writeFile(codePath, code);
  console.log(`Bundle written to ${codePath}`);
}
```
<!--SNIPEND-->

Then the bundle can be passed to the Worker:

<!--SNIPSTART typescript-production-worker-->
[production/src/worker.ts](https://github.com/temporalio/samples-typescript/blob/main/production/src/worker.ts)
```ts
const workflowOption = () =>
  process.env.NODE_ENV === 'production'
    ? {
        workflowBundle: {
          codePath: require.resolve('../workflow-bundle.js'),
        },
      }
    : { workflowsPath: require.resolve('./workflows') };

async function run() {
  const worker = await Worker.create({
    ...workflowOption(),
    activities,
    taskQueue: 'production-sample',
  });

  await worker.run();
}
```
<!--SNIPEND-->

## How to shut down a Worker and track its state {#shut-down-a-worker}

Workers shut down if they receive any of the Signals enumerated in [shutdownSignals](https://typescript.temporal.io/api/interfaces/worker.RuntimeOptions#shutdownsignals): `'SIGINT'`, `'SIGTERM'`, `'SIGQUIT'`, and `'SIGUSR2'`.

In development, we shut down Workers with `Ctrl+C` (`SIGINT`) or [nodemon](https://github.com/temporalio/samples-typescript/blob/c37bae3ea235d1b6956fcbe805478aa46af973ce/hello-world/package.json#L10) (`SIGUSR2`). In production, you usually want to give Workers time to finish any in-progress Activities by setting [shutdownGraceTime](https://typescript.temporal.io/api/interfaces/worker.WorkerOptions#shutdowngracetime).

As soon as a Worker receives a shutdown Signal or request, the Worker stops polling for new Tasks and allows in-flight Tasks to complete until `shutdownGraceTime` is reached.
Any Activities that are still running at that time will stop running and will be rescheduled by Temporal Server when an Activity timeout occurs.

If you must guarantee that the Worker eventually shuts down, you can set [shutdownForceTime](https://typescript.temporal.io/api/interfaces/worker.WorkerOptions#shutdownforcetime).

You might want to programmatically shut down Workers (with [Worker.shutdown()](https://typescript.temporal.io/api/classes/worker.Worker#shutdown)) in integration tests or when automating a fleet of Workers.

### Worker states

At any time, you can Query Worker state with [Worker.getState()](https://typescript.temporal.io/api/classes/worker.Worker#getstate).
A Worker is always in one of seven states:

- `INITIALIZED`: The initial state of the Worker after calling [Worker.create()](https://typescript.temporal.io/api/classes/worker.Worker#create) and successfully connecting to the server.
- `RUNNING`: [Worker.run()](https://typescript.temporal.io/api/classes/worker.Worker#run) was called and the Worker is polling Task Queues.
- `FAILED`: The Worker encountered an unrecoverable error; `Worker.run()` should reject with the error.
- The last four states are related to the Worker shutdown process:
  - `STOPPING`: The Worker received a shutdown Signal or `Worker.shutdown()` was called.
    The Worker will forcefully shut down after `shutdownGraceTime` expires.
  - `DRAINING`: All Workflow Tasks have been drained; waiting for Activities and cached Workflows eviction.
  - `DRAINED`: All Activities and Workflows have completed; ready to shut down.
  - `STOPPED`: Shutdown complete; `worker.run()` resolves.

If you need more visibility into internal Worker state, see the [Worker class](https://typescript.temporal.io/api/classes/worker.Worker) in the API reference.

## How to start a Workflow Execution {#start-workflow-execution}

[Workflow Execution](/workflow-execution) semantics rely on several parameters—that is, to start a Workflow Execution you must supply a Task Queue that will be used for the Tasks (one that a Worker is polling), the Workflow Type, language-specific contextual data, and Workflow Function parameters.

In the examples below, all Workflow Executions are started using a Temporal Client.
To spawn Workflow Executions from within another Workflow Execution, use either the [Child Workflow](/develop/typescript/child-workflows) or External Workflow APIs.

See the [Customize Workflow Type](#workflow-type) section to see how to customize the name of the Workflow Type.

A request to spawn a Workflow Execution causes the Temporal Service to create the first Event ([WorkflowExecutionStarted](/references/events#workflowexecutionstarted)) in the Workflow Execution Event History.
The Temporal Service then creates the first Workflow Task, resulting in the first [WorkflowTaskScheduled](/references/events#workflowtaskscheduled) Event.

When you have a Client, you can schedule the start of a Workflow with `client.workflow.start()`, specifying `workflowId`, `taskQueue`, and `args` and returning a Workflow handle immediately after the Server acknowledges the receipt.

```typescript
const handle = await client.workflow.start(example, {
  workflowId: 'your-workflow-id',
  taskQueue: 'your-task-queue',
  args: ['argument01', 'argument02', 'argument03'], // this is typechecked against workflowFn's args
});
const handle = client.getHandle(workflowId);
const result = await handle.result();
```

Calling `client.workflow.start()` and `client.workflow.execute()` send a command to Temporal Server to schedule a new Workflow Execution on the specified Task Queue. It does not actually start until a Worker that has a matching Workflow Type, polling that Task Queue, picks it up.

You can test this by executing a Client command without a matching Worker. Temporal Server records the command in Event History, but does not make progress with the Workflow Execution until a Worker starts polling with a matching Task Queue and Workflow Definition.

Workflow Execution run in a separate V8 isolate context in order to provide a [deterministic runtime](/workflow-definition#deterministic-constraints).

### How to set a Workflow's Task Queue {#set-task-queue}

In most SDKs, the only Workflow Option that must be set is the name of the [Task Queue](/task-queue).

For any code to execute, a Worker Process must be running that contains a Worker Entity that is polling the same Task Queue name.

A Task Queue is a dynamic queue in Temporal polled by one or more Workers.

Workers bundle Workflow code and node modules using Webpack v5 and execute them inside V8 isolates.
Activities are directly required and run by Workers in the Node.js environment.

Workers are flexible.
You can host any or all of your Workflows and Activities on a Worker, and you can host multiple Workers on a single machine.

The Worker need three main things:

- `taskQueue`: The Task Queue to poll. This is the only required argument.
- `activities`: Optional. Imported and supplied directly to the Worker.
- Workflow bundle. Choose one of the following options:
  - Specify `workflowsPath` pointing to your `workflows.ts` file to pass to Webpack; for example, `require.resolve('./workflows')`.
    Workflows are bundled with their dependencies.
  - If you prefer to handle the bundling yourself, pass a prebuilt bundle to `workflowBundle`.

```ts

async function run() {
  // Step 1: Register Workflows and Activities with the Worker and connect to
  // the Temporal server.
  const worker = await Worker.create({
    workflowsPath: require.resolve('./workflows'),
    activities,
    taskQueue: 'hello-world',
  });
  // Worker connects to localhost by default and uses console.error for logging.
  // Customize the Worker by passing more options to create():
  // https://typescript.temporal.io/api/classes/worker.Worker
  // If you need to configure server connection parameters, see docs:
  // /typescript/security#encryption-in-transit-with-mtls

  // Step 2: Start accepting tasks on the `tutorial` queue
  await worker.run();
}

run().catch((err) => {
  console.error(err);
  process.exit(1);
});
```

`taskQueue` is the only required option; however, use `workflowsPath` and `activities` to register Workflows and Activities with the Worker.

When scheduling a Workflow, you must specify `taskQueue`.

```ts

// This is the code that is used to start a Workflow.
const connection = await Connection.create();
const client = new Client({ connection });
const result = await client.workflow.execute(yourWorkflow, {
  // required
  taskQueue: 'your-task-queue',
  // required
  workflowId: 'your-workflow-id',
});
```

When creating a Worker, you must pass the `taskQueue` option to the `Worker.create()` function.

```ts
const worker = await Worker.create({
  // imported elsewhere
  activities,
  taskQueue: 'your-task-queue',
});
```

Optionally, in Workflow code, when calling an Activity, you can specify the Task Queue by passing the `taskQueue` option to `proxyActivities()`, `startChild()`, or `executeChild()`.
If you do not specify `taskQueue`, the TypeScript SDK places Activity and Child Workflow Tasks in the same Task Queue as the Workflow Task Queue.

### How to set a Workflow Id {#workflow-id}

Although it is not required, we recommend providing your own [Workflow Id](/workflow-execution/workflowid-runid#workflow-id)that maps to a business process or business entity identifier, such as an order identifier or customer identifier.

Connect to a Client with `client.workflow.start()` and any arguments. Then specify your `taskQueue` and set your `workflowId` to a meaningful business identifier.

```typescript
const handle = await client.workflow.start(example, {
  workflowId: 'yourWorkflowId',
  taskQueue: 'yourTaskQueue',
  args: ['your', 'arg', 'uments'],
});
```

This starts a new Client with the given Workflow Id, Task Queue name, and an argument.

### How to get the results of a Workflow Execution {#get-workflow-results}

If the call to start a Workflow Execution is successful, you will gain access to the Workflow Execution's Run Id.

The Workflow Id, Run Id, and Namespace may be used to uniquely identify a Workflow Execution in the system and get its result.

It's possible to both block progress on the result (synchronous execution) or get the result at some other point in time (asynchronous execution).

In the Temporal Platform, it's also acceptable to use Queries as the preferred method for accessing the state and results of Workflow Executions.

To return the results of a Workflow Execution:

```typescript
return (
  'Completed '
  + wf.workflowInfo().workflowId
  + ', Total Charged: '
  + totalCharged
);
```

`totalCharged` is just a function declared in your code. For a full example, see [subscription-workflow-project-template-typescript/src/workflows.ts](https://github.com/temporalio/subscription-workflow-project-template-typescript/blob/main/src/workflows.ts).

A Workflow function may return a result. If it doesn't (in which case the return type is `Promise<void>`), the result will be `undefined`.

If you started a Workflow with `client.workflow.start()`, you can choose to wait for the result anytime with `handle.result()`.

```typescript
const handle = client.getHandle(workflowId);
const result = await handle.result();
```

Using a Workflow Handle isn't necessary with `client.workflow.execute()`.

Workflows that prematurely end will throw a `WorkflowFailedError` if you call `result()`.

If you call `result()` on a Workflow that prematurely ended for some reason, it throws a [`WorkflowFailedError` error](https://typescript.temporal.io/api/classes/client.WorkflowFailedError/) that reflects the reason. For that reason, it is recommended to catch that error.

```typescript
const handle = client.getHandle(workflowId);
try {
  const result = await handle.result();
} catch (err) {
  if (err instanceof WorkflowFailedError) {
    throw new Error('Temporal workflow failed: ' + workflowId, {
      cause: err,
    });
  } else {
    throw new Error('error from Temporal workflow ' + workflowId, {
      cause: err,
    });
  }
}
```

## Cancellation scopes in Typescript {#cancellation-scopes}

In the TypeScript SDK, Workflows are represented internally by a tree of cancellation scopes, each with cancellation behaviors you can specify.
By default, everything runs in the "root" scope.

Scopes are created using the [CancellationScope](https://typescript.temporal.io/api/classes/workflow.CancellationScope) constructor or one of three static helpers:

- [cancellable(fn)](https://typescript.temporal.io/api/classes/workflow.CancellationScope#cancellable-1): Children are automatically cancelled when their containing scope is cancelled.
  - Equivalent to `new CancellationScope().run(fn)`.
- [nonCancellable(fn)](https://typescript.temporal.io/api/classes/workflow.CancellationScope#noncancellable): Cancellation does not propagate to children.
  - Equivalent to `new CancellationScope({ cancellable: false }).run(fn)`.
- [withTimeout(timeoutMs, fn)](https://typescript.temporal.io/api/classes/workflow.CancellationScope#withtimeout): If a timeout triggers before `fn` resolves, the scope is cancelled, triggering cancellation of any enclosed operations, such as Activities and Timers.
  - Equivalent to `new CancellationScope({ cancellable: true, timeout: timeoutMs }).run(fn)`.

Cancellations are applied to cancellation scopes, which can encompass an entire Workflow or just part of one.
Scopes can be nested, and cancellation propagates from outer scopes to inner ones.
A Workflow's `main` function runs in the outermost scope.
Cancellations are handled by catching `CancelledFailure`s thrown by cancelable operations.

`CancellationScope.run()` and the static helpers mentioned earlier return native JavaScript promises, so you can use the familiar Promise APIs like `Promise.all` and `Promise.race` to model your asynchronous logic.
You can also use the following APIs:

- `CancellationScope.current()`: Get the current scope.
- `scope.cancel()`: Cancel all operations inside a `scope`.
- `scope.run(fn)`: Run an async function within a `scope` and return the result of `fn`.
- `scope.cancelRequested`: A promise that resolves when a scope cancellation is requested, such as when Workflow code calls `cancel()` or the entire Workflow is cancelled by an external client.

When a `CancellationScope` is cancelled, it propagates cancellation in any child scopes and of any cancelable operations created within it, such as the following:

- Activities
- Timers (created with the [sleep](https://typescript.temporal.io/api/namespaces/workflow#sleep) function)
- [Triggers](https://typescript.temporal.io/api/classes/workflow.Trigger)

### CancelledFailure

Timers and triggers throw [CancelledFailure](https://typescript.temporal.io/api/classes/common.CancelledFailure) when cancelled; Activities and Child Workflows throw `ActivityFailure` and `ChildWorkflowFailure` with cause set to `CancelledFailure`.
One exception is when an Activity or Child Workflow is scheduled in an already cancelled scope (or Workflow).
In this case, they propagate the `CancelledFailure` that was thrown to cancel the scope.

To simplify checking for cancellation, use the [isCancellation(err)](https://typescript.temporal.io/api/namespaces/workflow#iscancellation) function.

### Internal cancellation example

<!--SNIPSTART typescript-cancel-a-timer-from-workflow-->
[packages/test/src/workflows/cancel-timer-immediately.ts](https://github.com/temporalio/sdk-typescript/blob/main/packages/test/src/workflows/cancel-timer-immediately.ts)
```ts

export async function cancelTimer(): Promise<void> {
  // Timers and Activities are automatically cancelled when their containing scope is cancelled.
  try {
    await CancellationScope.cancellable(async () => {
      const promise = sleep(1); // <-- Will be cancelled because it is attached to this closure's scope
      CancellationScope.current().cancel();
      await promise; // <-- Promise must be awaited in order for `cancellable` to throw
    });
  } catch (e) {
    if (e instanceof CancelledFailure) {
      console.log('Timer cancelled 👍');
    } else {
      throw e; // <-- Fail the workflow
    }
  }
}
```
<!--SNIPEND-->

Alternatively, the preceding can be written as the following.

<!--SNIPSTART typescript-cancel-a-timer-from-workflow-alternative-impl-->
[packages/test/src/workflows/cancel-timer-immediately-alternative-impl.ts](https://github.com/temporalio/sdk-typescript/blob/main/packages/test/src/workflows/cancel-timer-immediately-alternative-impl.ts)
```ts

export async function cancelTimerAltImpl(): Promise<void> {
  try {
    const scope = new CancellationScope();
    const promise = scope.run(() => sleep(1));
    scope.cancel(); // <-- Cancel the timer created in scope
    await promise; // <-- Throws CancelledFailure
  } catch (e) {
    if (e instanceof CancelledFailure) {
      console.log('Timer cancelled 👍');
    } else {
      throw e; // <-- Fail the workflow
    }
  }
}
```
<!--SNIPEND-->

### External cancellation example

The following code shows how to handle Workflow cancellation by an external client while an Activity is running.

{/* TODO: add a sample here of how this Workflow could be cancelled using a WorkflowHandle */}

<!--SNIPSTART typescript-handle-external-workflow-cancellation-while-activity-running-->
[packages/test/src/workflows/handle-external-workflow-cancellation-while-activity-running.ts](https://github.com/temporalio/sdk-typescript/blob/main/packages/test/src/workflows/handle-external-workflow-cancellation-while-activity-running.ts)
```ts

const { httpPostJSON, cleanup } = proxyActivities<typeof activities>({
  startToCloseTimeout: '10m',
});

export async function handleExternalWorkflowCancellationWhileActivityRunning(url: string, data: any): Promise<void> {
  try {
    await httpPostJSON(url, data);
  } catch (err) {
    if (isCancellation(err)) {
      console.log('Workflow cancelled');
      // Cleanup logic must be in a nonCancellable scope
      // If we'd run cleanup outside of a nonCancellable scope it would've been cancelled
      // before being started because the Workflow's root scope is cancelled.
      await CancellationScope.nonCancellable(() => cleanup(url));
    }
    throw err; // <-- Fail the Workflow
  }
}
```
<!--SNIPEND-->

### nonCancellable example

`CancellationScope.nonCancellable` prevents cancellation from propagating to children.

<!--SNIPSTART typescript-non-cancellable-shields-children-->
[activities-cancellation-heartbeating/src/cancellation-scopes.ts](https://github.com/temporalio/samples-typescript/blob/main/activities-cancellation-heartbeating/src/cancellation-scopes.ts)
```ts
export async function nonCancellable(url: string): Promise<any> {
  // Prevent Activity from being cancelled and await completion.
  // Note that the Workflow is completely oblivious and impervious to cancellation in this example.
  return CancellationScope.nonCancellable(() => httpGetJSON(url));
}
```
<!--SNIPEND-->

### withTimeout example

A common operation is to cancel one or more Activities if a deadline elapses.
`withTimeout` creates a `CancellationScope` that is automatically cancelled after a timeout.

<!--SNIPSTART typescript-multiple-activities-single-timeout-workflow-->
[packages/test/src/workflows/multiple-activities-single-timeout.ts](https://github.com/temporalio/sdk-typescript/blob/main/packages/test/src/workflows/multiple-activities-single-timeout.ts)
```ts

export function multipleActivitiesSingleTimeout(urls: string[], timeoutMs: number): Promise<any> {
  const { httpGetJSON } = proxyActivities<typeof activities>({
    startToCloseTimeout: timeoutMs,
  });

  // If timeout triggers before all activities complete
  // the Workflow will fail with a CancelledError.
  return CancellationScope.withTimeout(timeoutMs, () => Promise.all(urls.map((url) => httpGetJSON(url))));
}
```
<!--SNIPEND-->

### scope.cancelRequested

You can await `cancelRequested` to make a Workflow aware of cancellation while waiting on `nonCancellable` scopes.

<!--SNIPSTART typescript-cancel-requested-with-non-cancellable-->
[packages/test/src/workflows/cancel-requested-with-non-cancellable.ts](https://github.com/temporalio/sdk-typescript/blob/main/packages/test/src/workflows/cancel-requested-with-non-cancellable.ts)
```ts

const { httpGetJSON } = proxyActivities<typeof activities>({
  startToCloseTimeout: '10m',
});

export async function resumeAfterCancellation(url: string): Promise<any> {
  let result: any = undefined;
  const scope = new CancellationScope({ cancellable: false });
  const promise = scope.run(() => httpGetJSON(url));
  try {
    result = await Promise.race([scope.cancelRequested, promise]);
  } catch (err) {
    if (!(err instanceof CancelledFailure)) {
      throw err;
    }
    // Prevent Workflow from completing so Activity can complete
    result = await promise;
  }
  return result;
}
```
<!--SNIPEND-->

### Cancellation scopes and callbacks

Callbacks are not particularly useful in Workflows because all meaningful asynchronous operations return promises.
In the rare case that code uses callbacks and needs to handle cancellation, a callback can consume the `CancellationScope.cancelRequested` promise.

<!--SNIPSTART typescript-cancellation-scopes-with-callbacks-->
[packages/test/src/workflows/cancellation-scopes-with-callbacks.ts](https://github.com/temporalio/sdk-typescript/blob/main/packages/test/src/workflows/cancellation-scopes-with-callbacks.ts)
```ts

function doSomething(callback: () => any) {
  setTimeout(callback, 10);
}

export async function cancellationScopesWithCallbacks(): Promise<void> {
  await new Promise<void>((resolve, reject) => {
    doSomething(resolve);
    CancellationScope.current().cancelRequested.catch(reject);
  });
}
```
<!--SNIPEND-->

### Nesting cancellation scopes

You can achieve complex flows by nesting cancellation scopes.

<!--SNIPSTART typescript-nested-cancellation-scopes-->
[packages/test/src/workflows/nested-cancellation.ts](https://github.com/temporalio/sdk-typescript/blob/main/packages/test/src/workflows/nested-cancellation.ts)
```ts

const { setup, httpPostJSON, cleanup } = proxyActivities<typeof activities>({
  startToCloseTimeout: '10m',
});

export async function nestedCancellation(url: string): Promise<void> {
  await CancellationScope.cancellable(async () => {
    await CancellationScope.nonCancellable(() => setup());
    try {
      await CancellationScope.withTimeout(1000, () => httpPostJSON(url, { some: 'data' }));
    } catch (err) {
      if (isCancellation(err)) {
        await CancellationScope.nonCancellable(() => cleanup(url));
      }
      throw err;
    }
  });
}
```
<!--SNIPEND-->

### Sharing promises between scopes

Operations like Timers and Activities are cancelled by the cancellation scope they were created in.
Promises returned by these operations can be awaited in different scopes.

<!--SNIPSTART typescript-shared-promise-scopes-->
[activities-cancellation-heartbeating/src/cancellation-scopes.ts](https://github.com/temporalio/samples-typescript/blob/main/activities-cancellation-heartbeating/src/cancellation-scopes.ts)
```ts
export async function sharedScopes(): Promise<any> {
  // Start activities in the root scope
  const p1 = httpGetJSON('http://url1.ninja');
  const p2 = httpGetJSON('http://url2.ninja');

  const scopePromise = CancellationScope.cancellable(async () => {
    const first = await Promise.race([p1, p2]);
    // Does not cancel activity1 or activity2 as they're linked to the root scope
    CancellationScope.current().cancel();
    return first;
  });
  return await scopePromise;
  // The Activity that did not complete will effectively be cancelled when
  // Workflow completes unless the Activity is awaited:
  // await Promise.all([p1, p2]);
}
```
<!--SNIPEND-->

<!--SNIPSTART typescript-shield-awaited-in-root-scope-->
[activities-cancellation-heartbeating/src/cancellation-scopes.ts](https://github.com/temporalio/samples-typescript/blob/main/activities-cancellation-heartbeating/src/cancellation-scopes.ts)
```ts
export async function shieldAwaitedInRootScope(): Promise<any> {
  let p: Promise<any> | undefined = undefined;

  await CancellationScope.nonCancellable(async () => {
    p = httpGetJSON('http://example.com'); // <-- Start activity in nonCancellable scope without awaiting completion
  });
  // Activity is shielded from cancellation even though it is awaited in the cancellable root scope
  return p;
}
```
<!--SNIPEND-->

---

## Debugging - TypeScript SDK

The Debugging section of the Temporal TypeScript SDK developer's guide covers tools for debugging and how to troubleshoot common issues.

## How to debug in a development environment {#debug-in-a-development-environment}

In addition to the normal development tools of logging and a debugger, you can also see what's happening in your Workflow by using the [Web UI](/web-ui) or [Temporal CLI](/cli).

## How to debug in a production environment {#debug-in-a-production-environment}

You can debug production Workflows using:

- [Web UI](/web-ui)
- [Temporal CLI](/cli)
- [Replay](/develop/typescript/testing-suite#replay)
- [Tracing](/develop/typescript/observability#tracing)
- [Logging](/develop/typescript/observability#logging)

You can debug and tune Worker performance with metrics and the [Worker performance guide](/develop/worker-performance).
For information on setting up SDK metrics, see [Metrics](/develop/typescript/observability#metrics) in the Observability section of the TypeScript SDK developer's guide.

Debug Server performance with [Cloud metrics](/cloud/metrics/) or [self-hosted Server metrics](/self-hosted-guide/production-checklist#scaling-and-metrics).

## How to troubleshoot common issues in the TypeScript SDK {#troubleshoot-common-issues}

{/* The following was ported from \docs-src\typescript\troubleshooting.md */}

### Two locations to watch

- Workflow Errors are reflected in Temporal Web.
- Worker errors and logs are reflected in the terminal.

If something isn't behaving the way you expect, make sure to check both locations for helpful error messages.

### Stale Workflows

If you are developing Workflows and finding that code isn't executing as expected, the first place to look is whether old Workflows are still running.

If those old Workflows have the same name and are on the same task queue, Temporal will try to continue executing them on your new code by design.
You may get errors that make no sense to you because

- Temporal is trying to execute old Workflow code that no longer exists in your codebase, or
- your new Client code is expecting Temporal to execute old Workflow/Activity code it doesn't yet know about.

The biggest sign that this is happening is if you notice Temporal is acting non-deterministically: running the same Workflow twice gets different results.

Stale workflows are usually a non-issue because the errors generated are just noise from code you no longer want to run.
If you need to terminate old stale Workflows, you can do so with Temporal Web or the Temporal CLI.

### Workflow/Activity registration errors

**If your Workflows or Activities are not imported or spelled correctly**, here are some errors we've seen:

- `ApplicationFailure: 'MyFunction' is not a function`
- `Workflow did not register a handler for MyQuery`

Double check that your Workers are registering the right Workflow and Activity Definitions (function names) on the right Task Queues.

**If you are running Temporal in a monorepo**, then your `node_modules` may be in a different location than where Temporal expects to find it by default, which results in errors like:

```bash
[ERROR] Module not found: Error: Can't resolve '@temporalio/workflow/lib/worker-interface.js' in '/src'
```

Our [Next.js tutorial](https://learn.temporal.io/tutorials/typescript/nextjs) is written for people setting up Temporal **within an existing monorepo**, which may be of use here.

When you pass a `workflowsPath`, our Webpack config expects to find `node_modules` in the same or a parent/ancestor directory.

**If you are custom bundling your own Workflows** you may get errors like these:

```bash
[ERROR] Failed to activate workflow {
  runId: 'aaf84a83-51ce-462a-9ab7-6a641a703bff',
  error: ReferenceError: exports is not defined,
  workflowExists: false
}
```

Temporal Workflow Bundles need to [export a set of methods that fit the compiled `worker-interface.ts` from `@temporalio/workflow`](https://github.com/temporalio/sdk-typescript/blob/eaa2d205c9bc5ff4a3b17c0b34f2dcf6b1e0264a/packages/worker/src/workflow/bundler.ts#L81) as an entry point.
We do offer a `bundleWorkflowCode` method to assist you with this, though it uses our Webpack settings.
For more information, see the [Register types](/develop/typescript/core-application#register-types) section.

### Webpack errors

The TypeScript SDK's Worker bundles Workflows based on `workflowsPath` with [Webpack](https://webpack.js.org/) and run them inside v8 isolates.

If Webpack fails to create the bundle, the SDK will throw an error and emit webpack logs using the SDK's [logger](/develop/typescript/observability#logging).

If you do not see Webpack output in your terminal make sure that you have not disabled SDK logging (see reference to `Runtime.install()` in the link above).

**A common mistake for newcomers to the TypeScript SDK is trying to use Node.js built-ins and modules in their Workflow code.** Usually, the best thing to do is move that code to an Activity.

Some common examples that will **not** work in the Workflow isolate:

<details>
    <summary>
    Importing node built-in modules
    </summary>

:::danger Antipattern

```ts

const config = fs.readFileSync('config.json', 'utf8');
```

:::

This is invalid because reading from the filesystem is a non-deterministic operation: the file may change from the time of the original Workflow Execution to when the Workflow is replayed.

You'll typically see an error in this form in the Webpack output:

```
2021-10-14T19:22:00.606Z [INFO] Module not found: Error: Can't resolve 'fs' in '/Users/you/your-project/src'
2021-10-14T19:22:00.606Z [INFO] resolve 'fs' in '/Users/you/your-project/src'
2021-10-14T19:22:00.606Z [INFO]   Parsed request is a module
2021-10-14T19:22:00.606Z [INFO]   using description file: /Users/you/your-project/package.json (relative path: ./src)
2021-10-14T19:22:00.606Z [INFO]     Field 'browser' doesn't contain a valid alias configuration
```

</details>

<details>
    <summary>
    Importing and calling Activities directly from Workflow code
    </summary>

:::danger Antipattern

```ts

export async function yourWorkflow(): Promise<string> {
  return await makeHTTPRequest('https://temporal.io');
}
```

:::

This is invalid because activity implementations should not be directly referenced by Workflow code.
Activities are used by Workflows in order make network calls and reading from the filesystem, operations which are non-deterministic by nature because they rely on external state.
Temporal records Activity results in the Workflow history and in case your Workflow is replayed, completed Activities will not be rerun, instead their recorded result will be delivered to the Workflow.

You'll typically see an error in this form in the Webpack output:

```
2021-10-14T19:46:52.731Z [INFO] ERROR in ./src/activities.ts 8:31-46
2021-10-14T19:46:52.731Z [INFO] Module not found: Error: Can't resolve 'http' in '/Users/you/your-project/src'
2021-10-14T19:46:52.731Z [INFO]
2021-10-14T19:46:52.731Z [INFO] BREAKING CHANGE: webpack < 5 used to include polyfills for node.js core modules by default.
2021-10-14T19:46:52.731Z [INFO] This is no longer the case. Verify if you need this module and configure a polyfill for it.
2021-10-14T19:46:52.731Z [INFO]
2021-10-14T19:46:52.731Z [INFO] If you want to include a polyfill, you need to:
2021-10-14T19:46:52.731Z [INFO]         - add a fallback 'resolve.fallback: { "http": require.resolve("stream-http") }'
2021-10-14T19:46:52.731Z [INFO]         - install 'stream-http'
2021-10-14T19:46:52.731Z [INFO] If you don't want to include a polyfill, you can use an empty module like this:
2021-10-14T19:46:52.731Z [INFO]         resolve.fallback: { "http": false }
```

To properly call your Activities from Workflow code use `proxyActivities` and make sure to only import the Activity types.

```ts

const { makeHTTPRequest } = proxyActivities<typeof activities>();

export async function yourWorkflow(): Promise<string> {
  return await makeHTTPRequest('https://temporal.io');
}
```

</details>

### Works in Dev but not in Prod

The two main sources of dev-prod discrepancies are in bundling and connecting.

#### Production bundling

You may experience your Client sending stripped names as the Workflow "Type" when scheduling a Workflow.
Webpack can change the Workflow's function name to something shorter.
Temporal won't know how to handle the mismatch between the shorter name and the expect Workflow type.

You may experience errors like this:

```
Error: 3 INVALID_ARGUMENT: WorkflowType is not set on request.
```

Or you may see shorter names in the Temporal Service's Web UI when Webpack changed the Workflow's function name to something shorter, in this case the single letter 's':

<CaptionedImage
    src="/img/webui/stripped_workflow_types_in_webui.png"
    title="Temporal Web UI showing stripped 'Workflow Type' entries, in this case the single letter 's'"
/>

This issue can happen when your bundler strips out Workflow function names.
Temporal relies on those names to set the "Workflow Type" in the Service Web UI.
To prevent the build process from shortening Workflow function names, modify the webpack configuration file ( `webpack.config.js`) to set the Boolean that retains the original names in the `TerserPlugin` configuration section.
Setting the option (`keep_fnames`) to `true` prevents name stripping.

<Tabs
defaultValue="webpackterser"
values={[
{label: 'Webpack with Terser', value: 'webpackterser'},
{label: 'ESbuild', value: 'esbuild'},
]
}>

<TabItem value="webpackterser">

```js
// webpack.config.js
module.exports = {
  optimization: {
    minimize: true,
    minimizer: [
      new TerserPlugin({
        terserOptions: {
          keep_fnames: true, // don't strip function names in production
        },
      }),
    ],
  },
};
```

</TabItem>
<TabItem value="esbuild">

```js
require('esbuild').buildSync({
  entryPoints: ['app.js'],
  minify: true,
  keepNames: true,
  outfile: 'out.js',
});
```

See the [esbuild docs](https://esbuild.github.io/api/#keep-names) for more information.
</TabItem>
</Tabs>

#### Connecting to Temporal Server

If you are trying to connect in production and getting this:

```bash
[TransportError: transport error]
```

It is a sign that something is wrong with your Cert/Key pair.
Log it out and make sure it is an exact match with what is expected (often, the issue can be whitespace when injecting from your production secrets management environment).

### Resetting Workflows to deal with logical bugs

You can "rewind time" using the Temporal CLI, resetting Workflow History to some previous point in time. You can read the Temporal CLI docs on:

- [Restarting and resetting Workflows by ID](/cli)
- [Resetting all Workflows by binary checksum identifier](/cli)

If you need to reset programmatically, the TS SDK does not have any high level APIs for this, but you can make raw gRPC calls to [resetWorkflowExecution](https://typescript.temporal.io/api/classes/proto.temporal.api.workflowservice.v1.WorkflowService-1/#resetworkflowexecution).

Resetting should only be used to deal with serious logical bugs in your code: it's not for handling transient failures, like a downstream service being unreachable. It should not be used in the course of normal application flows.

### gRPC call timeouts (context deadline exceeded)

The opaque `context deadline exceeded` error comes from `gRPC`:

```
Error: 4 DEADLINE_EXCEEDED: context deadline exceeded
    at Object.callErrorFromStatus (/Users/swyx/Work/Temporal/samples-typescript/nextjs-oneclick/node_modules/@grpc/grpc-js/build/src/call.js:31:26)
    at Object.onReceiveStatus (/Users/swyx/Work/Temporal/samples-typescript/nextjs-oneclick/node_modules/@grpc/grpc-js/build/src/client.js:179:52)
    at Object.onReceiveStatus (/Users/swyx/Work/Temporal/samples-typescript/nextjs-oneclick/node_modules/@grpc/grpc-js/build/src/client-interceptors.js:336:141)
    at Object.onReceiveStatus (/Users/swyx/Work/Temporal/samples-typescript/nextjs-oneclick/node_modules/@grpc/grpc-js/build/src/client-interceptors.js:299:181)
    at /Users/swyx/Work/Temporal/samples-typescript/nextjs-oneclick/node_modules/@grpc/grpc-js/build/src/call-stream.js:145:78
    at processTicksAndRejections (node:internal/process/task_queues:78:11) {
  code: 4,
  details: 'context deadline exceeded',
  metadata: Metadata {
    internalRepr: Map(1) { 'content-type' => [Array] },
    options: {}
  },
  page: '/api/getBuyState'
}
```

Several conditions can cause this error, including network hiccups, timeouts that are too short, and an overloaded server.
Querying a Workflow Execution whose query handler causes an error can result in the query call timing out.

Some troubleshooting actions you can take:

- Verify the connection from your Worker to the Temporal Server is working and doesn't have unusually high latency.
- If you are running Temporal Server yourself, check your [server metrics](/self-hosted-guide/production-checklist#scaling-and-metrics) to ensure it's not overloaded.
- If what's timing out is a query, check the logs of your Workers to see if they are having issues handling the query.

If none of the preceding actions help you discover why timeouts are occurring, please try to produce a minimal repro and we'll be glad to help.

---

## Enriching the User Interface - TypeScript SDK

Temporal supports adding context to Workflows and Events with metadata. 
This helps users identify and understand Workflows and their operations.

## Adding Summary and Details to Workflows

### Starting a Workflow

When starting a Workflow, you can provide a static summary and details to help identify the workflow in the UI:

```typescript

const client = new Client();

// Start a workflow with static summary and details
const handle = await client.workflow.start(yourWorkflow, {
  args: ['workflow input'],
  taskQueue: 'your-task-queue',
  workflowId: 'your-workflow-id',
  staticSummary: 'Order processing for customer #12345',
  staticDetails: 'Processing premium order with expedited shipping'
});
```

`staticSummary` is a single-line description that appears in the workflow list view, limited to 200 bytes.
`staticDetails` can be multi-line and provides more comprehensive information that appears in the workflow details view, with a larger limit of 20K bytes.

The input format is standard Markdown excluding images, HTML, and scripts.

You can also use the `execute` method with the same parameters:

```typescript
const result = await client.workflow.execute(yourWorkflow, {
  args: ['workflow input'],
  taskQueue: 'your-task-queue',
  workflowId: 'your-workflow-id',
  staticSummary: 'Order processing for customer #12345',
  staticDetails: 'Processing premium order with expedited shipping'
});
```

### Inside the Workflow

Within a Workflow, you can get and set the _current workflow details_. 
Unlike static summary/details set at Workflow start, this value can be updated throughout the life of the Workflow. 
Current Workflow details also takes Markdown format (excluding images, HTML, and scripts) and can span multiple lines.

```typescript

export async function yourWorkflow(input: string): Promise<string> {
  // Get the current details
  const currentDetails = getCurrentDetails();
  console.log(`Current details: ${currentDetails}`);
  
  // Set/update the current details
  setCurrentDetails('Updated workflow details with new status');
  
  return 'Workflow completed';
}
```

### Adding Summary to Activities and Timers

You can attach a `summary` to activities by using `executeWithOptions` when calling them:

```typescript

const { yourActivity } = proxyActivities<typeof activities>({
  startToCloseTimeout: '10 seconds'
});

export async function yourWorkflow(input: string): Promise<string> {
  // Execute an activity with a summary using executeWithOptions
  const result = await yourActivity.executeWithOptions(
    {
      staticSummary: 'Processing user data'
    },
    [input] // Note: arguments must be passed as an array
  );
  
  return result;
}
```

Similarly, you can attach a `summary` to timers within a workflow:

```typescript

export async function yourWorkflow(input: string): Promise<string> {
  // Create a timer with a summary
  await sleep('5 minutes', { summary: 'Waiting for payment confirmation' });
  
  return 'Timer completed';
}
```

The input format for `summary` is a string, and limited to 200 bytes.

## Viewing Summary and Details in the UI

Once you've added summaries and details to your Workflows, Activities, and Timers, you can view this enriched information in the Temporal Web UI.
Navigate to your Workflow's details page to see the metadata displayed in two key locations:

### Workflow Overview Section

At the top of the workflow details page, you'll find the workflow-level metadata:

- **Summary & Details** - Displays the static summary and static details set when starting the workflow
- **Current Details** - Displays the dynamic details that can be updated during workflow execution

All Workflow details support standard Markdown formatting (excluding images, HTML, and scripts), allowing you to create rich, structured information displays.

### Event History

Individual events in the Workflow's Event History display their associated summaries when available. 

Workflow, Activity and Timer summaries appear in purple text next to their corresponding Events, providing immediate context without requiring you to expand the event details. 
When you do expand an Event, the summary is also prominently displayed in the detailed view.

---

## Entity pattern - TypeScript SDK

### Single-entity design pattern in TypeScript {#single-entity-pattern}

The following is a simple pattern that represents a single entity.
It tracks the number of iterations regardless of frequency, and calls `continueAsNew` while properly handling pending updates from Signals.

```ts
interface Input {
  /* Define your Workflow input type here */
}
interface Update {
  /* Define your Workflow update type here */
}

const MAX_ITERATIONS = 1;

export async function entityWorkflow(
  input: Input,
  isNew = true,
): Promise<void> {
  try {
    const pendingUpdates = Array<Update>();
    setHandler(updateSignal, (updateCommand) => {
      pendingUpdates.push(updateCommand);
    });

    if (isNew) {
      await setup(input);
    }

    for (let iteration = 1; iteration <= MAX_ITERATIONS; ++iteration) {
      // Ensure that we don't block the Workflow Execution forever waiting
      // for updates, which means that it will eventually Continue-As-New
      // even if it does not receive updates.
      await condition(() => pendingUpdates.length > 0, '1 day');

      while (pendingUpdates.length) {
        const update = pendingUpdates.shift();
        await runAnActivityOrChildWorkflow(update);
      }
    }
  } catch (err) {
    if (isCancellation(err)) {
      await CancellationScope.nonCancellable(async () => {
        await cleanup();
      });
    }
    throw err;
  }
  await continueAsNew<typeof entityWorkflow>(input, false);
}
```

---

## Failure detection - TypeScript SDK feature guide

This page shows how to do the following:

- [Raise and Handle Exceptions](#exception-handling)
- [Deliberately Fail Workflows](#workflow-failure)
- [Workflow Timeouts](#workflow-timeouts)
- [Workflow retries](#workflow-retries)
- [Activity Timeouts](#activity-timeouts)
- [Activity Retry Policy](#activity-retries)
- [Activity next Retry delay](#activity-next-retry-delay)
- [Heartbeat an Activity](#activity-heartbeats)
- [Activity Heartbeat Timeout](#activity-heartbeat-timeout)

## Raise and Handle Exceptions {#exception-handling}

In each Temporal SDK, error handling is implemented idiomatically, following the conventions of the language.
Temporal uses several different error classes internally — for example, [`CancelledFailure`](https://typescript.temporal.io/api/classes/common.CancelledFailure) in the Typescript SDK, to handle a Workflow cancellation. 
You should not raise or otherwise implement these manually, as they are tied to Temporal platform logic.

The one Temporal error class that you will typically raise deliberately is [`ApplicationFailure`](https://typescript.temporal.io/api/classes/common.ApplicationFailure).
In fact, *any* other exceptions that are raised from your Typescript code in a Temporal Activity will be converted to an `ApplicationError` internally.
This way, an error's type, severity, and any additional details can be sent to the Temporal Service, indexed by the Web UI, and even serialized across language boundaries.

In other words, these two code samples do the same thing:

```typescript 
class InvalidChargeError extends Error {
    constructor(message: string) {
        super(message);
        this.name = "InvalidChargeError";
        Object.setPrototypeOf(this, CustomError.prototype);
    }
}

if (chargeAmount < 0) {
  throw new InvalidChargeError(`Invalid charge amount: ${chargeAmount} (must be above zero)`);
}
```

```typescript 
if (chargeAmount < 0) {
  throw ApplicationFailure.create({
    message: `Invalid charge amount: ${chargeAmount} (must be above zero)`,
    type: 'InvalidChargeError',
  });
}
```

Depending on your implementation, you may decide to use either method.
One reason to use the Temporal `ApplicationFailure` class is because it allows you to set an additional `non_retryable` parameter.
This way, you can decide whether an error should not be retried automatically by Temporal.
This can be useful for deliberately failing a Workflow due to bad input data, rather than waiting for a timeout to elapse:

```typescript
if (chargeAmount < 0) {
  throw ApplicationFailure.create({
    message: `Invalid charge amount: ${chargeAmount} (must be above zero)`,
    nonRetryable: true
  });
}
```

You can alternately specify a list of errors that are non-retryable in your Activity [Retry Policy](#activity-retries).

## Failing Workflows {#workflow-failure}

One of the core design principles of Temporal is that an Activity Failure will never directly cause a Workflow Failure — a Workflow should never return as Failed unless deliberately.
The default retry policy associated with Temporal Activities is to retry them until reaching a certain timeout threshold.
Activities will not actually *return* a failure to your Workflow until this condition or another non-retryable condition is met.
At this point, you can decide how to handle an error returned by your Activity the way you would in any other program.
For example, you could implement a [Saga Pattern](https://github.com/temporalio/samples-typescript/tree/main/saga) that uses `try` and `catch` blocks to "unwind" some of the steps your Workflow has performed up to the point of Activity Failure.

**You will only fail a Workflow by manually raising an `ApplicationFailure` from the Workflow code.**
You could do this in response to an Activity Failure, if the failure of that Activity means that your Workflow should not continue:

```typescript
try {
  await addAddress();
} catch (err) {
  if (err instanceof ActivityFailure && err.cause instanceof ApplicationFailure) {
    log.error(err.cause.message);
    throw err;
  }
}
```

This works differently in a Workflow than raising exceptions from Activities.
In an Activity, any Typescript exceptions or custom exceptions are converted to a Temporal `ApplicationFailure`.
In a Workflow, any exceptions that are raised other than an explicit Temporal `ApplicationFailure` will only fail that particular [Workflow Task](https://docs.temporal.io/tasks#workflow-task-execution) and be retried.
This includes any typical Typescript runtime errors like an `undefined` error that are raised automatically.
These errors are treated as bugs that can be corrected with a fixed deployment, rather than a reason for a Temporal Workflow Execution to return unexpectedly.

## Workflow Timeouts {#workflow-timeouts}

**How to set Workflow Timeouts using the Temporal TypeScript SDK**

Each Workflow timeout controls the maximum duration of a different aspect of a Workflow Execution.

Before we continue, we want to note that we generally do not recommend setting Workflow Timeouts, because Workflows are designed to be long-running and resilient.
Instead, setting a Timeout can limit its ability to handle unexpected delays or long-running processes.
If you need to perform an action inside your Workflow after a specific period of time, we recommend using a Timer.

Workflow Timeouts are set when starting a Workflow using either the Client or Workflow API.

- **[Workflow Execution Timeout](/encyclopedia/detecting-workflow-failures#workflow-execution-timeout)** - restricts the maximum amount of time that a single Workflow Execution can be executed
- **[Workflow Run Timeout](/encyclopedia/detecting-workflow-failures#workflow-run-timeout):** restricts the maximum amount of time that a single Workflow Run can last
- **[Workflow Task Timeout](/encyclopedia/detecting-workflow-failures#workflow-task-timeout):** restricts the maximum amount of time that a Worker can execute a Workflow Task

The following properties can be set on the [`WorkflowOptions`](https://typescript.temporal.io/api/interfaces/client.WorkflowOptions/) when starting a Workflow using either the Client or Workflow API:

- [`workflowExecutionTimeout​`](https://typescript.temporal.io/api/interfaces/client.WorkflowOptions/#workflowexecutiontimeout)
- [`workflowRunTimeout`](https://typescript.temporal.io/api/interfaces/client.WorkflowOptions/#workflowruntimeout)
- [`workflowTaskTimeout`](https://typescript.temporal.io/api/interfaces/client.WorkflowOptions/#workflowtasktimeout)

```typescript
await client.workflow.start(example, {
  taskQueue,
  workflowId,
  // Set Workflow Timeout duration
  workflowExecutionTimeout: '1 day',
  // workflowRunTimeout: '1 minute',
  // workflowTaskTimeout: '30 seconds',
});
```

## Workflow retries {#workflow-retries}

**How to set Workflow retries using the Temporal TypeScript SDK**

A Retry Policy can work in cooperation with the timeouts to provide fine controls to optimize the execution experience.

Use a [Retry Policy](/encyclopedia/retry-policies) to retry a Workflow Execution in the event of a failure.

Workflow Executions do not retry by default, and Retry Policies should be used with Workflow Executions only in certain situations.

The Retry Policy can be set through the [`WorkflowOptions.retry`](https://typescript.temporal.io/api/interfaces/client.WorkflowOptions/#retry) property when starting a Workflow using either the Client or Workflow API.

```typescript
const handle = await client.workflow.start(example, {
  taskQueue,
  workflowId,
  retry: {
    maximumAttempts: 3,
    maximumInterval: '30 seconds',
  },
});
```

## Activity Timeouts {#activity-timeouts}

**How to set Activity Timeouts using the Temporal TypeScript SDK**

Each Activity Timeout controls the maximum duration of a different aspect of an Activity Execution.

The following Timeouts are available in the Activity Options:

- **[Schedule-To-Close Timeout](/encyclopedia/detecting-activity-failures#schedule-to-close-timeout):** is the maximum amount of time allowed for the entire [Activity Execution](/activity-execution), from when the [Activity Task](/tasks#activity-task) is initially scheduled by the Workflow to when the server receives a successful completion for that Activity Task
- **[Start-To-Close Timeout](/encyclopedia/detecting-activity-failures#start-to-close-timeout):** is the maximum time allowed for a single [Activity Task Execution](/tasks#activity-task-execution), from when the Activity Task Execution gets polled by a [Worker](/workers#worker) to when the server receives a successful completion for that Activity Task
- **[Schedule-To-Start Timeout](/encyclopedia/detecting-activity-failures#schedule-to-start-timeout):** is the maximum amount of time that is allowed from when an [Activity Task](/tasks#activity-task) is initially scheduled by the Workflow to when a [Worker](/workers#worker) polls the Activity Task Execution

An Activity Execution must have either the Start-To-Close or the Schedule-To-Close Timeout set.

The following properties can be set on the [`ActivityOptions`](https://typescript.temporal.io/api/interfaces/common.ActivityOptions) when creating Activity proxy functions using the [`proxyActivities()`](https://typescript.temporal.io/api/namespaces/workflow#proxyactivities) API:

- [`scheduleToCloseTimeout`](https://typescript.temporal.io/api/interfaces/common.ActivityOptions/#scheduletoclosetimeout)
- [`startToCloseTimeout`](https://typescript.temporal.io/api/interfaces/common.ActivityOptions/#starttoclosetimeout)
- [`scheduleToStartTimeout`](https://typescript.temporal.io/api/interfaces/common.ActivityOptions/#scheduletostarttimeout)

```typescript
const { myActivity } = proxyActivities<typeof activities>({
  scheduleToCloseTimeout: '5m',
  // startToCloseTimeout: "30s", // recommended
  // scheduleToStartTimeout: "60s",
});
```

## Activity Retry Policy {#activity-retries}

**How to set an Activity Retry Policy using the Temporal TypeScript SDK**

A Retry Policy works in cooperation with the timeouts to provide fine controls to optimize the execution experience.

Activity Executions are automatically associated with a default [Retry Policy](/encyclopedia/retry-policies) if a custom one is not provided.

To set an Activity's Retry Policy in TypeScript, assign the [`ActivityOptions.retry`](https://typescript.temporal.io/api/interfaces/common.ActivityOptions#retry) property when creating the corresponding Activity proxy function using the [`proxyActivities()`](https://typescript.temporal.io/api/namespaces/workflow#proxyactivities) API.

```typescript
const { myActivity } = proxyActivities<typeof activities>({
  // ...
  retry: {
    initialInterval: '10s',
    maximumAttempts: 5,
  },
});
```

## Activity next Retry delay {#activity-next-retry-delay}

**How to override the next Retry delay following an Activity failure using the Temporal TypeScript SDK**

The time to wait after a retryable Activity failure until the next retry is attempted is normally determined by that Activity's Retry Policy.
However, an Activity may override that duration when explicitly failing with an [`ApplicationFailure`](https://typescript.temporal.io/api/classes/common.ApplicationFailure) by setting a next Retry delay.

To override the next Retry delay for an `ApplicationFailure` thrown by an Activity in TypeScript, provide the [`nextRetryDelay`](https://typescript.temporal.io/api/interfaces/common.ApplicationFailureOptions#nextretrydelay) property on the object argument of the [`ApplicationFailure.create()`](https://typescript.temporal.io/api/classes/common.ApplicationFailure#create) factory method.

```typescript
throw ApplicationFailure.create({
  // ...
  nextRetryDelay: '15s',
});
```

## Heartbeat an Activity {#activity-heartbeats}

**How to Heartbeat an Activity using the Temporal TypeScript SDK**

An [Activity Heartbeat](/encyclopedia/detecting-activity-failures#activity-heartbeat) is a ping from the [Worker Process](/workers#worker-process) that is executing the Activity to the [Temporal Service](/temporal-service).
Each Heartbeat informs the Temporal Service that the [Activity Execution](/activity-execution) is making progress and the Worker has not crashed.
If the Temporal Service does not receive a Heartbeat within a [Heartbeat Timeout](/encyclopedia/detecting-activity-failures#heartbeat-timeout) time period, the Activity will be considered as timed out and another [Activity Task Execution](/tasks#activity-task-execution) may be scheduled according to the Retry Policy.

Activity Cancellations are delivered to Activities from the Temporal Service when they Heartbeat.
Activities that don't Heartbeat can't get notified of Cancellation requests.

:::note Handling Activity Cancellation
In the TypeScript SDK, Activity implementations must opt in to observe cancellation.

Use one of the following in your Activity to be notified of cancellation:
- `await Context.current().cancelled` — rejects with `CancelledFailure`.
- `await Context.current().sleep(ms)` — rejects on cancellation.
- Pass `Context.current().cancellationSignal` (an `AbortSignal`) to libraries that support abort.

> **Important:** Activities receive cancellation notifications when they heartbeat; if an Activity doesn’t heartbeat, delivery of the cancellation notification can be delayed.

See [Activity namespace reference](https://typescript.temporal.io/api/namespaces/activity#cancellation) and [Context API](https://typescript.temporal.io/api/classes/activity.Context) for more information.
:::

Heartbeats may not always be sent to the Temporal Service—they may be [throttled](/encyclopedia/detecting-activity-failures#throttling) by the Worker.
Heartbeat throttling may lead to Cancellation getting delivered later than expected.

To Heartbeat an Activity Execution in TypeScript, call the [`heartbeat()`](https://typescript.temporal.io/api/namespaces/activity#heartbeat) function from the Activity implementation.

```typescript
export async function myActivity(): Promise<void> {
  for (let progress = 1; progress <= 1000; ++progress) {
    // Do something that takes time
    await sleep('1s');

    heartbeat();
  }
}
```

An Activity may optionally checkpoint its progression, by providing a `details` argument to the [`heartbeat()`](https://typescript.temporal.io/api/namespaces/activity#heartbeat) function.
Should the Activity Execution times out and gets retried, then the Temporal Server will provide the `details` from the last Heartbeat it received to the next Activity Execution.
This can be used to allow the Activity to efficiently resume its work.

```typescript
export async function myActivity(): Promise<void> {
  // Resume work from latest heartbeat, if there's one, or start from 1 otherwise
  const startingPoint = activityInfo().heartbeatDetails?.progress ?? 1;

  for (let progress = startingPoint; progress <= 1000; ++progress) {
    // Do something that takes time
    await sleep('1s');

    heartbeat({ progress });
  }
}
```

## Activity Heartbeat Timeout {#activity-heartbeat-timeout}

**How to set a Heartbeat Timeout using the Temporal TypeScript SDK**

A [Heartbeat Timeout](/encyclopedia/detecting-activity-failures#heartbeat-timeout) works in conjunction with [Activity Heartbeats](/encyclopedia/detecting-activity-failures#activity-heartbeat).
If the Temporal Server doesn't receive a Heartbeat before expiration of the Heartbeat Timeout, the Activity is considered as timed out and another [Activity Task Execution](/tasks#activity-task-execution) may be scheduled according to the Retry Policy.

To set an Activity's Heartbeat Timeout in TypeScript, set the [`ActivityOptions.heartbeatTimeout`](https://typescript.temporal.io/api/interfaces/common.ActivityOptions#heartbeattimeout) property when creating the corresponding Activity proxy functions using the [`proxyActivities()`](https://typescript.temporal.io/api/namespaces/workflow#proxyactivities) API.

```typescript
const { myLongRunningActivity } = proxyActivities<typeof activities>({
  // ...
  heartbeatTimeout: '30s',
});
```

---

## TypeScript SDK developer guide

![TypeScript SDK Banner](/img/assets/banner-typescript-temporal.png)

:::info TYPESCRIPT SPECIFIC RESOURCES Build Temporal Applications with the TypeScript SDK.

**Temporal TypeScript Technical Resources:**

- [TypeScript SDK Quickstart - Setup Guide](https://docs.temporal.io/develop/typescript/set-up-your-local-typescript)
- [TypeScript API Documentation](https://typescript.temporal.io)
- [TypeScript SDK Code Samples](https://github.com/temporalio/samples-typescript)
- [TypeScript SDK GitHub](https://github.com/temporalio/sdk-typescript)
- [Temporal 101 in TypeScript Free Course](https://learn.temporal.io/courses/temporal_101/typescript/)

**Get Connected with the Temporal TypeScript Community:**

- [Temporal TypeScript Community Slack](https://temporalio.slack.com/archives/C01DKSMU94L)
- [TypeScript SDK Forum](https://community.temporal.io/tag/typescript-sdk)

:::

## [Core application](/develop/typescript/core-application)

Use the essential components of a Temporal Application (Workflows, Activities, and Workers) to build and run a Temporal
application.

- [Develop a Basic Workflow](/develop/typescript/core-application#develop-workflows)
- [Develop a Basic Activity](/develop/typescript/core-application#develop-activities)
- [Start an Activity Execution](/develop/typescript/core-application#activity-execution)
- [Run Worker Processes](/develop/typescript/core-application#run-a-dev-worker)

## [Temporal Client](/develop/typescript/temporal-client)

Connect to a Temporal Service and start a Workflow Execution.

- [Connect to Development Temporal Service](/develop/typescript/temporal-client#connect-to-development-service)
- [Connect to Temporal Cloud](/develop/typescript/temporal-client#connect-to-temporal-cloud)
- [Start a Workflow Execution](/develop/typescript/temporal-client#start-workflow-execution)

## [Testing](/develop/typescript/testing-suite)

Set up the testing suite and test Workflows and Activities.

- [Test Frameworks](/develop/typescript/testing-suite#test-frameworks)
- [Testing Activities](/develop/typescript/testing-suite#test-activities)
- [Testing Workflows](/develop/typescript/testing-suite#test-workflows)
- [How to Replay a Workflow Execution](/develop/typescript/testing-suite#replay)

## [Failure detection](/develop/typescript/failure-detection)

Explore how your application can detect failures using timeouts and automatically attempt to mitigate them with retries.

- [Workflow Timeouts](/develop/typescript/failure-detection#workflow-timeouts)
- [Set Activity Timeouts](/develop/typescript/failure-detection#activity-timeouts)
- [Heartbeat an Activity](/develop/typescript/failure-detection#activity-heartbeats)

## [Workflow message passing](/develop/typescript/message-passing)

Send messages to and read the state of Workflow Executions.

- [Develop with Signals](/develop/typescript/message-passing#signals)
- [Develop with Queries](/develop/typescript/message-passing#queries)
- [What is a Dynamic Handler](/develop/typescript/message-passing#dynamic-handler)

## [Interrupt a Workflow feature guide](/develop/typescript/cancellation)

Interrupt a Workflow Execution with a Cancel or Terminate action.

- [Cancellation scopes in Typescript](/develop/typescript/cancellation#cancellation-scopes)
- [Reset a Workflow](/develop/typescript/cancellation#reset): Resume a Workflow Execution from an earlier point in its
  Event History.

## [Asynchronous Activity Completion](/develop/typescript/asynchronous-activity-completion)

Complete Activities asynchronously.

- [Asynchronously Complete an Activity](/develop/typescript/asynchronous-activity-completion)

## [Versioning](/develop/typescript/versioning)

Change Workflow Definitions without causing non-deterministic behavior in running Workflows.

- [Introduction to Versioning](/develop/typescript/versioning)
- [How to Use the Patching API](/develop/typescript/versioning#patching)

## [Observability](/develop/typescript/observability)

Configure and use the Temporal Observability APIs.

- [Emit Metrics](/develop/typescript/observability#metrics)
- [Setup Tracing](/develop/typescript/observability#tracing)
- [Log from a Workflow](/develop/typescript/observability#logging)
- [Use Visibility APIs](/develop/typescript/observability#visibility)

## [Debugging](/develop/typescript/debugging)

Explore various ways to debug your application.

- [Debugging](/develop/typescript/debugging)

## [Schedules](/develop/typescript/schedules)

Run Workflows on a schedule and delay the start of a Workflow.

- [Schedule a Workflow](/develop/typescript/schedules#schedule-a-workflow)
- [Temporal Cron Jobs](/develop/typescript/schedules#temporal-cron-jobs)
- [How to use Start Delay](/develop/typescript/schedules#start-delay)

## [Data encryption](/develop/typescript/converters-and-encryption)

Use compression, encryption, and other data handling by implementing custom converters and codecs.

- [Custom Payload Codec](/develop/typescript/converters-and-encryption#custom-payload-conversion)

## [Temporal Nexus](/develop/typescript/nexus)

The Temporal Nexus feature guide shows how to use Temporal Nexus to connect durable
executions within and across Namespaces using a Nexus Endpoint, a Nexus Service contract, and Nexus Operations.

- [Create a Nexus Endpoint to route requests from caller to handler](/develop/typescript/nexus#create-nexus-endpoint)
- [Define the Nexus Service contract](/develop/typescript/nexus#define-nexus-service-contract)
- [Develop a Nexus Service and Operation handlers](/develop/typescript/nexus#develop-nexus-service-operation-handlers)
- [Develop a caller Workflow that uses a Nexus Service](/develop/typescript/nexus#develop-caller-workflow-nexus-service)
- [Make Nexus calls across Namespaces with a dev Server](/develop/typescript/nexus#register-the-caller-workflow-in-a-worker-and-start-the-caller-workflow)
- [Make Nexus calls across Namespaces in Temporal Cloud](/develop/typescript/nexus#nexus-calls-across-namespaces-temporal-cloud)

## [Durable Timers](/develop/typescript/timers)

Use Timers to make a Workflow Execution pause or "sleep" for seconds, minutes, days, months, or years.

- [What is a Timer](/develop/typescript/timers)

## [Child Workflows](/develop/typescript/child-workflows)

Explore how to spawn a Child Workflow Execution and handle Child Workflow Events.

- [Start a Child Workflow Execution](/develop/typescript/child-workflows)

## [Continue-As-New](/develop/typescript/continue-as-new)

Continue the Workflow Execution with a new Workflow Execution using the same Workflow ID.

- [Continue-As-New](/develop/typescript/continue-as-new)

## [Enriching the User Interface](/develop/typescript/enriching-ui)

Add descriptive information to workflows and events for better visibility and context in the UI.

- [Adding Summary and Details to Workflows](/develop/typescript/enriching-ui#adding-summary-and-details-to-workflows)

## [Interceptors](/develop/typescript/interceptors)

Manage inbound and outbound SDK calls, enhance tracing, and add authorization to your Workflows and Activities.

- [How to implement interceptors](/develop/typescript/interceptors#interceptors)
- [Register an interceptor](/develop/typescript/interceptors#register-interceptor)

## [Vercel AI SDK Integration](/develop/typescript/integrations/ai-sdk)

Integrate the Vercel AI SDK with Temporal to build durable AI agents and AI-powered applications.

- [Vercel AI SDK Integration](/develop/typescript/integrations/ai-sdk)

---

## AI SDK by Vercel Integration

Temporal's integration with [Vercel's AI SDK](https://ai-sdk.dev/) lets you use the AI SDK's API directly in Workflow
code while Temporal handles Durable Execution.

Like all API calls, LLM API calls are non-deterministic. In a [Temporal Application](/glossary#temporal-application),
that means you cannot make LLM calls directly from a [Workflow](/glossary#workflow); they must run as
[Activities](/glossary#activity). The AI SDK plugin handles this automatically: when you call methods in the AI SDK such
as `generateText()`, the plugin wraps those calls in Activities behind the scenes. This preserves the Vercel AI SDK's
developer experience that you are already familiar with while Temporal handles Durable Execution for you.

All code snippets in this guide are taken from the TypeScript SDK
[ai-sdk samples](https://github.com/temporalio/samples-typescript/tree/main/ai-sdk). Refer to the samples for the
complete code and run them locally.

:::info

The Vercel AI SDK Integration is in Public Preview. Refer to the
[Temporal product release stages guide](/evaluate/development-production-features/release-stages) for more information.

:::

## Prerequisites

- This guide assumes you are already familiar with the Vercel AI SDK. If you aren't, refer to the
  [Vercel AI SDK documentation](https://ai-sdk.dev/) for more details.
- If you are new to Temporal, we also recommend you read the [Understanding Temporal](/evaluate/understanding-temporal)
  document or take the [Temporal 101](https://learn.temporal.io/courses/temporal_101/) course to understand the basics
  of Temporal.
- Ensure you have set up your local development environment by following the
  [Set up your local with the TypeScript SDK](/develop/typescript/set-up-your-local-typescript) guide. When you are
  done, leave the Temporal Development Server running if you want to test your code locally.

## Configure Workers to use the AI SDK

Workers are the compute layer of a Temporal Application. They are responsible for executing the code that defines your
[Workflows](/glossary#workflow) and [Activities](/glossary#activity). Before you can execute a Workflow or Activity with
the Vercel AI SDK, you need to create a Worker and configure it to use the AI SDK plugin.

Follow the steps below to configure your Worker.

1. Install the `@temporalio/ai-sdk` package.

   ```bash
   npm install @temporalio/ai-sdk
   ```

2. Create a `worker.ts` file and configure the Worker to use the AI SDK plugin.

   ```ts {9-11}

   //... other import statements, initializing a connection
   //  to the Temporal Service to be used by the Worker

   const worker = await Worker.create({
     plugins: [
       new AiSDKPlugin({
         modelProvider: openai,
       }),
     ],
     connection,
     namespace: 'default',
     taskQueue: 'ai-sdk',
     workflowsPath: require.resolve('./workflows'),
     activities,
   });

   // ... code that runs the worker
   ```

   The `modelProvider` specifies which AI provider to use when creating models. Choose the provider that best suits your
   needs. In the Worker options, you are also specifying that the Worker polls the `ai-sdk` Task Queue for work in the
   `default` Namespace. Make sure that you configure your Client application to use the same Task Queue and Namespace.

3. Run the Worker. This Worker will now poll the Temporal Service for work on the `ai-sdk` Task Queue in the `default`
   Namespace until you stop it.

   ```bash
   nodemon worker.ts
   ```

   You must ensure the Worker process has access to your API credentials. Most provider SDKs read credentials from
   environment variables. Refer to the [Vercel AI SDK documentation](https://ai-sdk.dev/providers/ai-sdk-providers) for
   instructions on how to set up your environment variables for the provider you chose.

   :::tip

   You only need to give provider credentials to the Worker process. The client application, meaning the application
   that sends requests to the Temporal Service to start Workflow Executions, doesn't need to know about the credentials.

   :::

See the full example at [ai-sdk samples](https://github.com/temporalio/samples-typescript/tree/main/ai-sdk).

## Develop a Simple Haiku Agent

To help you get started, you can develop a simple Haiku Agent that generates haikus based on a prompt.

If you weren't using Temporal, you would write code like this to generate a haiku:

```ts

async function haikuAgent(prompt: string): Promise<string> {
  const result = await generateText({
    model: openai('gpt-4o-mini'),
    prompt,
    system: 'You only respond in haikus.',
  });
  return result.text;
}
```

To add Durable Execution to your agent, implement the agent as a Temporal Workflow. Use the AI SDK as you normally
would, but pass `temporalProvider.languageModel()` as the model. The string you provide (like `'gpt-4o-mini'`) is passed
to your configured `modelProvider` to create the model.

```ts {2,6}

export async function haikuAgent(prompt: string): Promise<string> {
  const result = await generateText({
    model: temporalProvider.languageModel('gpt-4o-mini'),
    prompt,
    system: 'You only respond in haikus.',
  });
  return result.text;
}
```

With only two line changes, you have added Durable Execution to your agent. Your agent now gets automatic retries,
timeouts, and the ability to run for extended periods without losing state if the process crashes.

## Provide your durable agent with tools

The Vercel AI SDK lets you provide tools to your agents, and when the model calls them, they execute in the Workflow.
Since tool functions run in Workflow context, they must follow Workflow rules. That means they must call Activities or
Child Workflows to perform non-deterministic operations like API calls.

For example, if you want to call an external API to get the weather, you would implement it as an Activity and call it
from the tool function. The following is an example of an Activity that gets the weather for a given location:

<!--SNIPSTART typescript-vercel-ai-sdk-weather-activity -->

[ai-sdk/src/activities.ts](https://github.com/temporalio/samples-typescript/blob/main/ai-sdk/src/activities.ts)

```ts
export async function getWeather(input: {
  location: string;
}): Promise<{ city: string; temperatureRange: string; conditions: string }> {
  console.log('Activity execution');
  return {
    city: input.location,
    temperatureRange: '14-20C',
    conditions: 'Sunny with wind.',
  };
}
```

<!--SNIPEND-->

Then in your agent implementation, provide the tool to the model using the `tools` option and instruct the model to use
the tool when needed.

```ts {15-23}

const { getWeather } = proxyActivities<typeof activities>({
  startToCloseTimeout: '1 minute',
});

export async function toolsAgent(question: string): Promise<string> {
  const result = await generateText({
    model: temporalProvider.languageModel('gpt-4o-mini'),
    prompt: question,
    system: 'You are a helpful agent.',
    tools: {
      getWeather: tool({
        description: 'Get the weather for a given city',
        inputSchema: z.object({
          location: z.string().describe('The location to get the weather for'),
        }),
        execute: getWeather,
      }),
    },
    stopWhen: stepCountIs(5),
  });
  return result.text;
}
```

## Integrate with Model Context Protocol (MCP) servers

[Model Context Protocol (MCP)](https://modelcontextprotocol.io/) is an open standard that lets AI applications connect
to external tools and data sources. Calls to MCP servers, being calls to external APIs, are non-deterministic and would
usually need to be implemented as Activities. The Temporal AI SDK integration handles this for you and provides a
built-in implementation of a stateless MCP client that you can use inside Workflows.

Follow the steps below to integrate your agent with an MCP server.

1. Create a connection to the MCP servers using the `experimental_createMCPClient` function from the `@ai-sdk/mcp`
   package. You can register multiple MCP servers by providing multiple factory functions in `mcpClientFactories`.

   ```ts

   const mcpClientFactories = {
     testServer: () =>
       createMCPClient({
         transport: new StdioClientTransport({
           command: 'node',
           args: ['lib/mcp-server.js'],
         }),
       }),
   };
   ```

   The example uses `StdioClientTransport` as the transport mechanisms for client-server communication. Each time the
   Worker processes a Task that requires communication with the MCP server, it will start the server process and connect
   to it as required by the Task.

2. Configure the Worker to use the MCP client factories.

   ```ts {5}
   const worker = await Worker.create({
     plugins: [
       new AiSDKPlugin({
         modelProvider: openai,
         mcpClientFactories
       }),
     ]},
     ...
   );
   ```

3. In your agent Workflow, use `TemporalMCPClient` to get tools from the MCP server by referencing it by name:

   ```ts {4-5,9}

   export async function mcpAgent(prompt: string): Promise<string> {
     const mcpClient = new TemporalMCPClient({ name: 'testServer' });
     const tools = await mcpClient.tools();
     const result = await generateText({
       model: temporalProvider.languageModel('gpt-4o-mini'),
       prompt,
       tools,
       system: 'You are a helpful agent, You always use your tools when needed.',
       stopWhen: stepCountIs(5),
     });
     return result.text;
   }
   ```

   Both listing tools and calling them run as Activities behind the scenes, giving you automatic retries, timeouts, and
   full observability.

---

## AI Integrations(Integrations)

Temporal TypeScript SDK provides integrations with the following tools and services:

- [AI SDK by Vercel](/develop/typescript/integrations/ai-sdk)

---

## Manage Interceptors - TypeScript SDK

Interceptors are a mechanism for modifying inbound and outbound SDK calls.
Interceptors are commonly used to add tracing and authorization to the scheduling and execution of Workflows and Activities.
You can compare these to "middleware" in other frameworks.

## How to implement interceptors in TypeScript {#interceptors}

The TypeScript SDK comes with an optional interceptor package that adds tracing with [OpenTelemetry](https://www.npmjs.com/package/@temporalio/interceptors-opentelemetry).
See how to use it in the [interceptors-opentelemetry](https://github.com/temporalio/samples-typescript/tree/main/interceptors-opentelemetry) code sample.

- [WorkflowInboundCallsInterceptor](https://typescript.temporal.io/api/interfaces/workflow.WorkflowInboundCallsInterceptor/): Intercept Workflow inbound calls like execution, Signals, and Queries.
- [WorkflowOutboundCallsInterceptor](https://typescript.temporal.io/api/interfaces/workflow.WorkflowOutboundCallsInterceptor/): Intercept Workflow outbound calls to Temporal APIs like scheduling Activities and starting Timers.
- [ActivityInboundCallsInterceptor](https://typescript.temporal.io/api/interfaces/worker.ActivityInboundCallsInterceptor): Intercept inbound calls to an Activity (such as `execute`).
- [WorkflowClientInterceptor](https://typescript.temporal.io/api/interfaces/client.WorkflowClientInterceptor/): Intercept workflow-related methods of [`Client`](https://typescript.temporal.io/api/classes/client.Client/) and [`WorkflowHandle`](https://typescript.temporal.io/api/interfaces/client.WorkflowHandle) like starting or signaling a Workflow.

Interceptors are run in a chain, and all interceptors work similarly.
They accept two arguments: `input` and `next`, where `next` calls the next interceptor in the chain.
All interceptor methods are optional—it's up to the implementor to choose which methods to intercept.

## Interceptor examples

**Log start and completion of Activities**

```ts

  ActivityInput,
  Next,
  WorkflowOutboundCallsInterceptor,
} from '@temporalio/workflow';

export class ActivityLogInterceptor
  implements WorkflowOutboundCallsInterceptor
{
  constructor(public readonly workflowType: string) {}

  async scheduleActivity(
    input: ActivityInput,
    next: Next<WorkflowOutboundCallsInterceptor, 'scheduleActivity'>,
  ): Promise<unknown> {
    console.log('Starting activity', { activityType: input.activityType });
    try {
      return await next(input);
    } finally {
      console.log('Completed activity', {
        workflow: this.workflowType,
        activityType: input.activityType,
      });
    }
  }
}
```

**Authorization**

```ts

  defaultDataConverter,
  Next,
  WorkflowInboundCallsInterceptor,
  WorkflowInput,
} from '@temporalio/workflow';

/**
 * WARNING: This demo is meant as a simple auth example.
 * Do not use this for actual authorization logic.
 * Auth headers should be encrypted and credentials
 * stored outside of the codebase.
 */
export class DumbWorkflowAuthInterceptor
  implements WorkflowInboundCallsInterceptor
{
  public async execute(
    input: WorkflowInput,
    next: Next<WorkflowInboundCallsInterceptor, 'execute'>,
  ): Promise<unknown> {
    const authHeader = input.headers.auth;
    const { user, password } = authHeader
      ? await defaultDataConverter.fromPayload(authHeader)
      : undefined;

    if (!(user === 'admin' && password === 'admin')) {
      throw new Error('Unauthorized');
    }
    return await next(input);
  }
}
```

To properly do authorization from Workflow code, the Workflow would need to access encryption keys and possibly authenticate against an external user database, which requires the Workflow to break isolation.
Please contact us if you need to discuss this further.

## Register an Interceptor {#register-interceptor}

### Activity and client interceptors registration

- Activity interceptors are registered on Worker creation by passing an array of [ActivityInboundCallsInterceptor factory functions](https://typescript.temporal.io/api/interfaces/worker.ActivityInboundCallsInterceptorFactory) through [WorkerOptions](https://typescript.temporal.io/api/interfaces/worker.WorkerOptions#interceptors).

- Client interceptors are registered on `Client` construction by passing an array of [WorkflowClientInterceptor](https://typescript.temporal.io/api/interfaces/client.WorkflowClientInterceptor) via [ClientOptions.interceptors](https://typescript.temporal.io/api/interfaces/client.ClientOptions#interceptors).

### Workflow interceptors registration

Workflow interceptor registration is different from the other interceptors because they run in the Workflow isolate.
To register Workflow interceptors, export an `interceptors` function from a file located in the `workflows` directory and provide the name of that file to the Worker on creation via [WorkerOptions](https://typescript.temporal.io/api/interfaces/worker.WorkerOptions#interceptors).

At the time of construction, the Workflow context is already initialized for the current Workflow.
You may call the [`workflowInfo()`](https://typescript.temporal.io/api/namespaces/workflow#workflowinfo) function to access Workflow-specific information from an interceptor.

`src/workflows/your-interceptors.ts`

```ts

export const interceptors = () => ({
  outbound: [new ActivityLogInterceptor(workflowInfo().workflowType)],
  inbound: [],
});
```

`src/worker/index.ts`

```ts
const worker = await Worker.create({
  workflowsPath: require.resolve('./workflows'),
  interceptors: {
    workflowModules: [require.resolve('./workflows/your-interceptors')],
  },
});
```

---

## Workflow message passing - TypeScript SDK

A Workflow can act like a stateful web service that receives messages: Queries, Signals, and Updates.
The Workflow implementation defines these endpoints via handler methods that can react to incoming messages and return values.
Temporal Clients use messages to read Workflow state and control its execution.
See [Workflow message passing](/encyclopedia/workflow-message-passing) for a general overview of this topic.
This page introduces these features for the Temporal Typescript SDK.

## Write message handlers {#writing-message-handlers}

:::info
The code that follows is part of a working [message-passing sample](https://github.com/temporalio/samples-typescript/tree/main/message-passing/introduction).
:::

Follow these guidelines when writing your message handlers:

- Define a message type as a global variable using [`defineQuery`](https://typescript.temporal.io/api/namespaces/workflow#definequery), [`defineSignal`](https://typescript.temporal.io/api/namespaces/workflow#definesignal), or [`defineUpdate`](https://typescript.temporal.io/api/namespaces/workflow#defineupdate).
  This is what your client code will use to send a message to the workflow.
- Message handlers are defined by calling [`workflow.setHandler`](https://typescript.temporal.io/api/namespaces/workflow#sethandler) in your Workflow function.
- The parameters and return values of handlers and the main Workflow function must be [serializable](/dataconversion).
- Prefer using a single object over multiple input parameters.
  A single object allows you to add fields without changing the signature.

### Query handlers {#queries}

A [Query](/sending-messages#sending-queries) is a synchronous operation that retrieves state from a Workflow Execution:

```typescript
export enum Language {
  ARABIC = 'ARABIC',
  CHINESE = 'CHINESE',
  ENGLISH = 'ENGLISH',
  FRENCH = 'FRENCH',
  HINDI = 'HINDI',
  PORTUGUESE = 'PORTUGUESE',
  SPANISH = 'SPANISH',
}

interface GetLanguagesInput {
  includeUnsupported: boolean;
}

// 👉 Use the object returned by defineQuery to set the query handler in
// Workflow code, and when sending the Query in Client code.
export const getLanguages = wf.defineQuery<Language[], [GetLanguagesInput]>('getLanguages');

export async function greetingWorkflow(): Promise<string> {
  const greetings: Partial<Record<Language, string>> = {
    [Language.CHINESE]: '你好，世界',
    [Language.ENGLISH]: 'Hello, world',
  };

  wf.setHandler(getLanguages, (input: GetLanguagesInput): Language[] => {
    // 👉 A Query handler returns a value: it must not mutate the Workflow state
    // and can't perform async operations.
    if (input.includeUnsupported) {
      return Object.values(Language);
    } else {
      return Object.keys(greetings) as Language[];
    }
  });

  ...
}
```

- A Query handler cannot be `async`.
  You can't perform async operations like executing an Activity in a Query handler.

- `setHandler` can take `QueryHandlerOptions` (such as `description`) as described in the API reference docs for [`workflow.setHandler`](https://typescript.temporal.io/api/namespaces/workflow#sethandler).

### Signal handlers {#signals}

A [Signal](/sending-messages#sending-signals) is an asynchronous message sent to a running Workflow Execution to change its state and control its flow:

```typescript
// 👉 Use the object returned by defineSignal to set the Signal handler in
// Workflow code, and to send the Signal from Client code.
export const approve = wf.defineSignal<[ApproveInput]>('approve');

export async function greetingWorkflow(): Promise<string> {
  let approvedForRelease = false;
  let approverName: string | undefined;

  wf.setHandler(approve, (input) => {
    // 👉 A Signal handler mutates the Workflow state but cannot return a value.
    approvedForRelease = true;
    approverName = input.name;
  });

  ...
}
...
```

- The handler cannot return a value.
  The response is sent immediately from the server, without waiting for the Workflow to process the Signal.

- Signal and Update handlers can be `async`.
  This allows you to use Activities, Child Workflows, durable [`workflow.sleep`](https://typescript.temporal.io/api/namespaces/workflow#sleep) Timers, [`workflow.condition`](https://typescript.temporal.io/api/namespaces/workflow#condition) conditions, and more.
  See [Async handlers](#async-handlers) and [Workflow message passing](/encyclopedia/workflow-message-passing) for guidelines on safely using async Signal and Update handlers.

- If your Workflow needs to do some async initialization before handling a Signal or Update, use [`workflow.condition`](https://typescript.temporal.io/api/namespaces/workflow#condition) inside your handler to wait until initialization has completed.

- `setHandler` can take `SignalHandlerOptions` (such as `description` and `unfinishedPolicy`) as described in the API reference docs for [`workflow.setHandler`](https://typescript.temporal.io/api/namespaces/workflow#sethandler).

### Update handlers and validators {#updates}

An [Update](/sending-messages#sending-updates) is a trackable synchronous request sent to a running Workflow Execution.
It can change the Workflow state, control its flow, and return a result.
The sender must wait until the Worker accepts or rejects the Update.
The sender may wait further to receive a returned value or an exception if something goes wrong:

```typescript
// 👉 Use the object returned by defineUpdate to set the Update handler in
// Workflow code, and to send Updates from Client code.
export const setLanguage = wf.defineUpdate<Language, [Language]>('setLanguage');

export async function greetingWorkflow(): Promise<string> {
  const greetings: Partial<Record<Language, string>> = {
    [Language.CHINESE]: '你好，世界',
    [Language.ENGLISH]: 'Hello, world',
  };

  let language = Language.ENGLISH;

wf.setHandler(
    setLanguage,
    (newLanguage: Language) => {
      // 👉 An Update handler can mutate the Workflow state and return a value.
      const previousLanguage = language;
      language = newLanguage;
      return previousLanguage;
    },
    {
      validator: (newLanguage: Language) => {
        // 👉 Update validators are optional
        if (!(newLanguage in greetings)) {
          throw new Error(`${newLanguage} is not supported`);
        }
      },
    }
  );

  ...
}
```

- `setHandler` can take `UpdateHandlerOptions` (such as `validator`, `description` and `unfinishedPolicy`) as described in the API reference docs for [`workflow.setHandler`](https://typescript.temporal.io/api/namespaces/workflow#sethandler).

- About validators:
  - Use validators to reject an Update before it is written to History.
    Validators are always optional.
    If you don't need to reject Updates, you don't need a validator.
  - To set a validator, pass the validator function in `UpdateHandlerOptions` when calling [`workflow.setHandler`](https://typescript.temporal.io/api/namespaces/workflow#sethandler).
    The validator must be a non-async function that accepts the same argument types as the handler and returns `void`.

- Accepting and rejecting Updates with validators:
  - To reject an Update, throw an error of any type in the validator.
  - Without a validator, Updates are always accepted.
- Validators and Event History:
  - The `WorkflowExecutionUpdateAccepted` event is written into History whether the acceptance was automatic or due to a validator function not throwing an error.
  - When a Validator throws an error, the Update is rejected and `WorkflowExecutionUpdateAccepted` _won't_ be added to the Event History.
    The caller receives an "Update failed" error.

- Use [`workflow.currentUpdateInfo`](https://typescript.temporal.io/api/namespaces/workflow#current_update_info) to obtain information about the current Update.
  This includes the Update ID, which can be useful for deduplication when using Continue-As-New: see [Ensuring your messages are processed exactly once](/handling-messages#exactly-once-message-processing).
- Update and Signal handlers can be `async`, letting them use Activities, Child Workflows, durable [`workflow.sleep`](https://typescript.temporal.io/api/namespaces/workflow#sleep) Timers, [`workflow.condition`](https://typescript.temporal.io/api/namespaces/workflow#condition) conditions, and more.
  See [Async handlers](#async-handlers) and [Workflow message passing](/encyclopedia/workflow-message-passing) for safe usage guidelines.
- If your Workflow needs to do some async initialization before handling an Update or Signal, use [`workflow.condition`](https://typescript.temporal.io/api/namespaces/workflow#condition) inside your handler to wait until initialization has completed.

## Send messages {#send-messages}

To send Queries, Signals, or Updates, you call methods on a [WorkflowHandle](https://typescript.temporal.io/api/namespaces/client#workflowhandle) object:

- Use [`client.workflow.start`](https://typescript.temporal.io/api/classes/client.WorkflowClient#start) and return its handle.

- Use [`client.workflow.getHandle`](https://typescript.temporal.io/api/classes/client.WorkflowClient#gethandle) to retrieve a Workflow handle by its Workflow Id.

For example:

```typescript
const handle = await client.workflow.start(greetingWorkflow, {
  taskQueue: 'my-task-queue',
  args: [myArg],
  workflowId: 'my-workflow-id',
});
```

To check the argument types required when sending messages -- and the return type for Queries and Updates -- refer to the corresponding handler method in the Workflow Definition.

:::warning Using Continue-as-New and Updates

- Temporal _does not_ support Continue-as-New functionality within Update handlers.
- Complete all handlers _before_ using Continue-as-New.
- Use Continue-as-New from your main Workflow Definition method, just as you would complete or fail a Workflow Execution.

:::

### Send a Query {#send-query}

Use [`WorkflowHandle.query`](https://typescript.temporal.io/api/interfaces/client.WorkflowHandle/#query) to send a Query to a Workflow Execution:

```typescript
const supportedLanguages = await handle.query(getLanguages, {
  includeUnsupported: false,
});
```

- Sending a Query doesn’t add events to a Workflow's Event History.

- You can send Queries to closed Workflow Executions within a Namespace's Workflow retention period.
  This includes Workflows that have completed, failed, or timed out.
  Querying terminated Workflows is not safe and, therefore, not supported.

- A Worker must be online and polling the Task Queue to process a Query.

### Send a Signal {#send-signal}

You can send a Signal to a Workflow Execution from a Temporal Client or from another Workflow Execution.
However, you can only send Signals to Workflow Executions that haven’t closed.

#### Send a Signal from a Client {#send-signal-from-client}

Use [WorkflowHandle.signal](https://typescript.temporal.io/api/interfaces/client.WorkflowHandle#signal) to send a Signal:

```typescript
await handle.signal(greetingWorkflow.approve, { name: 'me' });
```

- The call returns when the server accepts the Signal; it does _not_ wait for the Signal to be delivered to the Workflow Execution.

- The [WorkflowExecutionSignaled](/references/events#workflowexecutionsignaled) Event appears in the Workflow's Event History.

### Send a Signal from a Workflow {#send-signal-from-workflow}

A Workflow can send a Signal to another Workflow, in which case it's called an _External Signal_.
Use [`getExternalWorkflowHandle`](https://typescript.temporal.io/api/namespaces/workflow#getExternalWorkflowHandle):

```typescript

export async function yourWorkflowThatSignals() {
  const handle = getExternalWorkflowHandle('workflow-id-123');
  await handle.signal(joinSignal, { userId: 'user-1', groupId: 'group-1' });
}
```

When an External Signal is sent:

- A [SignalExternalWorkflowExecutionInitiated](/references/events#signalexternalworkflowexecutioninitiated) Event appears in the sender's Event History.
- A [WorkflowExecutionSignaled](/references/events#workflowexecutionsignaled) Event appears in the recipient's Event History.

The `getExternalWorkflowHandle` method helps ensure that Workflows remain deterministic.
Recall that one aspect of deterministic Workflows means not directly making network calls from the Workflow.
This means that developers cannot use a Temporal Client directly within the Workflow code to send Signals or start other Workflows.
Instead, to communicate between Workflows, we use `getExternalWorkflowHandle` to both ensure that Workflows remain deterministic and also that these interactions are recorded as Events in the Workflow's Event History.

### Signal-With-Start {#signal-with-start}

Signal-With-Start allows a Client to send a Signal to a Workflow Execution, starting the Execution if it is not already running.
Use [`Client.workflow.signalWithStart`](https://typescript.temporal.io/api/classes/client.WorkflowClient#signalwithstart):

```typescript

const client = new Client();

await client.workflow.signalWithStart(yourWorkflow, {
  workflowId: 'workflow-id-123',
  taskQueue: 'my-taskqueue',
  args: [{ foo: 1 }],
  signal: joinSignal,
  signalArgs: [{ userId: 'user-1', groupId: 'group-1' }],
});
```

Signal-With-Start is limited to Client use.
It cannot be called from a Workflow.

### Send an Update {#send-update-from-client}

An Update is a synchronous, blocking call that can change Workflow state, control its flow, and return a result.

A client sending an Update must wait until the Server delivers the Update to a Worker.
Workers must be available and responsive.
If you need a response as soon as the Server receives the request, use a Signal instead.
Also note that you can't send Updates to other Workflow Executions or perform an Update equivalent of Signal-With-Start.

- `WorkflowExecutionUpdateAccepted` is added to the Event History when the Worker confirms that the Update passed validation.
- `WorkflowExecutionUpdateCompleted` is added to the Event History when the Worker confirms that the Update has finished.

To send an Update to a Workflow Execution, you can:

- Call [`WorkflowHandle.executeUpdate`](https://typescript.temporal.io/api/interfaces/client.WorkflowHandle/#executeUpdate) and wait for the Update to complete.
  This code fetches an Update result:

  ```typescript
  let previousLanguage = await handle.executeUpdate(setLanguage, {
    args: [Language.CHINESE],
  });
  ```

- Send [`WorkflowHandle.startUpdate`](https://typescript.temporal.io/api/interfaces/client.WorkflowHandle/#startUpdate) to receive an [`WorkflowUpdateHandle`](https://typescript.temporal.io/api/interfaces/client.WorkflowUpdateHandle) as soon as the Update is accepted or rejected.

  - Use this `UpdateHandle` later to fetch your results.
  - `async` Update handlers normally perform long-running asynchronous operations, such as calling an Activity.
  - `startUpdate` only waits until the Worker has accepted or rejected the Update, not until all asynchronous operations are complete.

  For example:

  ```typescript
  const updateHandle = await handle.startUpdate(setLanguage, {
    args: [Language.ENGLISH],
    waitForStage: WorkflowUpdateStage.ACCEPTED,
  });
  previousLanguage = await updateHandle.result();
  ```

  For more details, see the "Async handlers" section.

To obtain an Update handle, you can:

- Use [`WorkflowHandle.startUpdate`](https://typescript.temporal.io/api/interfaces/client.WorkflowHandle/#startUpdate) to start an Update and return the handle, as shown in the preceding example.
- Use [`getUpdateHandle`](https://typescript.temporal.io/api/interfaces/client.WorkflowHandle/#getupdatehandle) to fetch a handle for an in-progress Update using the Update ID.

#### Update-With-Start {#update-with-start}

:::tip

For open source server users, Temporal Server version [Temporal Server version 1.28](https://github.com/temporalio/temporal/releases/tag/v1.28.0) is recommended.

:::

[Update-with-Start](/sending-messages#update-with-start) lets you
[send an Update](/develop/typescript/message-passing#send-update-from-client) that checks whether an already-running Workflow with that ID exists:

- If the Workflow exists, the Update is processed.
- If the Workflow does not exist, a new Workflow Execution is started with the given ID, and the Update is processed before the main Workflow method starts to execute.

Use [`executeUpdateWithStart`](https://typescript.temporal.io/api/classes/client.WorkflowClient#executeUpdateWithStart) to start an Update and wait for the result in one go.

Alternatively, use [`startUpdateWithStart`](https://typescript.temporal.io/api/classes/client.WorkflowClient#startUpdateWithStart) to start an Update and receive a [`WorkflowUpdateHandle`](https://typescript.temporal.io/api/interfaces/client.WorkflowUpdateHandle), and then use `await updateHandle.result()` to retrieve the result from the Update.

These calls return once the requested Update wait stage has been reached, or when the request times out.

You will need to provide a [`WithStartWorkflowOperation`](https://typescript.temporal.io/api/classes/client.WithStartWorkflowOperation) to define the Workflow that will be started if necessary, and its arguments.
You must specify a [WorkflowIdConflictPolicy](/workflow-execution/workflowid-runid#workflow-id-conflict-policy) when creating the `WithStartWorkflowOperation`.
Note that a `WithStartWorkflowOperation` can only be used once.

Here's an example taken from the [early-return](https://github.com/temporalio/samples-typescript/tree/main/early-return) sample:

```typescript
const startWorkflowOperation = new WithStartWorkflowOperation.create(
  transactionWorkflow,
  {
    workflowId,
    args: [transactionID],
    taskQueue: 'early-return',
    workflowIdConflictPolicy: 'FAIL',
  },
);

const earlyConfirmation = await client.workflow.executeUpdateWithStart(
  getTransactionConfirmation,
  {
    startWorkflowOperation,
  },
);

const wfHandle = await startWorkflowOperation.workflowHandle();
const finalReport = await wfHandle.result();
```

:::info SEND MESSAGES WITHOUT TYPE SAFETY

In real-world development, sometimes you may be unable to import message type objects defined by `defineQuery`, `defineSignal`, or `defineUpdate`.
When you don't have access to the Workflow Definition or it isn't written in Typescript, you can still use APIs that aren't type-safe, and dynamic method invocation.
Pass message type names instead of message type objects to:

- [`client.workflow.start`](https://typescript.temporal.io/api/classes/client.WorkflowClient#start)
- [`WorkflowHandle.query`](https://typescript.temporal.io/api/interfaces/client.WorkflowHandle/#query)
- [WorkflowHandle.signal](https://typescript.temporal.io/api/interfaces/client.WorkflowHandle#signal)
- [`WorkflowHandle.executeUpdate`](https://typescript.temporal.io/api/interfaces/client.WorkflowHandle/#executeUpdate)
- [`WorkflowHandle.startUpdate`](https://typescript.temporal.io/api/interfaces/client.WorkflowHandle/#startUpdate)

Pass Workflow IDs to these APIs to get Workflow handles:

- [`client.workflow.getHandle`](https://typescript.temporal.io/api/classes/client.WorkflowClient#gethandle)
- [`getExternalWorkflowHandle`](https://typescript.temporal.io/api/namespaces/workflow#getExternalWorkflowHandle).

:::

## Message handler patterns {#message-handler-patterns}

This section covers common write operations, such as Signal and Update handlers.
It doesn't apply to pure read operations, like Queries or Update Validators.

:::tip

For additional information, see [Inject work into the main Workflow](/handling-messages#injecting-work-into-main-workflow), [Ensuring your messages are processed exactly once](/handling-messages#exactly-once-message-processing), and [this sample](https://github.com/temporalio/samples-typescript/blob/main/message-passing/safe-message-handlers/README.md) demonstrating safe `async` message handling.

:::

### Use async handlers {#async-handlers}

Signal and Update handlers can be `async` functions.
Using `async` allows you to use `await` with Activities, Child Workflows, durable [`workflow.sleep`](https://typescript.temporal.io/api/namespaces/workflow#sleep) Timers, [`workflow.condition`](https://typescript.temporal.io/api/namespaces/workflow#condition) conditions, etc.
This expands the possibilities for what can be done by a handler but it also means that handler executions and your main Workflow method are all running concurrently, with switching occurring between them at `await` calls.
It's essential to understand the things that could go wrong in order to use `async` handlers safely.
See [Workflow message passing](/encyclopedia/workflow-message-passing) for guidance on safe usage of async Signal and Update handlers, the [Safe message handlers](https://github.com/temporalio/samples-typescript/blob/main/message-passing/safe-message-handlers/README.md) sample, and the [Controlling handler concurrency](#control-handler-concurrency) and [Waiting for message handlers to finish](#wait-for-message-handlers) sections below.

The following code executes an Activity that makes a network call to a remote service.
It modifies the Update handler from earlier on this page, turning it into an `async` function:

```typescript
// 👉 Use the objects returned by defineUpdate to set the Update handler in
// Workflow code, and to send Updates from Client code.
export const setLanguageUsingActivity = wf.defineUpdate<Language, [Language]>('setLanguageUsingActivity');

export async function greetingWorkflow(): Promise<string> {
  const greetings: Partial<Record<Language, string>> = {
    [Language.CHINESE]: '你好，世界',
    [Language.ENGLISH]: 'Hello, world',
  };

  let language = Language.ENGLISH;

  const lock = new Mutex();
  wf.setHandler(setLanguageUsingActivity, async (newLanguage) => {
    // 👉 An Update handler can mutate the Workflow state and return a value.
    // 👉 Since this update handler is async, it can execute an activity.
    if (!(newLanguage in greetings)) {
      // 👉 Do the following with the lock held to ensure that multiple calls to set_language are processed in order.
      await lock.runExclusive(async () => {
        if (!(newLanguage in greetings)) {
          const greeting = await callGreetingService(newLanguage);
          if (!greeting) {
            // 👉 An update validator cannot be async, so cannot be used to check that the remote
            // call_greeting_service supports the requested language. Raising ApplicationError
            // will fail the Update, but the WorkflowExecutionUpdateAccepted event will still be
            // added to history.
            throw new wf.ApplicationFailure(`${newLanguage} is not supported by the greeting service`);
          }
          greetings[newLanguage] = greeting;
        }
      });
    }
    const previousLanguage = language;
    language = newLanguage;
    return previousLanguage;
  });
  ...
}
```

After updating the code to use `async`, your Update handler can schedule an Activity and await the result.
Although an `async` Signal handler can also execute an Activity, using an Update handler allows the client to receive a result or error once the Activity completes.
This lets your client track the progress of asynchronous work performed by the Update's Activities, Child Workflows, etc.

### Add wait conditions to block

Sometimes, `async` Signal or Update handlers need to meet certain conditions before they should continue.
You can use [`workflow.condition`](https://typescript.temporal.io/api/namespaces/workflow#condition) to prevent the code from proceeding until a condition is true.
You specify the condition by passing a function that returns `true` or `false`.
This is an important feature that helps you control your handler logic.

Here are three important use cases for `workflow.condition`:

- Waiting for a Signal or Update to arrive
- Waiting in a handler until it is appropriate to continue.
- Waiting in the main Workflow until all active handlers have finished.

#### Wait for a Signal or Update to arrive

It's common to use `workflow.condition` to wait for a particular Signal or Update to be sent by a Client:

```typescript
export async function greetingWorkflow(): Promise<string> {
  let approvedForRelease = false;
  let approverName: string | undefined;

  wf.setHandler(approve, (input) => {
    approvedForRelease = true;
    approverName = input.name;
  });
  ...

  await wf.condition(() => approvedForRelease);
  ...
}
```

#### Use wait conditions in handlers

It's common to use a Workflow wait condition in a handler.
For example, suppose your Workflow has a mutable variable `readyForUpdateToExecute` that indicates whether your Update handler should be allowed to start executing.
You can use `workflow.condition` in the handler to make the handler pause until the condition is met:

```typescript
let readyForUpdateToExecute = false;

wf.setHandler(myUpdate, async (input: MyUpdateInput): Promise<MyUpdateOutput> => {
  await wf.condition(() => readyForUpdateToExecute);
  ...
});
```

Remember: handlers can execute before the main Workflow method starts.

You can also use wait conditions anywhere else in the handler to wait for a specific condition to become true.
This allows you to write handlers that pause at multiple points, each time waiting for a required condition to become true.

#### Ensure your handlers finish before the Workflow completes {#wait-for-message-handlers}

Workflow wait conditions can ensure your handler completes before a Workflow finishes.
When your Workflow uses `async` Signal or Update handlers, your main Workflow method can return or Continue-as-New while a handler is still waiting on an async task, such as an Activity result.
The Workflow completing may interrupt the handler before it finishes crucial work and cause client errors when trying retrieve Update results.
Use [`workflow.condition`](https://typescript.temporal.io/api/namespaces/workflow#condition) and [`allHandlersFinished`](https://typescript.temporal.io/api/namespaces/workflow#condition#allhandlersfinished) to address this problem and allow your Workflow to end smoothly:

```typescript
export async function myWorkflow(): Promise<MyWorkflowOutput> {
  await wf.condition(wf.allHandlersFinished);
  return workflowOutput;
}
```

By default, your Worker will log a warning when you allow a Workflow Execution to finish with unfinished handler executions.
You can silence these warnings on a per-handler basis by setting the `unfinishedPolicy` in `SignalHandlerOptions` or `UpdateHandlerOptions` when calling [`workflow.setHandler`](https://typescript.temporal.io/api/namespaces/workflow#sethandler)

See [Finishing handlers before the Workflow completes](/handling-messages#finishing-message-handlers) for more information.

### Use a lock to prevent concurrent handler execution {#control-handler-concurrency}

Concurrent processes can interact in unpredictable ways.
Incorrectly written [concurrent message-passing](/handling-messages#message-handler-concurrency) code may not work correctly when multiple handler instances run simultaneously.
Here's an example of a pathological case:

```typescript
export async function myWorkflow(): Promise<MyWorkflowOutput> {
  let x = 0;
  let y = 0;
  wf.setHandler(mySignal, async () => {
    const data = await myActivity();
    x = data.x;

    // 🐛🐛 Bug!! If multiple instances of this handler are executing
    // concurrently, then there may be times when the Workflow has x from one
    // Activity execution and y from another.
    await wf.sleep(500); // or await anything else

    y = data.y;
  });
  ...
}
```

Coordinating access using a lock (also known as a mutex) corrects this code.
Locking makes sure that only one handler instance can execute a specific section of code at any given time:

```typescript

...

export async function myWorkflow(): Promise<MyWorkflowOutput> {
  let x = 0;
  let y = 0;
  const lock = new Mutex();

  wf.setHandler(mySignal, async () => {
    await lock.runExclusive(async () => {
      const data = await myActivity();
      x = data.x;

      // ✅ OK: node's event loop may switch now to a different handler
      // execution, or to the main workflow function, but no other execution of
      // this handler can run until this execution finishes.
      await wf.sleep(500); // or await anything else

      y = data.y;
    });
  });
  return {
    name: 'hello',
  };
}
```

## Message handler troubleshooting {#message-handler-troubleshooting}

When sending a Signal, Update, or Query to a Workflow, your Client might encounter the following errors:

- **The client can't contact the server**:
  You'll receive a [`client.ServiceError`](https://typescript.temporal.io/api/classes/client.ServiceError) on which the `cause.code` attribute is [gRPC status code](https://grpc.io/docs/guides/status-codes/) 14 `UNAVAILABLE` (after some retries).

- **The workflow does not exist**:
  You'll receive an [`common.WorkflowNotFoundError`](https://typescript.temporal.io/api/classes/common.WorkflowNotFoundError) error.

### Problems when sending a Signal {#signal-problems}

When using Signal, the two errors described above are the only errors that will result from your requests.

For Queries and Updates, the client waits for a response from the Worker and therefore additional errors may occur during the handler Execution by the Worker.

### Problems when sending an Update {#update-problems}

When working with Updates, you may encounter these problems:

- **No Workflow Workers are polling the Task Queue**:
  Your request will be retried by the SDK Client indefinitely.

- **Update failed**: You'll receive a [`client.WorkflowUpdateFailedError`](https://typescript.temporal.io/api/classes/client.WorkflowUpdateFailedError) exception.
  There are two ways this can happen:

  - The Update was rejected by an Update validator defined in the Workflow alongside the Update handler.

  - The Update failed after having been accepted.

  Update failures are like [Workflow failures](/references/failures#errors-in-workflows).
  Issues that cause a Workflow failure in the main method also cause Update failures in the Update handler.
  These might include:

      - A failed Child Workflow
      - A failed Activity (if the Activity retries have been set to a finite number)
      - The Workflow author raising `ApplicationFailure`

- **The handler caused the Workflow Task to fail**:
  A [Workflow Task Failure](/references/failures#errors-in-workflows) causes the server to retry Workflow Tasks indefinitely. What happens to your Update request depends on its stage:
  - If the request hasn't been accepted by the server, you receive a [`client.ServiceError`](https://typescript.temporal.io/api/classes/client.ServiceError) on which the `cause.code` attribute is [gRPC status code](https://grpc.io/docs/guides/status-codes/) 9 `FAILED_PRECONDITION` (after some retries).
  - If the request has been accepted, it is durable.
    Once the Workflow is healthy again after a code deploy, use an [`WorkflowUpdateHandle`](https://typescript.temporal.io/api/interfaces/client.WorkflowUpdateHandle) to fetch the Update result.

- **The Workflow finished while the Update handler execution was in progress**:
  You'll receive a [`client.ServiceError`](https://typescript.temporal.io/api/classes/client.ServiceError) on which the `cause.code` attribute is [gRPC status code](https://grpc.io/docs/guides/status-codes/) 5 `NOT_FOUND`.
  This happens if the Workflow finished while the Update handler execution was in progress, for example because

  - The Workflow was canceled or failed.

  - The Workflow completed normally or continued-as-new and the Workflow author did not [wait for handlers to be finished](/handling-messages#finishing-message-handlers).

### Problems when sending a Query {#query-problems}

When working with Queries, you may encounter these errors:

- **There is no Workflow Worker polling the Task Queue**:
  You'll receive a [`client.ServiceError`](https://typescript.temporal.io/api/classes/client.ServiceError) on which the `cause.code` attribute is [gRPC status code](https://grpc.io/docs/guides/status-codes/) 9 `FAILED_PRECONDITION`.

- **Query failed**:
  You'll receive a [`client.QueryNotRegisteredError`](https://typescript.temporal.io/api/classes/client.QueryNotRegisteredError) exception if something goes wrong during a Query.
  Any error in a Query handler will trigger this error.
  This differs from Signal and Update requests, where errors can lead to Workflow Task Failure instead.

- **The handler caused the Workflow Task to fail.**
  This would happen, for example, if the Query handler blocks the thread for too long without yielding.

## Define Signals and Queries statically or dynamically {#dynamic-handler}

- Handlers for both Signals and Queries can take arguments, which can be used inside `setHandler` logic.
- Only Signal Handlers can mutate state, and only Query Handlers can return values.

* [Define Signals and Queries statically](#static-signals-and-queries)
* [Define Signals and Queries dynamically](#dynamic-signals-and-queries)

### Define Signals and Queries statically {#static-signals-and-queries}

If you know the name of your Signals and Queries upfront, we recommend declaring them outside the Workflow Definition.

<!--SNIPSTART typescript-blocked-workflow-->
[signals-queries/src/workflows.ts](https://github.com/temporalio/samples-typescript/blob/main/signals-queries/src/workflows.ts)
```ts

export const unblockSignal = wf.defineSignal('unblock');
export const isBlockedQuery = wf.defineQuery<boolean>('isBlocked');

export async function unblockOrCancel(): Promise<void> {
  let isBlocked = true;
  wf.setHandler(unblockSignal, () => void (isBlocked = false));
  wf.setHandler(isBlockedQuery, () => isBlocked);
  wf.log.info('Blocked');
  try {
    await wf.condition(() => !isBlocked);
    wf.log.info('Unblocked');
  } catch (err) {
    if (err instanceof wf.CancelledFailure) {
      wf.log.info('Cancelled');
    }
    throw err;
  }
}
```
<!--SNIPEND-->

This technique helps provide type safety because you can export the type signature of the Signal or Query to be called by the Client.

### Define Signals and Queries dynamically {#dynamic-signals-and-queries}

For more flexible use cases, you might want a dynamic Signal (such as a generated ID).
You can handle it in two ways:

- Avoid making it dynamic by collapsing all Signals into one handler and move the ID to the payload.
- Actually make the Signal name dynamic by inlining the Signal definition per handler.

```ts

// "fat handler" solution
wf.setHandler(`genericSignal`, (payload) => {
  switch (payload.taskId) {
    case taskAId:
      // do task A things
      break;
    case taskBId:
      // do task B things
      break;
    default:
      throw new Error('Unexpected task.');
  }
});

// "inline definition" solution
wf.setHandler(wf.defineSignal(`task-${taskAId}`), (payload) => {
  /* do task A things */
});
wf.setHandler(wf.defineSignal(`task-${taskBId}`), (payload) => {
  /* do task B things */
});

// utility "inline definition" helper
const inlineSignal = (signalName, handler) =>
  wf.setHandler(wf.defineSignal(signalName), handler);
inlineSignal(`task-${taskBId}`, (payload) => {
  /* do task B things */
});
```

<details>
  <summary>
    API Design FAQs
  </summary>

**Why not "new Signal" and "new Query"?**

The semantic of `defineSignal` and `defineQuery` is intentional.
They return Signal and Query **definitions**, not unique instances of Signals and Queries themselves
The following is their [entire source code](https://github.com/temporalio/sdk-typescript/blob/fc658d3760e6653aec47732ab17a0062b7dd23fc/packages/workflow/src/workflow.ts#L883-L907):

```ts
/**
 * Define a signal method for a Workflow.
 */
export function defineSignal<Args extends any[] = []>(
  name: string,
): SignalDefinition<Args> {
  return {
    type: 'signal',
    name,
  };
}

/**
 * Define a query method for a Workflow.
 */
export function defineQuery<Ret, Args extends any[] = []>(
  name: string,
): QueryDefinition<Ret, Args> {
  return {
    type: 'query',
    name,
  };
}
```

Signals and Queries are instantiated only in `setHandler` and are specific to particular Workflow Executions.

These distinctions might seem minor, but they model how Temporal works under the hood, because Signals and Queries are messages identified by "just strings" and don't have meaning independent of the Workflow having a listener to handle them.
This will be clearer if you refer to the Client-side APIs.

**Why setHandler and not OTHER_API?**

We named it `setHandler` instead of `subscribe` because a Signal or Query can have only one "handler" at a time, whereas `subscribe` could imply an Observable with multiple consumers and is a higher-level construct.

```ts
wf.setHandler(MySignal, handlerFn1);
wf.setHandler(MySignal, handlerFn2); // replaces handlerFn1
```

If you are familiar with [RxJS](https://rxjs.dev/), you are free to wrap your Signals and Queries into Observables if you want, or you could dynamically reassign the listener based on your business logic or Workflow state.

</details>

---

## Manage Namespaces - TypeScript SDK

## How to create and manage Namespaces {#namespaces}

You can create, update, deprecate or delete your [Namespaces](/namespaces) using either the Temporal CLI or SDK APIs.

Use Namespaces to isolate your Workflow Executions according to your needs.
For example, you can use Namespaces to match the development lifecycle by having separate `dev` and `prod` Namespaces.
You could also use them to ensure Workflow Executions between different teams never communicate - such as ensuring that the `teamA` Namespace never impacts the `teamB` Namespace.

On Temporal Cloud, use the [Temporal Cloud UI](/cloud/namespaces#create-a-namespace) to create and manage a Namespace from the UI, or [tcld commands](https://docs.temporal.io/cloud/tcld/namespace/) to manage Namespaces from the command-line interface.

On self-hosted Temporal Service, you can register and manage your Namespaces using the Temporal CLI (recommended) or programmatically using APIs.
Note that these APIs and Temporal CLI commands will not work with Temporal Cloud.

Use a custom [Authorizer](/self-hosted-guide/security#authorizer-plugin) on your Frontend Service in the Temporal Service to set restrictions on who can create, update, or deprecate Namespaces.

You must register a Namespace with the Temporal Service before setting it in the Temporal Client.

### How to register Namespaces {#register-namespace}

Registering a Namespace creates a Namespace on the Temporal Service or Temporal Cloud.

On Temporal Cloud, use the [Temporal Cloud UI](/cloud/namespaces#create-a-namespace) or [tcld commands](https://docs.temporal.io/cloud/tcld/namespace/) to create Namespaces.

On self-hosted Temporal Service, you can register your Namespaces using the Temporal CLI (recommended) or programmatically using APIs.
Note that these APIs and Temporal CLI commands will not work with Temporal Cloud.

Use a custom [Authorizer](/self-hosted-guide/security#authorizer-plugin) on your Frontend Service in the Temporal Service to set restrictions on who can create, update, or deprecate Namespaces.

### How to manage Namespaces {#manage-namespaces}

You can get details for your Namespaces, update Namespace configuration, and deprecate or delete your Namespaces.

On Temporal Cloud, use the [Temporal Cloud UI](/cloud/namespaces#create-a-namespace) or [tcld commands](https://docs.temporal.io/cloud/tcld/namespace/) to manage Namespaces.

On self-hosted Temporal Service, you can manage your registered Namespaces using the Temporal CLI (recommended) or programmatically using APIs.
Note that these APIs and Temporal CLI commands will not work with Temporal Cloud.

Use a custom [Authorizer](/self-hosted-guide/security#authorizer-plugin) on your Frontend Service in the Temporal Service to set restrictions on who can create, update, or deprecate Namespaces.

You must register a Namespace with the Temporal Service before setting it in the Temporal Client.

---

## Observability - TypeScript SDK

The observability section of the TypeScript developer guide covers the many ways to view the current state of your [Temporal Application](/temporal#temporal-application)—that is, ways to view which [Workflow Executions](/workflow-execution) are tracked by the [Temporal Platform](/temporal#temporal-platform) and the state of any specified Workflow Execution, either currently or at points of an execution.

This section covers features related to viewing the state of the application, including:

- [Emit metrics](#metrics)
- [Set up tracing](#tracing)
- [Log from a Workflow](#logging)
- [Visibility APIs](#visibility)

## Emit metrics {#metrics}

Each Temporal SDK is capable of emitting an optional set of metrics from either the Client or the Worker process.
For a complete list of metrics capable of being emitted, see the [SDK metrics reference](/references/sdk-metrics).

- For an overview of Prometheus and Grafana integration, refer to the [Monitoring](/self-hosted-guide/monitoring) guide.
- For a list of metrics, see the [SDK metrics reference](/references/sdk-metrics).
- For an end-to-end example that exposes metrics with the TypeScript SDK, refer to the [samples-typescript](https://github.com/temporalio/samples-typescript/tree/main/interceptors-opentelemetry) repo.

Workers can emit metrics and traces. There are a few [telemetry options](https://typescript.temporal.io/api/interfaces/worker.TelemetryOptions) that can be provided to [`Runtime.install`](https://typescript.temporal.io/api/classes/worker.Runtime/#install). The common options are:

- `metrics: { otel: { url } }`: The URL of a gRPC [OpenTelemetry collector](https://opentelemetry.io/docs/collector/).
- `metrics: { prometheus: { bindAddress } }`: Address on the Worker host that will have metrics for [Prometheus](https://prometheus.io/) to scrape.

To set up tracing of Workflows and Activities, use our `opentelemetry-interceptors` package.
(For details, see the next section.)

```typescript
telemetryOptions: {
    metrics: {
      prometheus: { bindAddress: '0.0.0.0:9464' },
    },
    logging: { forward: { level: 'DEBUG' } },
  },
```

## Set up tracing {#tracing}

Tracing allows you to view the call graph of a Workflow along with its Activities and any Child Workflows.

Temporal Web's tracing capabilities mainly track Activity Execution within a Temporal context. If you need custom tracing specific for your use case, you should make use of context propagation to add tracing logic accordingly.

The [`interceptors-opentelemetry`](https://github.com/temporalio/samples-typescript/tree/main/interceptors-opentelemetry) sample shows how to use the SDK's built-in OpenTelemetry tracing to trace everything from starting a Workflow to Workflow Execution to running an Activity from that Workflow.

The built-in tracing uses protobuf message headers (like [this one](https://github.com/temporalio/api/blob/b2b8ae6592a8730dd5be6d90569d1aea84e1712f/temporal/api/workflowservice/v1/request_response.proto#L161) when starting a Workflow) to propagate the tracing information from the client to the Workflow and from the Workflow to its successors (when Continued As New), children, and Activities.
All of these executions are linked with a single trace identifier and have the proper `parent -> child` span relation.

Tracing is compatible between different Temporal SDKs as long as compatible [context propagators](https://opentelemetry.io/docs/concepts/context-propagation/) are used.

**Context propagation**

The TypeScript SDK uses the global OpenTelemetry propagator.

To extend the default ([Trace Context](https://github.com/open-telemetry/opentelemetry-js/blob/main/packages/opentelemetry-core/README.md#w3ctracecontextpropagator-propagator) and [Baggage](https://github.com/open-telemetry/opentelemetry-js/blob/main/packages/opentelemetry-core/README.md#baggage-propagator) propagators) to also include the [Jaeger propagator](https://www.npmjs.com/package/@opentelemetry/propagator-jaeger), follow these steps:

- `npm i @opentelemetry/propagator-jaeger`

- At the top level of your Workflow code, add the following lines:

  ```js

    CompositePropagator,
    W3CBaggagePropagator,
    W3CTraceContextPropagator,
  } from '@opentelemetry/core';

  propagation.setGlobalPropagator(
    new CompositePropagator({
      propagators: [
        new W3CTraceContextPropagator(),
        new W3CBaggagePropagator(),
        new JaegerPropagator(),
      ],
    }),
  );
  ```

Similarly, you can customize the OpenTelemetry `NodeSDK` propagators by following the instructions in the [Initialize the SDK](https://github.com/open-telemetry/opentelemetry-js/tree/main/experimental/packages/opentelemetry-sdk-node#initialize-the-sdk) section of the `README.md` file.

## Log from a Workflow {#logging}

Logging enables you to record critical information during code execution.
Loggers create an audit trail and capture information about your Workflow's operation.
An appropriate logging level depends on your specific needs.
During development or troubleshooting, you might use debug or even trace.
In production, you might use info or warn to avoid excessive log volume.

The logger supports the following logging levels:

| Level   | Use                                                                                                       |
| ------- | --------------------------------------------------------------------------------------------------------- |
| `TRACE` | The most detailed level of logging, used for very fine-grained information.                               |
| `DEBUG` | Detailed information, typically useful for debugging purposes.                                            |
| `INFO`  | General information about the application's operation.                                                    |
| `WARN`  | Indicates potentially harmful situations or minor issues that don't prevent the application from working. |
| `ERROR` | Indicates error conditions that might still allow the application to continue running.                    |

The Temporal SDK core normally uses `WARN` as its default logging level.

### Logging from Activities

Activities run in the standard Node.js environment and may therefore use any Node.js logger directly.

The Temporal SDK however provides a convenient Activity Context logger, which funnels log messages to the [Runtime's logger](/develop/typescript/observability#customizing-the-default-logger). Attributes from the current Activity context are automatically included as metadata on every log entries emitted using the Activity context logger, and some key events of the Activity's lifecycle are automatically logged (at DEBUG level for most messages; WARN for failures).

<details>
<summary>
Using the Activity Context logger
</summary>

```ts

export async function greet(name: string): Promise<string> {
  log.info('Log from activity', { name });
  return `Hello, ${name}!`;
}
```

</details>

{/*

#### Customizing Activity logging with `ActivityOutboundCallsInterceptor`

FIXME(JWH): Quick introduction to `ActivityOutboundCallsInterceptor.getLogAttributes()`.
*/}

### Logging from Workflows

Workflows may not use regular Node.js loggers because:

1. Workflows run in a sandboxed environment and cannot do any I/O.
1. Workflow code might get replayed at any time, which would result in duplicated log messages.

The Temporal SDK however provides a Workflow Context logger, which funnels log messages to the [Runtime's logger](/develop/typescript/observability#customizing-the-default-logger). Attributes from the current Workflow context are automatically included as metadata on every log entries emitted using the Workflow context logger, and some key events of the Workflow's lifecycle are automatically logged (at DEBUG level for most messages; WARN for failures).

<details>
<summary>
Using the Workflow Context logger
</summary>

```ts

export async function myWorkflow(name: string): Promise<string> {
  log.info('Log from workflow', { name });
  return `Hello, ${name}!`;
}
```

</details>

The Workflow Context Logger tries to avoid reemitting log messages on Workflow Replays.

{/*

#### Customizing Workflow logging using `WorkflowOutboundCallsInterceptor`

FIXME(JWH): Quick introduction to `WorkflowOutboundCallsInterceptor.getLogAttributes()`.
*/}

#### Limitations of Workflow logs

Internally, Workflow logging uses Sinks, and is consequently subject to the same limitations as Sinks.
Notably, logged objects must be serializable using the V8 serialization.

{/* FIXME(JWH): Add more details and link to actual Sinks documentation */}

### What is the Runtime's Logger

A Temporal Worker may emit logs in various ways, including:

- Messages emitted using the [Workflow Context Logger](#logging);
- Messages emitted using the [Activity Context Logger](#logging-from-activities);
- Messages emitted by the TypeScript SDK Worker itself;
- Messages emitted by the underlying Temporal Core SDK (native code).

All of these messages are internally routed to a single logger object, called the Runtime's Logger.
By default, the Runtime's Logger simply write messages to the console (i.e. the process's `STDOUT`).

#### How to customize the Runtime's Logger

A custom Runtime Logger may be registered when the SDK `Runtime` is instantiated. This is done only once per process.

To register a custom Runtime Logger, you must explicitly instantiate the Runtime, using the [`Runtime.install()`](https://typescript.temporal.io/api/classes/worker.Runtime/#install) function.
For example:

```typescript

  DefaultLogger,
  makeTelemetryFilterString,
  Runtime,
} from '@temporalio/worker';

// This is your custom Logger.
const logger = new DefaultLogger('WARN', ({ level, message }) => {
  console.log(`Custom logger: ${level} — ${message}`);
});

Runtime.install({
  logger,
  // The following block is optional, but generally desired.
  // It allows capturing log messages emitted by the underlying Temporal Core SDK (native code).
  // The Telemetry Filter String determine the desired verboseness of messages emitted by the
  // Temporal Core SDK itself ("core"), and by other native libraries ("other").
  telemetryOptions: {
    logging: {
      filter: makeTelemetryFilterString({ core: 'INFO', other: 'INFO' }),
      forward: {},
    },
  },
});
```

A common use case for this is to write log messages to a file to be picked up by a collector service, such as the [Datadog Agent](https://docs.datadoghq.com/logs/log_collection/nodejs/?tab=winston30).
For example:

```typescript

  DefaultLogger,
  makeTelemetryFilterString,
  Runtime,
} from '@temporalio/worker';

const logger = winston.createLogger({
  level: 'info',
  format: winston.format.json(),
  transports: [new transports.File({ filename: '/path/to/worker.log' })],
});

Runtime.install({
  logger,
  // The following block is optional, but generally desired.
  // It allows capturing log messages emitted by the underlying Temporal Core SDK (native code).
  // The Telemetry Filter String determine the desired verboseness of messages emitted by the
  // Temporal Core SDK itself ("core"), and by other native libraries ("other").
  telemetryOptions: {
    logging: {
      filter: makeTelemetryFilterString({ core: 'INFO', other: 'INFO' }),
      forward: {},
    },
  },
});
```

{/* FIXME(JWH): Everything below this point must be revisited and moved to a distinct section (Sinks). */}

### Implementing custom Logging-like features based on Workflow Sinks

Sinks enable one-way export of logs, metrics, and traces from the Workflow isolate to the Node.js environment.

{/*
Workflows in Temporal may be replayed from the beginning of their history when resumed. In order for Temporal to recreate the exact state Workflow code was in, the code is required to be fully deterministic. To prevent breaking determinism, in the TypeScript SDK, Workflow code runs in an isolated execution environment and may not use any of the Node.js APIs or communicate directly with the outside world. */}

Sinks are written as objects with methods.
Similar to Activities, they are declared in the Worker and then proxied in Workflow code, and it helps to share types between both.

#### Comparing Sinks and Activities

Sinks are similar to Activities in that they are both registered on the Worker and proxied into the Workflow.
However, they differ from Activities in important ways:

- A sink function doesn't return any value back to the Workflow and cannot be awaited.
- A sink call isn't recorded in the Event History of a Workflow Execution (no timeouts or retries).
- A sink function _always_ runs on the same Worker that runs the Workflow Execution it's called from.

#### Declare the sink interface

Explicitly declaring a sink's interface is optional but is useful for ensuring type safety in subsequent steps:

<!--SNIPSTART typescript-logger-sink-interface-->
[sinks/src/workflows.ts](https://github.com/temporalio/samples-typescript/blob/main/sinks/src/workflows.ts)
```ts

export interface AlertSinks extends Sinks {
  alerter: {
    alert(message: string): void;
  };
}

export type MySinks = AlertSinks;
```
<!--SNIPEND-->

#### Implement sinks

Implementing sinks is a two-step process.

Implement and inject the Sink function into a Worker

<!--SNIPSTART typescript-logger-sink-worker-->
[sinks/src/worker.ts](https://github.com/temporalio/samples-typescript/blob/main/sinks/src/worker.ts)
```ts

async function main() {
  const sinks: InjectedSinks<MySinks> = {
    alerter: {
      alert: {
        fn(workflowInfo, message) {
          console.log('sending SMS alert!', {
            workflowId: workflowInfo.workflowId,
            workflowRunId: workflowInfo.runId,
            message,
          });
        },
        callDuringReplay: false, // The default
      },
    },
  };
  const worker = await Worker.create({
    workflowsPath: require.resolve('./workflows'),
    taskQueue: 'sinks',
    sinks,
  });
  await worker.run();
  console.log('Worker gracefully shutdown');
}

main().catch((err) => {
  console.error(err);
  process.exit(1);
});
```
<!--SNIPEND-->

- Sink function implementations are passed as an object into [WorkerOptions](https://typescript.temporal.io/api/interfaces/worker.WorkerOptions/#sinks).
- You can specify whether you want the injected function to be called during Workflow replay by setting the `callDuringReplay` option.

#### Proxy and call a sink function from a Workflow

<!--SNIPSTART typescript-logger-sink-workflow-->
[sinks/src/workflows.ts](https://github.com/temporalio/samples-typescript/blob/main/sinks/src/workflows.ts)
```ts
const { alerter } = proxySinks<MySinks>();

export async function sinkWorkflow(): Promise<string> {
  log.info('Workflow Execution started');
  alerter.alert('alerter: Workflow Execution started');
  return 'Hello, Temporal!';
}
```
<!--SNIPEND-->

Some important features of the [InjectedSinkFunction](https://typescript.temporal.io/api/interfaces/worker.InjectedSinkFunction) interface:

- **Injected WorkflowInfo argument:** The first argument of a Sink function implementation is a [`workflowInfo` object](https://typescript.temporal.io/api/interfaces/workflow.WorkflowInfo/) that contains useful metadata.
- **Limited arguments types:** The remaining Sink function arguments are copied between the sandbox and the Node.js environment using the [structured clone algorithm](https://developer.mozilla.org/en-US/docs/Web/API/Web_Workers_API/Structured_clone_algorithm).
- **No return value:** To prevent breaking determinism, Sink functions cannot return values to the Workflow.

**Advanced: Performance considerations and non-blocking Sinks**

The injected sink function contributes to the overall Workflow Task processing duration.

- If you have a long-running sink function, such as one that tries to communicate with external services, you might start seeing Workflow Task timeouts.
- The effect is multiplied when using `callDuringReplay: true` and replaying long Workflow histories because the Workflow Task timer starts when the first history page is delivered to the Worker.

### How to provide a custom logger {#custom-logger}

Use a custom logger for logging.

#### Logging in Workers and Clients

The Worker comes with a default logger, which defaults to log any messages with level `INFO` and higher to `STDERR` using `console.error`.
The following [log levels](https://typescript.temporal.io/api/namespaces/worker#loglevel) are listed in increasing order of severity.

#### Customizing the default logger

Temporal uses a [`DefaultLogger`](https://typescript.temporal.io/api/classes/worker.DefaultLogger/) that implements the basic interface:

```ts

const logger = new DefaultLogger('WARN', ({ level, message }) => {
  console.log(`Custom logger: ${level} — ${message}`);
});
Runtime.install({ logger });
```

The previous code example sets the default logger to log only messages with level `WARN` and higher.

#### Accumulate logs for testing and reporting

```ts

const logs: LogEntry[] = [];
const logger = new DefaultLogger(LogLevel.TRACE, (entry) => logs.push(entry));

logger.debug('hey', { a: 1 });
logger.info('ho');
logger.warn('lets', { a: 1 });
logger.error('go');
```

A common logging use case is logging to a file to be picked up by a collector like the [Datadog Agent](https://docs.datadoghq.com/logs/log_collection/nodejs/?tab=winston30).

```ts

const logger = winston.createLogger({
  level: 'info',
  format: winston.format.json(),
  transports: [new transports.File({ filename: '/path/to/worker.log' })],
});
Runtime.install({ logger });
```

## Visibility APIs {#visibility}

The term Visibility, within the Temporal Platform, refers to the subsystems and APIs that enable an operator to view Workflow Executions that currently exist within a Temporal Service.

### How to use Search Attributes {#search-attributes}

The typical method of retrieving a Workflow Execution is by its Workflow Id.

However, sometimes you'll want to retrieve one or more Workflow Executions based on another property. For example, imagine you want to get all Workflow Executions of a certain type that have failed within a time range, so that you can start new ones with the same arguments.

You can do this with [Search Attributes](/search-attribute).

- [Default Search Attributes](/search-attribute#default-search-attribute) like `WorkflowType`, `StartTime` and `ExecutionStatus` are automatically added to Workflow Executions.
- _Custom Search Attributes_ can contain their own domain-specific data (like `customerId` or `numItems`).
  - A few [generic Custom Search Attributes](/search-attribute#custom-search-attribute) like `CustomKeywordField` and `CustomIntField` are created by default in Temporal's [Docker Compose](https://github.com/temporalio/docker-compose).

The steps to using custom Search Attributes are:

- Create a new Search Attribute in your Temporal Service using `temporal operator search-attribute create` or the Cloud UI.
- Set the value of the Search Attribute for a Workflow Execution:
  - On the Client by including it as an option when starting the Execution.
  - In the Workflow by calling `UpsertSearchAttributes`.
- Read the value of the Search Attribute:
  - On the Client by calling `DescribeWorkflow`.
  - In the Workflow by looking at `WorkflowInfo`.
- Query Workflow Executions by the Search Attribute using a [List Filter](/list-filter):
  - [With the Temporal CLI](/cli/workflow#list).
  - In code by calling `ListWorkflowExecutions`.

Here is how to query Workflow Executions:

Use [`WorkflowService.listWorkflowExecutions`](https://typescript.temporal.io/api/classes/proto.temporal.api.workflowservice.v1.WorkflowService-1#listworkflowexecutions):

```typescript

const connection = await Connection.connect();
const response = await connection.workflowService.listWorkflowExecutions({
  query: `ExecutionStatus = "Running"`,
});
```

where `query` is a [List Filter](/list-filter).

### How to set custom Search Attributes {#custom-search-attributes}

After you've created custom Search Attributes in your Temporal Service (using `temporal operator search-attribute create` or the Cloud UI), you can set the values of the custom Search Attributes when starting a Workflow.

Use [`WorkflowOptions.searchAttributes`](https://typescript.temporal.io/api/interfaces/client.WorkflowOptions#searchattributes).

<!--SNIPSTART typescript-search-attributes-client-->
[search-attributes/src/client.ts](https://github.com/temporalio/samples-typescript/blob/main/search-attributes/src/client.ts)
```ts
const handle = await client.workflow.start(example, {
  taskQueue: 'search-attributes',
  workflowId: 'search-attributes-example-0',
  searchAttributes: {
    CustomIntField: [2],
    CustomKeywordListField: ['keywordA', 'keywordB'],
    CustomBoolField: [true],
    CustomDatetimeField: [new Date()],
    CustomTextField: [
      'String field is for text. When queried, it will be tokenized for partial match. StringTypeField cannot be used in Order By',
    ],
  },
});

const { searchAttributes } = await handle.describe();
```
<!--SNIPEND-->

The type of `searchAttributes` is `Record<string, string[] | number[] | boolean[] | Date[]>`.

### How to upsert Search Attributes {#upsert-search-attributes}

You can upsert Search Attributes to add or update Search Attributes from within Workflow code.

Inside a Workflow, we can read from [`WorkflowInfo.searchAttributes`](https://typescript.temporal.io/api/interfaces/workflow.WorkflowInfo#searchattributes) and call [`upsertSearchAttributes`](https://typescript.temporal.io/api/namespaces/workflow#upsertsearchattributes):

<!--SNIPSTART typescript-search-attributes-workflow -->
[search-attributes/src/workflows.ts](https://github.com/temporalio/samples-typescript/blob/main/search-attributes/src/workflows.ts)
```ts
export async function example(): Promise<SearchAttributes> {
  const customInt = (workflowInfo().searchAttributes.CustomIntField?.[0] as number) || 0;
  upsertSearchAttributes({
    // overwrite the existing CustomIntField: [2]
    CustomIntField: [customInt + 1],

    // delete the existing CustomBoolField: [true]
    CustomBoolField: [],

    // add a new value
    CustomDoubleField: [3.14],
  });
  return workflowInfo().searchAttributes;
}
```
<!--SNIPEND-->

### How to remove a Search Attribute from a Workflow {#remove-search-attribute}

To remove a Search Attribute that was previously set, set it to an empty array: `[]`.

```typescript

async function yourWorkflow() {
  upsertSearchAttributes({ CustomIntField: [1, 2, 3] });

  // ... later, to remove:
  upsertSearchAttributes({ CustomIntField: [] });
}
```

---

## Schedules - TypeScript SDK

The pages shows how to do the following:

- [Schedule a Workflow](#schedule-a-workflow)
  - [Create a Scheduled Workflow](#create)
  - [Backfill a Scheduled Workflow](#backfill)
  - [Delete a Scheduled Workflow](#delete)
  - [Describe a Scheduled Workflow](#describe)
  - [List a Scheduled Workflow](#list)
  - [Pause a Scheduled Workflow](#pause)
  - [Trigger a Scheduled Workflow](#trigger)
  - [Update a Scheduled Workflow](#update)
- [Temporal Cron Jobs](#temporal-cron-jobs)
- [Start Delay](#start-delay)

## How to Schedule a Workflow {#schedule-a-workflow}

Scheduling Workflows is a crucial aspect of any automation process, especially when dealing with time-sensitive tasks. By scheduling a Workflow, you can automate repetitive tasks, reduce the need for manual intervention, and ensure timely execution of your business processes

Use any of the following action to help Schedule a Workflow Execution and take control over your automation process.

### How to Create a Scheduled Workflow {#create}

The create action enables you to create a new Schedule. When you create a new Schedule, a unique Schedule ID is generated, which you can use to reference the Schedule in other Schedule commands.

:::tip Schedule Auto-Deletion

Once a Schedule has completed creating all its Workflow Executions, the Temporal Service deletes it since it won’t fire again.
The Temporal Service doesn't guarantee when this removal will happen.

:::

<!--SNIPSTART typescript-create-a-scheduled-workflow-->
[schedules/src/start-schedule.ts](https://github.com/temporalio/samples-typescript/blob/main/schedules/src/start-schedule.ts)
```ts
async function run() {
  const config = loadClientConnectConfig();
  const connection = await Connection.connect(config.connectionOptions);
  const client = new Client({ connection });

  // https://typescript.temporal.io/api/classes/client.ScheduleClient#create
  const schedule = await client.schedule.create({
    action: {
      type: 'startWorkflow',
      workflowType: reminder,
      args: ['♻️ Dear future self, please take out the recycling tonight. Sincerely, past you ❤️'],
      taskQueue: 'schedules',
    },
    scheduleId: 'sample-schedule',
    policies: {
      catchupWindow: '1 day',
      overlap: ScheduleOverlapPolicy.ALLOW_ALL,
    },
    spec: {
      intervals: [{ every: '10s' }],
      // or periodic calendar times:
      // calendars: [
      //   {
      //     comment: 'every wednesday at 8:30pm',
      //     dayOfWeek: 'WEDNESDAY',
      //     hour: 20,
      //     minute: 30,
      //   },
      // ],
      // or a single datetime:
      // calendars: [
      //   {
      //     comment: '1/1/23 at 9am',
      //     year: 2023,
      //     month: 1,
      //     dayOfMonth: 1,
      //     hour: 9,
      //   },
      // ],
    },
  });
```
<!--SNIPEND-->

### How to Backfill a Scheduled Workflow {#backfill}

The backfill action executes Actions ahead of their specified time range. This command is useful when you need to execute a missed or delayed Action, or when you want to test the Workflow before its scheduled time.

<!--SNIPSTART typescript-backfill-a-scheduled-workflow-->
[schedules/src/backfill-schedule.ts](https://github.com/temporalio/samples-typescript/blob/main/schedules/src/backfill-schedule.ts)
```ts
function subtractMinutes(minutes: number): Date {
  const now = new Date();
  return new Date(now.getTime() - minutes * 60 * 1000);
}

async function run() {
  const client = new Client({
    connection: await Connection.connect(),
  });

  const backfillOptions: Backfill = {
    start: subtractMinutes(10),
    end: subtractMinutes(9),
    overlap: ScheduleOverlapPolicy.ALLOW_ALL,
  };

  const handle = client.schedule.getHandle('sample-schedule');
  await handle.backfill(backfillOptions);

  console.log(`Schedule is now backfilled.`);
}
```
<!--SNIPEND-->

### How to Delete a Scheduled Workflow {#delete}

The delete action enables you to delete a Schedule. When you delete a Schedule, it does not affect any Workflows that were started by the Schedule.

<!--SNIPSTART typescript-delete-a-scheduled-workflow-->
[schedules/src/delete-schedule.ts](https://github.com/temporalio/samples-typescript/blob/main/schedules/src/delete-schedule.ts)
```ts
async function run() {
  const client = new Client({
    connection: await Connection.connect(),
  });

  const handle = client.schedule.getHandle('sample-schedule');
  await handle.delete();

  console.log(`Schedule is now deleted.`);
}
```
<!--SNIPEND-->

### How to Describe a Scheduled Workflow {#describe}

The describe action shows the current Schedule configuration, including information about past, current, and future Workflow Runs. This command is helpful when you want to get a detailed view of the Schedule and its associated Workflow Runs.

<!--SNIPSTART typescript-describe-a-scheduled-workflow-->
[schedules/src/describe-schedule.ts](https://github.com/temporalio/samples-typescript/blob/main/schedules/src/describe-schedule.ts)
```ts
async function run() {
  const client = new Client({
    connection: await Connection.connect(),
  });

  const handle = client.schedule.getHandle('sample-schedule');

  const result = await handle.describe();

  console.log(`Schedule description: ${JSON.stringify(result)}`);
}
```
<!--SNIPEND-->

### How to List a Scheduled Workflow {#list}

The list action lists all the available Schedules. This command is useful when you want to view a list of all the Schedules and their respective Schedule IDs.

<!--SNIPSTART typescript-list-a-scheduled-workflow-->
[schedules/src/list-schedule.ts](https://github.com/temporalio/samples-typescript/blob/main/schedules/src/list-schedule.ts)
```ts
async function run() {
  const client = new Client({
    connection: await Connection.connect(),
  });

  const schedules = [];

  const scheduleList = client.schedule.list();

  for await (const schedule of scheduleList) {
    schedules.push(schedule);
  }

  console.log(`Schedules are now listed: ${JSON.stringify(schedules)}`);
}
```
<!--SNIPEND-->

### How to Pause a Scheduled Workflow {#pause}

The pause action enables you to pause and unpause a Schedule. When you pause a Schedule, all the future Workflow Runs associated with the Schedule are temporarily stopped. This command is useful when you want to temporarily halt a Workflow due to maintenance or any other reason.

<!--SNIPSTART typescript-pause-a-scheduled-workflow-->
[schedules/src/pause-schedule.ts](https://github.com/temporalio/samples-typescript/blob/main/schedules/src/pause-schedule.ts)
```ts
async function run() {
  const client = new Client({
    connection: await Connection.connect(),
  });

  const handle = client.schedule.getHandle('sample-schedule');
  await handle.pause();

  console.log(`Schedule is now paused.`);
}
```
<!--SNIPEND-->

### How to Trigger a Scheduled Workflow {#trigger}

The trigger action triggers an immediate action with a given Schedule. By default, this action is subject to the Overlap Policy of the Schedule. This command is helpful when you want to execute a Workflow outside of its scheduled time.

<!--SNIPSTART typescript-trigger-a-scheduled-workflow-->
[schedules/src/trigger-schedule.ts](https://github.com/temporalio/samples-typescript/blob/main/schedules/src/trigger-schedule.ts)
```ts
async function run() {
  const client = new Client({
    connection: await Connection.connect(),
  });

  const handle = client.schedule.getHandle('sample-schedule');

  await handle.trigger();

  console.log(`Schedule is now triggered.`);
}
```
<!--SNIPEND-->

### How to Update a Scheduled Workflow {#update}

The update action enables you to update an existing Schedule. This command is useful when you need to modify the Schedule's configuration, such as changing the start time, end time, or interval.

<!--SNIPSTART typescript-update-a-scheduled-workflow-->
[schedules/src/update-schedule.ts](https://github.com/temporalio/samples-typescript/blob/main/schedules/src/update-schedule.ts)
```ts
const updateSchedule = (
  input: ScheduleDescription,
): ScheduleUpdateOptions<ScheduleOptionsStartWorkflowAction<Workflow>> => {
  const scheduleAction = input.action;

  scheduleAction.args = ['my updated schedule arg'];

  return { ...input, ...scheduleAction };
};

async function run() {
  const client = new Client({
    connection: await Connection.connect(),
  });

  const handle = client.schedule.getHandle('sample-schedule');

  await handle.update(updateSchedule);

  console.log(`Schedule is now updated.`);
}
```
<!--SNIPEND-->

## How to use Temporal Cron Jobs {#temporal-cron-jobs}

:::caution Cron support is not recommended

We recommend using [Schedules](https://docs.temporal.io/schedule) instead of Cron Jobs.
Schedules were built to provide a better developer experience, including more configuration options and the ability to update or pause running Schedules.

:::

A [Temporal Cron Job](/cron-job) is the series of Workflow Executions that occur when a Cron Schedule is provided in the call to spawn a Workflow Execution.

A Cron Schedule is provided as an option when the call to spawn a Workflow Execution is made.

You can set each Workflow to repeat on a schedule with the `cronSchedule` option:

```typescript
const handle = await client.workflow.start(scheduledWorkflow, {
  // ...
  cronSchedule: '* * * * *', // start every minute
});
```

Temporal Workflow Schedule Cron strings follow this format:

```
┌───────────── minute (0 - 59)
│ ┌───────────── hour (0 - 23)
│ │ ┌───────────── day of the month (1 - 31)
│ │ │ ┌───────────── month (1 - 12)
│ │ │ │ ┌───────────── day of the week (0 - 6) (Sunday to Saturday)
│ │ │ │ │
* * * * *
```

## Start Delay {#start-delay}

**How to use Start Delay**

Use the `startDelay` to schedule a Workflow Execution at a specific one-time future point rather than on a recurring schedule.

You may specify the `startDelay` option on either the [`client.workflow.start()`](https://typescript.temporal.io/api/classes/client.WorkflowClient#start) or [`client.workflow.execute()`](https://typescript.temporal.io/api/classes/client.WorkflowClient#execute) methods of a Workflow Client.
For example:

```typescript
const handle = await client.workflow.start(someWorkflow, {
  // ...
  startDelay: '2 hours',
});
```

---

## Set up your local with the Typescript SDK