Skip to main content

Workers in Node

What is a Worker?#

A Worker is an object that connects to the Temporal Server, polls Task Queues for Commands, and executes Workflows and Activities in response to those Commands.

  • Workers are run on user-controlled hosts, an important security feature which means Temporal Server (or Temporal Cloud) never executes your Workflow or Activity code, and that Workers can have different hardware (e.g. custom GPUs for Machine Learning) than the rest of the system.
  • Workers automatically discover Workflows and Activities based on workDir or specified paths.
  • You can use the @temporalio/worker package's Worker class to create and run as many Workers as your use case demands, across any number of hosts.
  • Workers poll Task Queues for Tasks, execute chunks of code in response to those Tasks, and then communicate the results back to the Temporal Server.
  • You can check the status of Workers and the Task Queues they poll with Temporal Web.

How to develop a Worker#

First you create a Worker with Worker.create(), then call worker.run() on it.

Below is an example of starting a Worker that polls the Task Queue named tutorial.

import { Worker } from '@temporalio/worker';
run().catch((err) => console.log(err));
async function run() {  // Step 1: Automatically locate and register Activities and Workflows relative to __dirname.  const worker = await Worker.create({    workDir: __dirname,    taskQueue: 'tutorial',  });  // // Worker connects to localhost by default and uses console.error for logging.  // // Customize the Worker by passing more options to create():  // // https://nodejs.temporal.io/api/classes/worker.Worker    // // If you need to configure server connection parameters, see the mTLS example:  // // https://github.com/temporalio/samples-node/tree/main/hello-world-mtls      // Step 2: Start accepting tasks on the `tutorial` queue  await worker.run();    // You may create multiple Workers in a single process in order to poll on multiple task queues.}

taskQueue is the only required option, and you can offer one of workDir or activities, nodeModulesPath, and workflowsPath.

Workflow and Activity registration#

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.

When a workDir is specified (usually __dirname, which is the system global in Node.js for the path to the directory of the currently executing file), the Node SDK will automatically infer:

  • activities: Activities exported from workDir + '/activities.ts' or workDir + '/activities/index.ts' (or .js when using JavaScript).
  • nodeModulesPath: Path for webpack to look up modules in for bundling the Workflow code. Automatically discovered if workDir is provided. Defaults to ${workDir}/../node_modules
  • workflowsPath: Workflows exported from workDir + '/workflows/index.js'

If you have an unusual folder structure setup, you can override activities, nodeModulesPath, and workflowsPath. If you specify all three, then workDir is not needed.

Additional Worker Options#

This is a selected subset of options you are likely to use. More advanced options, particularly for performance tuning, are available in the API reference.

OptionsDescription
activitiesMapping of activity name to implementation. Automatically discovered from ${workDir}/activities if workDir is provided.
nodeModulesPathPath for webpack to look up modules in for bundling the Workflow code. Automatically discovered if workDir is provided. Defaults to ${workDir}/../node_modules
workflowsPathPath to look up workflows in. Automatically discovered if workDir is provided. Defaults to ${workDir}/workflows
dataConverterplaceholder for future DataConverter feature (pending feature)
dependenciesAllows injection of external dependencies (Advanced feature: see External Dependencies)
interceptorsA mapping of interceptor type to a list of factories or module paths (Advanced feature: see Interceptors)

For example, if you are working in monorepo style and want node_modules at your project root, with all Temporal code inside a /temporal/src folder, you can force nodeModulesPath:

// this file is /temporal/src/worker.ts but node modules are at /node_modules// activities are at /temporal/src/activities.ts - as expected by workDir, no override needed// workflows are at /temporal/src/workflow/index.ts - as expected by workDir, no override needed
const worker = await Worker.create({  workDir: __dirname,  nodeModulesPath: path.join(__dirname, "/../../node_modules"),  taskQueue: "tutorial",});
The Worker package embeds the Temporal Rust Core SDK, it comes pre-compiled for most installations.

We've provided pre-compiled binaries for:

  • Mac with an Intel chip: x86_64-apple-darwin
  • Mac with an Apple chip: aarch64-apple-darwin
  • Linux with x86_64 architecture: x86_64-unknown-linux-gnu
  • Windows with x86_64 architecture: x86_64-pc-windows-gnu (Windows is not yet supported but it is a priority for us).

If you need to compile the Worker yourself, set up the Rust toolchain by following the instructions here.

How to shut down a Worker and track its state#

You can programmatically shut down a worker with worker.shutdown(). Shut downs should be rare and often done manually in development (with SIGINT aka ^C) but you may do it in integration tests or in automating a fleet of workers.

At any point in time you can query Worker state with worker.getState(). A Worker is in one of 7 states at any given point:

  • INITIALIZED - The initial state of the Worker after calling Worker.create and successful connection to the server
  • RUNNING - worker.run was called, polling task queues
  • FAILED - Worker encountered an unrecoverable error, worker.run should reject with the error
  • The last 4 states are related to the Worker shutdown process:
    • STOPPING - Worker.shutdown was called or received shutdown signal, worker will forcefully shutdown in shutdownGraceTime
    • DRAINING - Core has indicated that shutdown is complete and all Workflow tasks have been drained, waiting for activities and cached workflows eviction
    • DRAINED - All activities and workflows have completed, ready to shutdown
    • STOPPED - Shutdown complete, worker.run resolves

If you need even more visibility into internal worker state, see the API reference for more.

Worker Security and Networking#

The Node SDK usually handles all of the communication between the Worker and the Temporal Server behind the scenes - no port configuration is required for development usecases.

In production settings, Temporal supports mTLS encryption, required by Temporal Cloud. To configure this, this SDK exposes the Rust Core SDK as Core, which you can configure before you run workflow.create:

hello-world-mtls/src/worker.ts

import { Worker, Core } from '@temporalio/worker';import { getEnv, Env } from './mtls-env';
/** * Run a Worker with an mTLS connection, configuration is provided via environment variables. * Note that serverNameOverride and serverRootCACertificate are optional. */async function run({  address,  namespace,  clientCertPath,  clientKeyPath,  serverNameOverride,  serverRootCACertificatePath,  taskQueue,}: Env) {  let serverRootCACertificate: Buffer | undefined = undefined;  if (serverRootCACertificatePath) {    serverRootCACertificate = fs.readFileSync(serverRootCACertificatePath);  }
  await Core.install({    serverOptions: {      address,      namespace,      tls: {        serverNameOverride,        serverRootCACertificate,        // See docs for other TLS options        clientCertPair: {          crt: fs.readFileSync(clientCertPath),          key: fs.readFileSync(clientKeyPath),        },      },    },  });
  const worker = await Worker.create({    workDir: __dirname,    taskQueue,  });  console.log('Worker connection succesfully established');  // Start accepting tasks on the `tutorial` queue  await worker.run();}
run(getEnv()).catch((err) => {  console.error(err);  process.exit(1);});