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

TypeRoleAnalogy
Orchestrator FunctionControls the workflow — calls activities in order or in parallelThe project manager who assigns tasks
Activity FunctionDoes one specific unit of workA worker who completes one assigned task
Client FunctionStarts 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:

URLPurpose
statusQueryGetUriCheck the current status of the orchestration
sendEventPostUriSend an external event to a waiting orchestration
terminatePostUriForce-stop the orchestration
purgeHistoryDeleteUriDelete 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

Leave a Comment