Note
Access to this page requires authorization. You can try signing in or changing directories.
Access to this page requires authorization. You can try changing directories.
This article guides you through upgrading your existing Durable Functions app to version 4 of the Node.js programming model. This article uses "TIP" banners to summarize the key steps needed to upgrade your app.
If you're interested in creating a brand new v4 app instead, you can follow the Visual Studio Code quickstarts for JavaScript and TypeScript.
Tip
Before following this guide, make sure you follow the general version 4 upgrade guide.
Prerequisites
Before following this guide, complete these steps:
- Install Node.js version 18.x+.
- Install TypeScript version 4.x+.
- Run your app on Azure Functions Runtime version 4.25+.
- Install Azure Functions Core Tools version 4.0.5382+.
- Review the general Azure Functions Node.js programming model v4 upgrade guide.
Upgrade the durable-functions npm package
Note
The programming model version shouldn't be confused with the durable-functions package version. durable-functions package version 3.x is required for the v4 programming model, while durable-functions version 2.x is required for the v3 programming model.
The v4 programming model is supported by the v3.x of the durable-functions npm package. In your programming model v3 app, you likely had durable-functions v2.x listed in your dependencies. Make sure to update to the v3.x of the durable-functions package.
Tip
Upgrade to v3.x of the durable-functions npm package with the following command:
npm install durable-functions
Register your Durable Functions triggers
In the v4 programming model, you no longer declare triggers and bindings in a separate function.json file. Instead, you can register your Durable Functions triggers and bindings directly in code, using the new APIs found in the app namespace on the root of the durable-functions package. The following code snippets show examples.
Migrating an orchestration
const df = require('durable-functions');
const activityName = 'helloActivity';
df.app.orchestration('durableOrchestrator', function* (context) {
const outputs = [];
outputs.push(yield context.df.callActivity(activityName, 'Tokyo'));
outputs.push(yield context.df.callActivity(activityName, 'Seattle'));
outputs.push(yield context.df.callActivity(activityName, 'Cairo'));
return outputs;
});
import * as df from 'durable-functions';
import { OrchestrationContext, OrchestrationHandler } from 'durable-functions';
const activityName = 'hello';
const durableHello1Orchestrator: OrchestrationHandler = function* (context: OrchestrationContext) {
const outputs = [];
outputs.push(yield context.df.callActivity(activityName, 'Tokyo'));
outputs.push(yield context.df.callActivity(activityName, 'Seattle'));
outputs.push(yield context.df.callActivity(activityName, 'Cairo'));
return outputs;
};
df.app.orchestration('durableOrchestrator', durableHello1Orchestrator);
Migrating an entity
const df = require('durable-functions');
df.app.entity('Counter', (context) => {
const currentValue = context.df.getState(() => 0);
switch (context.df.operationName) {
case 'add':
const amount = context.df.getInput();
context.df.setState(currentValue + amount);
break;
case 'reset':
context.df.setState(0);
break;
case 'get':
context.df.return(currentValue);
break;
}
});
import * as df from 'durable-functions';
import { EntityContext, EntityHandler } from 'durable-functions';
const counterEntity: EntityHandler<number> = (context: EntityContext<number>) => {
const currentValue: number = context.df.getState(() => 0);
switch (context.df.operationName) {
case 'add':
const amount: number = context.df.getInput();
context.df.setState(currentValue + amount);
break;
case 'reset':
context.df.setState(0);
break;
case 'get':
context.df.return(currentValue);
break;
}
};
df.app.entity('Counter', counterEntity);
Migrating an activity
Tip
Remove function.json files from your Durable Functions app. Instead, register your durable functions using the methods on the app namespace: df.app.orchestration(), df.app.entity(), and df.app.activity().
Register your Durable Client input binding
In the v4 model, registering secondary input bindings, like durable clients, is also done in code. Use the input.durableClient() method to register a durable client input binding to a function of your choice. In the function body, use getClient() to retrieve the client instance, as before. The following example uses an HTTP triggered function.
const { app } = require('@azure/functions');
const df = require('durable-functions');
app.http('durableHttpStart', {
route: 'orchestrators/{orchestratorName}',
extraInputs: [df.input.durableClient()],
handler: async (_request, context) => {
const client = df.getClient(context);
// Use client in function body
},
});
import { app, HttpHandler, HttpRequest, HttpResponse, InvocationContext } from '@azure/functions';
import * as df from 'durable-functions';
const durableHttpStart: HttpHandler = async (request: HttpRequest, context: InvocationContext): Promise<HttpResponse> => {
const client = df.getClient(context);
// Use client in function body
};
app.http('durableHttpStart', {
route: 'orchestrators/{orchestratorName}',
extraInputs: [df.input.durableClient()],
handler: durableHttpStart,
});
Tip
Use the input.durableClient() method to register a durable client extra input to your client function. Use getClient() as normal to retrieve a DurableClient instance.
Update your Durable Client API calls
Multiple APIs on the DurableClient class (renamed from DurableOrchestrationClient) were simplified in v3.x of durable-functions to make calling them easier and more streamlined. For many optional arguments to APIs, you now pass one options object, instead of multiple discrete optional arguments. The following example shows these changes:
The following table lists all the changes:
| V3 model (durable-functions v2.x) | V4 model (durable-functions v3.x) |
|---|---|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Tip
Make sure to update your DurableClient API calls from discrete optional arguments to options objects, where applicable. See the previous list for all APIs affected.
Update calls to callHttp API
In v3.x of durable-functions, the callHttp() API for DurableOrchestrationContext was updated. The following changes were made:
- Accept one options object for all arguments, instead of multiple optional arguments, to be more similar to frameworks such as Express.
- Rename
uriargument tourl - Rename
contentargument tobody - Deprecate
asynchronousPatternEnabledflag in favor ofenablePolling.
If your orchestrations used the callHttp API, make sure to update the API calls to conform to the preceding changes. The following example shows the updated syntax:
Tip
To use the new options object, update your API calls to callHttp inside your orchestrations.
Use new types
The durable-functions package now exposes new types that weren't previously exported. These types allow you to more strongly type your functions and provide stronger type safety for your orchestrations, entities, and activities. They also improve IntelliSense for authoring these functions.
The following list includes some of the new exported types:
OrchestrationHandler, andOrchestrationContextfor orchestrationsEntityHandlerandEntityContextfor entitiesActivityHandlerfor activitiesDurableClientclass for client functions
Tip
Strongly type your functions by using new types exported from the durable-functions package.
Troubleshooting
If you see the following error when running your orchestration code, make sure you're running on at least v4.25 of the Azure Functions Runtime or at least v4.0.5382 of Azure Functions Core Tools if running locally.
Exception: The orchestrator can not execute without an OrchestratorStarted event.
Stack: TypeError: The orchestrator can not execute without an OrchestratorStarted event.
If that doesn't work, or if you encounter any other issues, you can always file a bug report in our GitHub repo.