Azure Functions Durable Functions Basics
A standard Azure Function runs, completes its task, and disappears. It has no memory of what it did before. This works well for short tasks but fails for long-running workflows — like processing an order that requires approvals, calling multiple APIs in sequence, or coordinating ten parallel tasks.
Durable Functions is an extension of Azure Functions that adds state, sequencing, and coordination to functions. A Durable Function can pause, wait for an event, resume, and remember everything it did — even if it ran for hours or days.
Durable Functions vs Standard Functions
┌──────────────────────────────────────────────────────────────┐ │ STANDARD vs DURABLE FUNCTIONS │ │ │ │ Standard Function: │ │ Trigger → Run → Complete → Forget │ │ Max execution: 5–10 minutes (Consumption plan) │ │ No memory between calls │ │ │ │ Durable Function: │ │ Trigger → Run → Pause → Wait → Resume → Continue → Complete │ │ Duration: Minutes, hours, days, or longer │ │ Full state preserved across pauses │ └──────────────────────────────────────────────────────────────┘
The Three Function Types in Durable Functions
| Type | Role | Analogy |
|---|---|---|
| Orchestrator Function | Controls the workflow — calls activities in order or in parallel | The project manager who assigns tasks |
| Activity Function | Does one specific unit of work | A worker who completes one assigned task |
| Client Function | Starts the orchestration (usually an HTTP trigger) | The customer who submits the order |
Durable Functions Flow – Diagram
┌──────────────────────────────────────────────────────────────┐ │ DURABLE FUNCTIONS FLOW │ │ │ │ 1. HTTP Request arrives │ │ │ │ │ ▼ │ │ 2. Client Function starts the orchestration │ │ │ │ │ ▼ │ │ 3. Orchestrator Function begins │ │ │ │ │ ├── Calls Activity 1: "Validate Order" │ │ │ └── Waits for result (function pauses here) │ │ │ │ │ ├── Calls Activity 2: "Charge Payment" │ │ │ └── Waits for result │ │ │ │ │ ├── Calls Activity 3: "Send Confirmation Email" │ │ │ └── Waits for result │ │ │ │ │ ▼ │ │ 4. Orchestration completes. State saved throughout. │ └──────────────────────────────────────────────────────────────┘
Installation
Durable Functions requires an additional npm package:
npm install durable-functions
Example – Sequential Order Processing Workflow
Client Function (HTTP Trigger) – index.js
const df = require("durable-functions");
module.exports = async function (context, req) {
const client = df.getClient(context);
// Start the orchestration and get a unique instance ID
const instanceId = await client.startNew(
"OrderOrchestrator",
undefined,
req.body // Pass order data to the orchestrator
);
context.log(`Orchestration started. Instance ID: ${instanceId}`);
// Return a status check URL to the caller
return client.createCheckStatusResponse(req, instanceId);
};
Orchestrator Function – index.js
const df = require("durable-functions");
module.exports = df.orchestrator(function* (context) {
const order = context.df.getInput();
// Step 1: Validate the order
const isValid = yield context.df.callActivity("ValidateOrder", order);
if (!isValid) {
return { status: "failed", reason: "Invalid order" };
}
// Step 2: Charge the payment
const paymentResult = yield context.df.callActivity("ChargePayment", order);
// Step 3: Send confirmation email
yield context.df.callActivity("SendConfirmationEmail", {
email: order.email,
orderId: paymentResult.orderId
});
return { status: "completed", orderId: paymentResult.orderId };
});
Activity Functions
// ValidateOrder/index.js
module.exports = async function (context) {
const order = context.bindings.name;
context.log("Validating order:", order);
// Check if all required fields exist
return order.productId && order.quantity && order.email;
};
// ChargePayment/index.js
module.exports = async function (context) {
const order = context.bindings.name;
context.log("Charging payment for order");
// Call payment gateway API here
return { orderId: "ORD-" + Date.now(), charged: true };
};
// SendConfirmationEmail/index.js
module.exports = async function (context) {
const data = context.bindings.name;
context.log(`Sending email to ${data.email} for order ${data.orderId}`);
return { sent: true };
};
Durable Functions Patterns
Durable Functions supports five well-known workflow patterns:
Pattern 1 – Function Chaining (Sequential)
┌────────┐ ┌────────┐ ┌────────┐ ┌────────┐ │Step 1 │───►│Step 2 │───►│Step 3 │───►│Step 4 │ │Validate│ │Charge │ │Package │ │ Ship │ └────────┘ └────────┘ └────────┘ └────────┘ Output of each step passes as input to the next step.
Pattern 2 – Fan-Out / Fan-In (Parallel)
┌────────────┐
│Orchestrator│
└─────┬──────┘
┌─────────────┼─────────────┐
▼ ▼ ▼
┌──────────┐ ┌──────────┐ ┌──────────┐
│Process │ │Process │ │Process │
│City 1 │ │City 2 │ │City 3 │
└──────────┘ └──────────┘ └──────────┘
│ │ │
└─────────────▼─────────────┘
┌──────────┐
│Aggregate │
│Results │
└──────────┘
All three activities run at the same time (fan-out).
Wait for all to complete, then combine results (fan-in).
Fan-Out / Fan-In Code Example
module.exports = df.orchestrator(function* (context) {
const cities = ["Mumbai", "Delhi", "Bangalore"];
// Start all tasks at the same time (fan-out)
const tasks = cities.map(city =>
context.df.callActivity("GetCitySalesReport", city)
);
// Wait for all tasks to complete (fan-in)
const results = yield context.df.Task.all(tasks);
// Combine all results
const totalRevenue = results.reduce((sum, r) => sum + r.revenue, 0);
return { totalRevenue };
});
Pattern 3 – Async HTTP (Long-Running Operations)
Client sends request Client polls status
│ ▲
▼ │
Start orchestration ──► Return 202 Accepted + status URL ──────┘
│
▼
Long task runs (minutes/hours)
│
▼
Status changes to "completed"
│
▼
Client polls status URL → Gets final result
Pattern 4 – Human Interaction (Approval Workflow)
┌──────────────────────────────────────────────────────────────┐ │ HUMAN APPROVAL WORKFLOW │ │ │ │ 1. Employee submits expense claim │ │ 2. Orchestrator sends approval email to manager │ │ 3. Orchestrator PAUSES and waits for external event │ │ (Can wait hours or days) │ │ 4. Manager clicks "Approve" or "Reject" in email │ │ 5. External event sent to orchestration │ │ 6. Orchestrator RESUMES based on manager's decision │ │ 7. Workflow completes │ └──────────────────────────────────────────────────────────────┘
Monitoring Orchestration Status
When an orchestration starts, it returns a set of URLs for monitoring:
| URL | Purpose |
|---|---|
| statusQueryGetUri | Check the current status of the orchestration |
| sendEventPostUri | Send an external event to a waiting orchestration |
| terminatePostUri | Force-stop the orchestration |
| purgeHistoryDeleteUri | Delete the orchestration history |
Durable Functions Use Cases
- Multi-step approval workflows (expense, leave, purchase orders)
- Data processing pipelines (extract → transform → load)
- Parallel processing of large data sets
- Long-running operations that exceed the function timeout limit
- Saga pattern for distributed transactions across microservices
