AWS Lambda Durable Functions allow developers to write stateful, multi-step workflows entirely within code, removing the need for external orchestrators for many use cases. In this tutorial, we will build a classic e-commerce pattern—Order Processing—handling validation, payment, and inventory checks with built-in retries and checkpoints.
Key Takeaways
In this tutorial, we will cover the following concepts:
- The DurableContext: How to inject the coordination context into your handler (Node.js and Python).
- Steps and Checkpoints: Using
context.stepto persist results and avoid re-executing logic. - Waits: Suspending execution without billing using
context.wait. - Code Structure: Comparing the syntax between TypeScript/Node.js and Python.
The Order Processing Workflow
We are going to orchestrate a workflow that validates an order, processes a payment, waits for a brief confirmation period, and then confirms the order. If the function crashes or pauses, it resumes exactly where it left off.
Node.js / TypeScript Implementation
For Node.js (specifically runtimes like Node.js 24), we use the withDurableExecution wrapper. This injects the context object we need to manage state.
import { withDurableExecution } from "@aws/durable-execution-sdk-js"; export const handler = withDurableExecution(async (event, context) => { const orderId = event.orderId; // Step 1: Validate the order. The result is checkpointed. const validation = await context.step("validate-order", async () => { // Assume customerService is an imported module return await customerService.validate(event.customerId); }); if (!validation.isValid) { return { status: "rejected", reason: "Invalid Customer" }; } // Step 2: Process Payment. // If the function fails after this, payment is not charged twice. const payment = await context.step("process-payment", async () => { return await paymentService.charge(orderId, event.amount); }); // Step 3: Wait. // Execution suspends here. You are NOT billed for these 10 seconds. await context.wait({ seconds: 10 }); // Step 4: Confirm Order await context.step("confirm-order", async () => { return await inventoryService.confirm(orderId); }); return { status: "completed", orderId }; }); Python Implementation
If you prefer Python (specifically Python 3.14), the pattern uses decorators. The @durable_execution decorator handles the context injection, and @durable_step can be used on helper functions.
from aws_durable_execution_sdk_python import DurableContext, durable_execution, durable_step from aws_durable_execution_sdk_python.config import Duration @durable_step def validate_order(order_id): # Logic to validate order return {"status": "valid"} @durable_execution def lambda_handler(event, context: DurableContext): order_id = event['orderId'] # Step 1: Call the decorated step function # Note: context.step is used to wrap inline logic or calls validation = context.step( lambda: validate_order(order_id), name="validate-order" ) # Step 2: Wait using the Duration config context.wait(Duration.from_seconds(10)) # Step 3: Finalize result = context.step( lambda: {"status": "confirmed", "id": order_id}, name="finalize" ) return result How Replay Works
When the context.wait finishes (after 10 seconds), Lambda re-invokes your handler. It runs the code from the top. However, when it hits “validate-order” and “process-payment,” it sees those checkpoints exist. It skips the actual execution of those functions and immediately returns the stored result. The code only “runs” for the first time at lines following the wait.
Conclusion
We demonstrated how to build a multi-step workflow using simple code primitives. By wrapping side effects in steps, we ensure they are performed exactly once, and by using wait, we can pause execution for up to one year without paying for idle compute.