Durable Task Scheduler — JavaScript
Learn More
Durable Functions Setup
Required npm Packages
{
"dependencies": {
"@azure/functions": "^4.0.0",
"durable-functions": "^3.0.0"
}
}đź’ˇ Finding latest versions: Run
npm view durable-functions versionor check npmjs.com/package/durable-functions for the latest stable release.
host.json
{
"version": "2.0",
"extensions": {
"durableTask": {
"hubName": "default",
"storageProvider": {
"type": "durabletask-scheduler",
"connectionStringName": "DURABLE_TASK_SCHEDULER_CONNECTION_STRING"
}
}
},
"extensionBundle": {
"id": "Microsoft.Azure.Functions.ExtensionBundle",
"version": "[4.*, 5.0.0)"
}
}local.settings.json
{
"IsEncrypted": false,
"Values": {
"FUNCTIONS_WORKER_RUNTIME": "node",
"AzureWebJobsStorage": "UseDevelopmentStorage=true",
"DURABLE_TASK_SCHEDULER_CONNECTION_STRING": "Endpoint=http://localhost:8080;TaskHub=default;Authentication=None"
}
}Minimal Example
const { app } = require("@azure/functions");
const df = require("durable-functions");
// Activity
df.app.activity("sayHello", {
handler: (city) => `Hello ${city}!`,
});
// Orchestrator
df.app.orchestration("myOrchestration", function* (context) {
const result1 = yield context.df.callActivity("sayHello", "Tokyo");
const result2 = yield context.df.callActivity("sayHello", "Seattle");
return `${result1}, ${result2}`;
});
// HTTP Starter
app.http("HttpStart", {
route: "orchestrators/{orchestrationName}",
methods: ["POST"],
authLevel: "function",
extraInputs: [df.input.durableClient()],
handler: async (request, context) => {
const client = df.getClient(context);
const instanceId = await client.startNew(request.params.orchestrationName);
return client.createCheckStatusResponse(request, instanceId);
},
});Workflow Patterns
Fan-Out/Fan-In
df.app.orchestration("fanOutFanIn", function* (context) {
const cities = ["Tokyo", "Seattle", "London", "Paris", "Berlin"];
// Fan-out: schedule all activities in parallel
const tasks = cities.map((city) => context.df.callActivity("sayHello", city));
// Fan-in: wait for all to complete
const results = yield context.df.Task.all(tasks);
return results;
});Human Interaction
df.app.orchestration("approvalWorkflow", function* (context) {
yield context.df.callActivity("sendApprovalRequest", context.df.getInput());
// Wait for approval event with timeout
const expiration = new Date(context.df.currentUtcDateTime);
expiration.setDate(expiration.getDate() + 3);
const approvalTask = context.df.waitForExternalEvent("ApprovalEvent");
const timeoutTask = context.df.createTimer(expiration);
const winner = yield context.df.Task.any([approvalTask, timeoutTask]);
if (winner === approvalTask) {
return approvalTask.result ? "Approved" : "Rejected";
}
return "Timed out";
});Orchestration Determinism
| ❌ NEVER | ✅ ALWAYS USE |
|---|---|
new Date() | context.df.currentUtcDateTime |
Math.random() | Pass random values from activities |
setTimeout() | context.df.createTimer() |
| Direct I/O, HTTP, database | context.df.callActivity() |
Replay-Safe Logging
df.app.orchestration("myOrchestration", function* (context) {
if (!context.df.isReplaying) {
console.log("Started"); // Only logs once, not on replay
}
const result = yield context.df.callActivity("myActivity", "input");
return result;
});Error Handling & Retry
df.app.orchestration("workflowWithRetry", function* (context) {
const retryOptions = new df.RetryOptions(5000, 3); // firstRetryInterval, maxAttempts
retryOptions.backoffCoefficient = 2.0;
retryOptions.maxRetryIntervalInMilliseconds = 60000;
try {
const result = yield context.df.callActivityWithRetry(
"unreliableService",
retryOptions,
context.df.getInput()
);
return result;
} catch (ex) {
context.df.setCustomStatus({ error: ex.message });
yield context.df.callActivity("compensationActivity", context.df.getInput());
return "Compensated";
}
});Durable Task SDK (Non-Functions)
For applications running outside Azure Functions (containers, VMs, Azure Container Apps, Azure Kubernetes Service):
const { createAzureManagedWorkerBuilder, createAzureManagedClient } = require("@microsoft/durabletask-js-azuremanaged");
const connectionString = "Endpoint=http://localhost:8080;Authentication=None;TaskHub=default";
// Activity
const sayHello = async (_ctx, name) => `Hello ${name}!`;
// Orchestrator
const myOrchestration = async function* (ctx, name) {
const result = yield ctx.callActivity(sayHello, name);
return result;
};
async function main() {
// Worker
const worker = createAzureManagedWorkerBuilder(connectionString)
.addOrchestrator(myOrchestration)
.addActivity(sayHello)
.build();
await worker.start();
// Client
const client = createAzureManagedClient(connectionString);
const instanceId = await client.scheduleNewOrchestration("myOrchestration", "World");
const state = await client.waitForOrchestrationCompletion(instanceId, true, 30);
console.log("Output:", state.serializedOutput);
await client.stop();
await worker.stop();
}
main().catch(console.error);