A complete, working example of AWS Lambda Durable Functions in Python with a React frontend, demonstrating checkpoint/replay, parallel execution, human-in-the-loop callbacks (manager approval + external fraud check), and real-time progress tracking through a realistic loan approval pipeline.
Built for the presentation: "Lambda Durable Functions vs Step Functions: When Each Wins"
lambda-durable-demo/
├── template.yaml # SAM template (API GW, Lambdas, DynamoDB)
├── samconfig.toml # SAM deploy configuration
├── src/
│ ├── loan_demo.py # Durable workflow with DynamoDB progress logging
│ ├── api.py # API Lambda (POST /apply, GET /status, POST /approve)
│ ├── fraud_check.py # External fraud check Lambda (callback pattern)
│ └── requirements.txt # Python dependencies
└── frontend/
├── package.json
├── vite.config.js
├── index.html
├── .env # API URL (set after deploy)
└── src/
├── main.jsx
├── App.jsx
└── App.css
| Feature | Where | Description |
|---|---|---|
@durable_step |
loan_demo.py |
Checkpointed business logic units |
@durable_execution |
loan_demo.py |
Durable workflow handler |
context.step() |
loan_demo.py |
Execute and checkpoint a step |
context.parallel() |
loan_demo.py |
Concurrent credit bureau checks (3 bureaus) |
context.wait_for_callback() |
loan_demo.py |
Manager approval + external fraud check |
| Callback pattern | fraud_check.py |
External service sends callback to resume workflow |
| Real-time progress | api.py |
DynamoDB-backed progress polling from React frontend |
| Replay detection | loan_demo.py |
Counter-based [REPLAY] tagging on re-executed log entries |
| Structured logging | All functions | Powertools Logger with JSON output and correlation IDs |
| X-Ray tracing | api.py, fraud_check.py |
Powertools Tracer with method-level subsegments |
| CloudWatch metrics | api.py |
Powertools Metrics (ApplicationsSubmitted, ApprovalsProcessed) |
Three hardcoded profiles produce predictable outcomes:
| Profile | SIN (last 4) | Default Amount | Outcome |
|---|---|---|---|
| Alice Johnson | 1111 | $150,000 | Always approved (triggers manager approval) |
| Bob Martinez | 2222 | $50,000 | Always denied (credit score too low) |
| Charlie Wilson | 3333 | $25,000 | Approved if <= $25,000, denied if above |
- Python 3.13+
- Node.js 18+
- AWS SAM CLI v1.150.1+
- AWS account with Lambda access
sam build
sam deploy --guided # first time
sam deploy # subsequent deploysNote the LoanApiUrl output — you'll need it for the frontend.
cd frontend
npm install
# Set the API URL from the deploy output
echo "VITE_API_URL=https://YOUR-API-ID.execute-api.us-east-1.amazonaws.com" > .env
npm run devThe frontend runs at http://localhost:5173.
- Click a demo profile (Alice, Bob, or Charlie) to pre-fill the form
- Click Submit Application
- Watch the step indicator and logs update in real-time
- For Alice ($150K): a manager approval modal appears — approve or deny
- The fraud check runs automatically via an external Lambda callback
All functions use AWS Lambda Powertools for Python for structured observability:
- Logger: JSON-structured logs with correlation IDs and
application_idcontext - Tracer: X-Ray tracing with method-level subsegments (on
api.pyandfraud_check.py— not onloan_demo.pybecause@durable_executionreplay would create misleading traces) - Metrics: CloudWatch EMF metrics for
ApplicationsSubmitted,ApprovalsProcessed, andColdStart(onapi.pyonly)
When a durable function resumes after a callback or failure, the handler re-executes from the top. Completed steps return their cached checkpoint results instead of re-executing. This is why:
- Code must be deterministic (same operation order every time)
- Non-deterministic values (timestamps, random) must be inside steps
- Side effects must be inside steps (or they'll repeat on replay)
The workflow uses context.wait_for_callback() in two places:
- Manager approval — the workflow suspends and stores a
callback_idin DynamoDB. The frontend reads it and sends the approval via the API. - Fraud check — the workflow invokes an external Lambda (
fraud_check.py) which processes the request and callssend_durable_execution_callback_successto resume the workflow.
In both cases, the Lambda uses zero compute while waiting.
Durable functions must be invoked with a qualified ARN (version or alias). This ensures replay uses the same code version that started the execution. The template uses AutoPublishAlias: live to handle this automatically.


