Skip to main content

External Dependencies

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 Node SDK, Workflow code runs in an isolated execution environment limited to functionality provided by the SDK.

External Dependencies is an isolation breaking mechanism that allows injecting replay-aware functions from the main NodeJS environment into a Workflow isolate. They are typically used in order to inject custom instrumentation (e.g. logger) functions into the isolate.

Injection configuration#

The following configuration options are for controlling how an injected function is executed.

callDuringReplay#

A boolean controling whether or not the injected function will be called during Workflow replay - defaults to false.

ApplyMode#

The different modes for an injected function to be applied to the isolate are documented in the API reference.

  • ASYNC
  • ASYNC_IGNORED
  • SYNC
  • SYNC_IGNORED
  • SYNC_PROMISE
warning

Only IGNORED apply modes are safe to use since they cannot break determinism.
Use other modes only if you're certain you know what you're doing.

Function arguments and return value#

Functions configured to use ASYNC* apply modes always copy their arguments and return value, which limits them to primitive types such as number, string, array and object.

Function configured to use SYNC* apply modes always copy their return value and can control whether to copy their arguments or pass them in using isolated-vm References.

Example#

src/interfaces/dependencies.ts#

Define the interface for your external dependencies

export interface LoggerDependencies extends ExternalDependencies {
logger: {
info(message: string): void;
};
}

src/workflows/logger-example.ts#

Call an external dependency function from a Workflow

import { Context } from '@temporalio/workflow';
import { LoggerDependencies } from '../interfaces/dependencies';
const { logger } = Context.dependencies<LoggerDependencies>();
// logger cannot be used at the top level as exernal dependencies are not injected yet.
// Wait for Workflow to start (main called) before calling injected dependencies.
export async function main(): Promise<void> {
logger.info('Workflow execution started');
}

src/worker/index.ts#

Inject a function as a Workflow external dependency

import { Worker, ApplyMode } from '@temporalio/worker';
import { LoggerDependencies } from '../interfaces/dependencies';
async function main() {
const worker = await Worker.create<{ dependencies: LoggerDependencies }>({
...defaultOptions(), // omitted from this sample for brevity
workDir: __dirname,
taskQueue: 'sample',
dependencies: {
logger: {
info: {
fn(workflowInfo, message) {
console.log('workflow: ', workflowInfo.runId, 'message: ', message);
},
applyMode: ApplyMode.ASYNC_IGNORED, // See docs for other modes
callDuringReplay: false, // The default
},
},
},
});
await worker.run();
console.log('Worker gracefully shutdown');
}
main().then(
() => void process.exit(0),
(err) => {
console.error(err);
process.exit(1);
}
);

References#

Get notified of updates