TypeScript SDK Developer's Guide - Features - Temporal Documentation
TypeScript SDK Developer's Guide - Features - Temporal Documentation
https://docs.temporal.io/dev-guide/typescript/features#asynchronous-design-patterns 1/41
02/08/2023, 06:53 TypeScript SDK developer's guide - Features | Temporal Documentation
TypeScript JavaScript
interface JoinInput {
userId: string;
groupId: string;
}
TypeScript JavaScript
When a Signal is sent successfully from the Temporal Client, the WorkflowExecutionSignaled
Event appears in the Event History of the Workflow that receives the Signal.
WorkflowHandle.signal
https://docs.temporal.io/dev-guide/typescript/features#asynchronous-design-patterns 2/41
02/08/2023, 06:53 TypeScript SDK developer's guide - Features | Temporal Documentation
A Workflow can send a Signal to another Workflow, in which case it's called an External Signal.
When an External Signal is sent:
A SignalExternalWorkflowExecutionInitiated Event appears in the sender's Event History.
A WorkflowExecutionSignaled Event appears in the recipient's Event History.
getExternalWorkflowHandle
How to Signal-With-Start
Signal-With-Start is used from the Client. It takes a Workflow Id, Workflow arguments, a Signal
name, and Signal arguments.
If there's a Workflow running with the given Workflow Id, it will be signaled. If there isn't, a new
Workflow will be started and immediately signaled.
WorkflowClient.signalWithStart
https://docs.temporal.io/dev-guide/typescript/features#asynchronous-design-patterns 3/41
02/08/2023, 06:53 TypeScript SDK developer's guide - Features | Temporal Documentation
await client.signalWithStart(yourWorkflow, {
workflowId: 'workflow-id-123',
args: [{ foo: 1 }],
signal: joinSignal,
signalArgs: [{ userId: 'user-1', groupId: 'group-1' }],
});
https://docs.temporal.io/dev-guide/typescript/features#asynchronous-design-patterns 4/41
02/08/2023, 06:53 TypeScript SDK developer's guide - Features | Temporal Documentation
Don’t include any logic that causes Command generation within a Query handler (such as
executing Activities). Including such logic causes unexpected behavior.
Use handleQuery to handle Queries inside a Workflow.
You make a Query with handle.query(query, ...args) . A Query needs a return value, but
can also take arguments.
state/src/workflows.ts
TypeScript JavaScript
https://docs.temporal.io/dev-guide/typescript/features#asynchronous-design-patterns 5/41
02/08/2023, 06:53 TypeScript SDK developer's guide - Features | Temporal Documentation
Only Signal Handlers can mutate state, and only Query Handlers can return values.
Define Signals and Queries statically
If you know the name of your Signals and Queries upfront, we recommend declaring them
outside the Workflow Definition.
signals-queries/src/workflows.ts
TypeScript JavaScript
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
https://docs.temporal.io/dev-guide/typescript/features#asynchronous-design-patterns 6/41
02/08/2023, 06:53 TypeScript SDK developer's guide - Features | Temporal Documentation
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.
TypeScript JavaScript
Workflow timeouts
Each Workflow timeout controls the maximum duration of a different aspect of a Workflow
Execution.
Workflow timeouts are set when starting the Workflow Execution .
Workflow Execution Timeout - restricts the maximum amount of time that a single
Workflow Execution can be executed.
Workflow Run Timeout : restricts the maximum amount of time that a single Workflow
Run can last.
Workflow Task Timeout : restricts the maximum amount of time that a Worker can
execute a Workflow Task.
Create an instance of WorkflowOptions from the Client and set your Workflow Timeout.
Available timeouts are:
workflowExecutionTimeout
workflowRunTimeout
workflowTaskTimeout
snippets/src/client.ts
TypeScript JavaScript
await client.workflow.start(example, {
taskQueue,
workflowId,
workflowExecutionTimeout: '1 day',
});
snippets/src/client.ts
TypeScript JavaScript
await client.workflow.start(example, {
taskQueue,
https://docs.temporal.io/dev-guide/typescript/features#asynchronous-design-patterns 8/41
02/08/2023, 06:53 TypeScript SDK developer's guide - Features | Temporal Documentation
workflowId,
workflowRunTimeout: '1 minute',
});
snippets/src/client.ts
TypeScript JavaScript
await client.workflow.start(example, {
taskQueue,
workflowId,
workflowTaskTimeout: '1 minute',
});
Workflow retries
A Retry Policy can work in cooperation with the timeouts to provide fine controls to optimize
the execution experience.
Use a Retry Policy 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.
Create an instance of the Retry Policy, known as retry in TypeScript, from the
WorkflowOptions of the Client interface.
snippets/src/client.ts
TypeScript JavaScript
https://docs.temporal.io/dev-guide/typescript/features#asynchronous-design-patterns 9/41
02/08/2023, 06:53 TypeScript SDK developer's guide - Features | Temporal Documentation
startToCloseTimeout
scheduleToStartTimeout
retry: {
// default retry policy if not specified
initialInterval: '1s',
backoffCoefficient: 2,
maximumAttempts: Infinity,
maximumInterval: 100 * initialInterval,
nonRetryableErrorTypes: [],
},
});
https://docs.temporal.io/dev-guide/typescript/features#asynchronous-design-patterns 10/41
02/08/2023, 06:53 TypeScript SDK developer's guide - Features | Temporal Documentation
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 if a custom one is
not provided.
To set Activity Retry Policies in TypeScript, pass ActivityOptions.retry to
proxyActivities .
Long-running Activities should Heartbeat their progress back to the Workflow for earlier
detection of stalled Activities (with Heartbeat Timeout ) and resuming stalled Activities from
checkpoints (with Heartbeat details).
To set Activity Heartbeat, use Context.current().heartbeat() in your Activity
implementation, and set heartbeatTimeout in your Workflow.
TypeScript JavaScript
// activity implementation
export async function example(sleepIntervalMs = 1000): Promise<void> {
for (let progress = 1; progress <= 1000; ++progress) {
await Context.current().sleep(sleepIntervalMs);
// record activity heartbeat
Context.current().heartbeat();
}
}
// ...
In the previous example, setting the Heartbeat informs the Temporal Server of the Activity's
progress at regular intervals. If the Activity stalls or the Activity Worker becomes unavailable,
the absence of Heartbeats prompts the Temporal Server to retry the Activity immediately,
without waiting for startToCloseTimeout to complete.
You can also add heartbeatDetails as a checkpoint to collect data about failures during the
execution, and use it to resume the Activity from that point.
The following example extends the previous sample to include a heartbeatDetails
checkpoint.
TypeScript JavaScript
https://docs.temporal.io/dev-guide/typescript/features#asynchronous-design-patterns 12/41
02/08/2023, 06:53 TypeScript SDK developer's guide - Features | Temporal Documentation
In this example, when the heartbeatTimeout is reached and the Activity is retried, the
Activity Worker picks up the execution from where the previous attempt left off.
How to set a Heartbeat Timeout
Local Activities
To call Local Activities in TypeScript, use proxyLocalActivities .
TypeScript JavaScript
https://docs.temporal.io/dev-guide/typescript/features#asynchronous-design-patterns 14/41
02/08/2023, 06:53 TypeScript SDK developer's guide - Features | Temporal Documentation
// ...
}
Local Activities must be registered with the Worker the same way non-local Activities are.
https://docs.temporal.io/dev-guide/typescript/features#asynchronous-design-patterns 15/41
02/08/2023, 06:53 TypeScript SDK developer's guide - Features | Temporal Documentation
To be sure that the Child Workflow Execution has started, first call the Child Workflow
Execution method on the instance of Child Workflow future, which returns a different future.
Then get the value of an object that acts as a proxy for a result that is initially unknown, which
is what waits until the Child Workflow Execution has spawned.
To start a Child Workflow Execution and return a handle to it, use startChild.
TypeScript JavaScript
To start a Child Workflow Execution and await its completion, use executeChild.
By default, a child is scheduled on the same Task Queue as the parent.
child-workflows/src/workflows.ts
TypeScript JavaScript
Promise<string> {
const responseArray = await Promise.all(
names.map((name) =>
executeChild(childWorkflow, {
args: [name],
// workflowId, // add business-meaningful workflow id here
// // regular workflow options apply here, with two additions
(defaults shown):
// cancellationType:
ChildWorkflowCancellationType.WAIT_CANCELLATION_COMPLETED,
// parentClosePolicy:
ParentClosePolicy.PARENT_CLOSE_POLICY_TERMINATE
})
),
);
return responseArray.join('\n');
}
If the Child Workflow options aren't explicitly set, they inherit their values from the Parent
Workflow options. Two advanced options are unique to Child Workflows:
cancellationType: Controls when to throw the CanceledFailure exception when a Child
Workflow is canceled.
parentClosePolicy : Explained in the next section.
If you need to cancel a Child Workflow Execution, use cancellation scopes. A Child Workflow
Execution is automatically cancelled when its containing scope is cancelled.
https://docs.temporal.io/dev-guide/typescript/features#asynchronous-design-patterns 17/41
02/08/2023, 06:53 TypeScript SDK developer's guide - Features | Temporal Documentation
A Parent Close Policy determines what happens to a Child Workflow Execution if its Parent
changes to a Closed status (Completed, Failed, or Timed Out).
The default Parent Close Policy option is set to terminate the Child Workflow Execution.
To specify how a Child Workflow reacts to a Parent Workflow reaching a Closed state, use the
parentClosePolicy option.
child-workflows/src/workflows.ts
TypeScript JavaScript
How to Continue-As-New
Continue-As-New enables a Workflow Execution to close successfully and create a new
Workflow Execution in a single atomic operation if the number of Events in the Event History is
becoming too large. The Workflow Execution spawned from the use of Continue-As-New has
https://docs.temporal.io/dev-guide/typescript/features#asynchronous-design-patterns 18/41
02/08/2023, 06:53 TypeScript SDK developer's guide - Features | Temporal Documentation
the same Workflow Id, a new Run Id, and a fresh Event History and is passed all the appropriate
parameters.
To cause a Workflow Execution to Continue-As-New , the Workflow function should return
the result of the continueAsNew .
continue-as-new/src/workflows.ts
TypeScript JavaScript
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.
interface Input {
/* Define your Workflow input type here */
}
interface Update {
/* Define your Workflow update type here */
}
const MAX_ITERATIONS = 1;
try {
const pendingUpdates = Array<Update>();
setHandler(updateSignal, (updateCommand) => {
pendingUpdates.push(updateCommand);
});
if (isNew) {
await setup(input);
}
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);
}
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.
How to Backfill a Scheduled Workflow
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.
How to Delete a Scheduled Workflow
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.
How to Describe a Scheduled Workflow
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.
How to List a Scheduled Workflow
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.
How to Pause a Scheduled Workflow
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.
How to Trigger a Scheduled Workflow
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.
How to Update a Scheduled Workflow
https://docs.temporal.io/dev-guide/typescript/features#asynchronous-design-patterns 21/41
02/08/2023, 06:53 TypeScript SDK developer's guide - Features | Temporal Documentation
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.
What is a Timer?
A Workflow can set a durable timer for a fixed time period. In some SDKs, the function is called
sleep() , and in others, it's called timer() .
A Workflow can sleep for months. Timers are persisted, so even if your Worker or Temporal
Cluster is down when the time period completes, as soon as your Worker and Cluster are back
up, the sleep() call will resolve and your code will continue executing.
Sleeping is a resource-light operation: it does not tie up the process, and you can run millions
of Timers off a single Worker.
Racing Signals
Updatable Timer
You can set each Workflow to repeat on a schedule with the cronSchedule option:
const handle = await client.start(scheduledWorkflow, {
// ...
cronSchedule: '* * * * *', // start every minute
});
On Temporal Cloud, use the Temporal Cloud UI to create and manage a Namespace from the
UI, or tcld commands to manage Namespaces from the command-line interface.
On self-hosted Temporal Cluster, you can register and manage your Namespaces using tctl
(recommended) or programmatically using APIs. Note that these APIs and tctl commands will
not work with Temporal Cloud.
Use a custom Authorizer on your Frontend Service in the Temporal Cluster to set restrictions
on who can create, update, or deprecate Namespaces.
You must register a Namespace with the Temporal Cluster before setting it in the Temporal
Client.
How to register Namespaces
https://docs.temporal.io/dev-guide/typescript/features#asynchronous-design-patterns 23/41
02/08/2023, 06:53 TypeScript SDK developer's guide - Features | Temporal Documentation
Use a custom Authorizer on your Frontend Service in the Temporal Cluster to set restrictions
on who can create, update, or deprecate Namespaces.
How to 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 or tcld commands to manage Namespaces.
On self-hosted Temporal Cluster, you can manage your registered Namespaces using tctl
(recommended) or programmatically using APIs. Note that these APIs and tctl commands will
not work with Temporal Cloud.
Use a custom Authorizer on your Frontend Service in the Temporal Cluster to set restrictions
on who can create, update, or deprecate Namespaces.
You must register a Namespace with the Temporal Cluster before setting it in the Temporal
Client.
interface DataConverter {
payloadConverterPath?: string;
payloadCodecs?: PayloadCodec[];
}
Payload Converter
interface PayloadConverter {
/**
* Converts a value to a {@link Payload}.
* @param value The value to convert. Example values include the
Workflow args sent by the client and the values returned by a Workflow
or Activity.
*/
toPayload<T>(value: T): Payload;
/**
* Converts a {@link Payload} back to a value.
*/
fromPayload<T>(payload: Payload): T;
}
Custom implementation
common/src/converter/protobuf-payload-converters.ts
The sample project samples-typescript/ejson creates an EJSON custom PayloadConverter .
It implements PayloadConverterWithEncoding instead of PayloadConverter so that it
could be used with CompositePayloadConverter:
ejson/src/ejson-payload-converter.ts
TypeScript JavaScript
import {
EncodingType,
METADATA_ENCODING_KEY,
Payload,
PayloadConverterError,
PayloadConverterWithEncoding,
} from '@temporalio/common';
import { decode, encode } from '@temporalio/common/lib/encoding';
import EJSON from 'ejson';
/**
* Converts between values and [EJSON]
(https://docs.meteor.com/api/ejson.html) Payloads.
*/
export class EjsonPayloadConverter implements
PayloadConverterWithEncoding {
// Use 'json/plain' so that Payloads are displayed in the UI
public encodingType = 'json/plain' as EncodingType;
https://docs.temporal.io/dev-guide/typescript/features#asynchronous-design-patterns 26/41
02/08/2023, 06:53 TypeScript SDK developer's guide - Features | Temporal Documentation
e as Error,
);
}
return {
metadata: {
[METADATA_ENCODING_KEY]: encode('json/plain'),
// Include an additional metadata field to indicate that this
is an EJSON payload
format: encode('extended'),
},
data: encode(ejson),
};
}
import {
CompositePayloadConverter,
UndefinedPayloadConverter,
} from '@temporalio/common';
import { EjsonPayloadConverter } from './ejson-payload-converter';
https://docs.temporal.io/dev-guide/typescript/features#asynchronous-design-patterns 27/41
02/08/2023, 06:53 TypeScript SDK developer's guide - Features | Temporal Documentation
new EjsonPayloadConverter(),
);
ejson/src/client.ts
TypeScript JavaScript
Protobufs
Patch json-module.js :
https://docs.temporal.io/dev-guide/typescript/features#asynchronous-design-patterns 29/41
02/08/2023, 06:53 TypeScript SDK developer's guide - Features | Temporal Documentation
protobufs/protos/root.js
const { patchProtobufRoot } =
require('@temporalio/common/lib/protobufs');
const unpatchedRoot = require('./json-module');
module.exports = patchProtobufRoot(unpatchedRoot);
Create a DefaultPayloadConverterWithProtobufs :
protobufs/src/payload-converter.ts
TypeScript JavaScript
Alternatively, we can use Protobuf Payload Converters directly, or with other converters. If we
know that we only use Protobuf objects, and we want them binary encoded (which saves
space over proto3 JSON, but can't be viewed in the Web UI), we could do the following:
TypeScript JavaScript
https://docs.temporal.io/dev-guide/typescript/features#asynchronous-design-patterns 30/41
02/08/2023, 06:53 TypeScript SDK developer's guide - Features | Temporal Documentation
import {
BinaryPayloadConverter,
CompositePayloadConverter,
JsonPayloadConverter,
UndefinedPayloadConverter,
} from '@temporalio/common';
import { ProtobufBinaryPayloadConverter } from
'@temporalio/common/lib/protobufs';
import root from '../protos/root';
WorkerOptions.dataConverter
Provide it to the Client:
https://docs.temporal.io/dev-guide/typescript/features#asynchronous-design-patterns 31/41
02/08/2023, 06:53 TypeScript SDK developer's guide - Features | Temporal Documentation
protobufs/src/client.ts
TypeScript JavaScript
});
protobufs/src/activities.ts
TypeScript JavaScript
Payload Codec
interface PayloadCodec {
/**
* Encode an array of {@link Payload}s for sending over the wire.
* @param payloads May have length 0.
*/
encode(payloads: Payload[]): Promise<Payload[]>;
/**
* Decode an array of {@link Payload}s received from the wire.
https://docs.temporal.io/dev-guide/typescript/features#asynchronous-design-patterns 33/41
02/08/2023, 06:53 TypeScript SDK developer's guide - Features | Temporal Documentation
*/
decode(payloads: Payload[]): Promise<Payload[]>;
}
Encryption
Background: Encryption
The following is an example class that implements the PayloadCodec interface:
encryption/src/encryption-codec.ts
TypeScript JavaScript
import {
METADATA_ENCODING_KEY,
Payload,
PayloadCodec,
ValueError,
} from '@temporalio/common';
import { decode, encode } from '@temporalio/common/lib/encoding';
import { temporal } from '@temporalio/proto';
import { webcrypto as crypto } from 'node:crypto';
import { decrypt, encrypt } from './crypto';
https://docs.temporal.io/dev-guide/typescript/features#asynchronous-design-patterns 34/41
02/08/2023, 06:53 TypeScript SDK developer's guide - Features | Temporal Documentation
[METADATA_ENCODING_KEY]: encode(ENCODING),
[METADATA_ENCRYPTION_KEY_ID]: encode(this.defaultKeyId),
},
// Encrypt entire payload, preserving metadata
data: await encrypt(
temporal.api.common.v1.Payload.encode(payload).finish(),
this.keys.get(this.defaultKeyId)!, // eslint-disable-line
@typescript-eslint/no-non-null-assertion
),
})),
);
}
const keyIdBytes =
payload.metadata[METADATA_ENCRYPTION_KEY_ID];
if (!keyIdBytes) {
throw new ValueError(
'Unable to decrypt Payload without encryption key id',
);
}
https://docs.temporal.io/dev-guide/typescript/features#asynchronous-design-patterns 35/41
02/08/2023, 06:53 TypeScript SDK developer's guide - Features | Temporal Documentation
}
}
return cryptoKey;
}
The encryption and decryption code is in src/crypto.ts. Because encryption is CPU intensive,
and doing AES with the crypto module built into Node.js blocks the main thread, we use
@ronomon/crypto-async , which uses the Node.js thread pool.
https://docs.temporal.io/dev-guide/typescript/features#asynchronous-design-patterns 36/41
02/08/2023, 06:53 TypeScript SDK developer's guide - Features | Temporal Documentation
encryption/src/worker.ts
TypeScript JavaScript
When the Client sends 'Alice: Private message for Bob.' to the Workflow, it gets
encrypted on the Client and decrypted in the Worker. The Workflow receives the decrypted
message and appends another message. When it returns that longer string, the string gets
encrypted by the Worker and decrypted by the Client.
encryption/src/workflows.ts
TypeScript JavaScript
https://docs.temporal.io/dev-guide/typescript/features#asynchronous-design-patterns 37/41
02/08/2023, 06:53 TypeScript SDK developer's guide - Features | Temporal Documentation
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
import {
ActivityInput,
Next,
WorkflowOutboundCallsInterceptor,
} from '@temporalio/workflow';
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,
});
https://docs.temporal.io/dev-guide/typescript/features#asynchronous-design-patterns 38/41
02/08/2023, 06:53 TypeScript SDK developer's guide - Features | Temporal Documentation
}
}
}
Authorization
TypeScript JavaScript
import {
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;
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.
https://docs.temporal.io/dev-guide/typescript/features#asynchronous-design-patterns 39/41
02/08/2023, 06:53 TypeScript SDK developer's guide - Features | Temporal Documentation
Interceptor registration
TypeScript JavaScript
src/worker/index.ts
TypeScript JavaScript
},
});
https://docs.temporal.io/dev-guide/typescript/features#asynchronous-design-patterns 41/41