Workflow execution with the TypeScript SDK
How to start a 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 or External Workflow APIs.
See the Customize 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) in the Workflow Execution Event History. The Temporal Service then creates the first Workflow Task, resulting in the first 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.
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.
How to set a Workflow's Task Queue
In most SDKs, the only Workflow Option that must be set is the name of the 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
workflowsPathpointing to yourworkflows.tsfile 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.
- Specify
import { Worker } from '@temporalio/worker';
import * as activities from './activities';
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.
import { Client, Connection } from '@temporalio/client';
// 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.
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
Although it is not required, we recommend providing your own Workflow Idthat 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.
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
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:
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.
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().
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 that reflects the
reason. For that reason, it is recommended to catch that error.
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,
});
}
}