From 7c1b46a589f6d9dc2e03e591923c6a2e3bcfe24a Mon Sep 17 00:00:00 2001 From: Theophilus Ajayi Date: Thu, 5 Feb 2026 19:01:32 +0100 Subject: [PATCH 1/2] Lambda durable functions - Call a REST API Node.js --- lambda-durable-rest-api-sam-js/.gitignore | 24 ++++ lambda-durable-rest-api-sam-js/README.md | 111 ++++++++++++++++++ .../example-pattern.json | 64 ++++++++++ lambda-durable-rest-api-sam-js/src/app.mjs | 84 +++++++++++++ .../src/package.json | 21 ++++ lambda-durable-rest-api-sam-js/template.yaml | 30 +++++ 6 files changed, 334 insertions(+) create mode 100644 lambda-durable-rest-api-sam-js/.gitignore create mode 100644 lambda-durable-rest-api-sam-js/README.md create mode 100644 lambda-durable-rest-api-sam-js/example-pattern.json create mode 100644 lambda-durable-rest-api-sam-js/src/app.mjs create mode 100644 lambda-durable-rest-api-sam-js/src/package.json create mode 100644 lambda-durable-rest-api-sam-js/template.yaml diff --git a/lambda-durable-rest-api-sam-js/.gitignore b/lambda-durable-rest-api-sam-js/.gitignore new file mode 100644 index 000000000..4b6c6f876 --- /dev/null +++ b/lambda-durable-rest-api-sam-js/.gitignore @@ -0,0 +1,24 @@ +# SAM +.aws-sam/ +samconfig.toml + +# Node.js +node_modules/ +package-lock.json +npm-debug.log* +yarn-debug.log* +yarn-error.log* + +# IDE +.vscode/ +.idea/ +*.swp +*.swo +*~ + +# OS +.DS_Store +Thumbs.db + +# Test outputs +response.json diff --git a/lambda-durable-rest-api-sam-js/README.md b/lambda-durable-rest-api-sam-js/README.md new file mode 100644 index 000000000..e6d006517 --- /dev/null +++ b/lambda-durable-rest-api-sam-js/README.md @@ -0,0 +1,111 @@ +# Lambda Durable Function - REST API Call with Node.js + +This pattern demonstrates a Lambda durable function that calls an external REST API using Node.js. + +Learn more about this pattern at Serverless Land Patterns: << Add the live URL here >> + +Important: this application uses various AWS services and there are costs associated with these services after the Free Tier usage - please see the [AWS Pricing page](https://aws.amazon.com/pricing/) for details. You are responsible for any AWS costs incurred. No warranty is implied in this example. + +## Requirements + +* [Create an AWS account](https://portal.aws.amazon.com/gp/aws/developer/registration/index.html) if you do not already have one and log in. The IAM user that you use must have sufficient permissions to make necessary AWS service calls and manage AWS resources. +* [AWS CLI](https://docs.aws.amazon.com/cli/latest/userguide/install-cliv2.html) installed and configured +* [Git Installed](https://git-scm.com/book/en/v2/Getting-Started-Installing-Git) +* [AWS Serverless Application Model](https://docs.aws.amazon.com/serverless-application-model/latest/developerguide/serverless-sam-cli-install.html) (AWS SAM) installed + +## Deployment Instructions + +1. Create a new directory, navigate to that directory in a terminal and clone the GitHub repository: + ``` + git clone https://github.com/aws-samples/serverless-patterns + ``` +1. Change directory to the pattern directory: + ``` + cd lambda-durable-rest-api-sam-js + ``` + +1. From the command line, use AWS SAM to deploy the AWS resources for the pattern as specified in the template.yml file: + ``` + sam build + sam deploy --guided + ``` +1. During the prompts: + * Enter a stack name + * Enter the desired AWS Region + * Allow SAM CLI to create IAM roles with the required permissions. + + Once you have run `sam deploy --guided` mode once and saved arguments to a configuration file (samconfig.toml), you can use `sam deploy` in future to use these defaults. + +1. Note the outputs from the SAM deployment process. These contain the resource names and/or ARNs which are used for testing. + +## How it works + +This pattern demonstrates AWS Lambda Durable Execution for calling external REST APIs. It uses the AWS Durable Execution SDK to create a durable function that can: + +**AWS Durable Execution Features:** +- **Automatic State Management**: AWS manages execution state across invocations +- **Durable Steps**: The `context.step()` method marks functions that can be retried automatically +- **Durable Waits**: Use `context.wait()` to pause execution without consuming CPU or memory +- **Built-in Retry Logic**: Failed steps are automatically retried by AWS +- **Execution History**: AWS tracks the complete execution history and state + +The function uses the `withDurableExecution` wrapper to mark the Lambda handler as a durable execution workflow. All steps defined with `context.step()` are automatically retryable. + +AWS Lambda Durable Execution automatically handles failures, retries, and state persistence without requiring external services like DynamoDB or Step Functions. + +**Note**: This pattern requires Node.js 24.x runtime which has native support for durable execution. + +## Testing + +1. Get the function name from the stack outputs: +```bash +aws cloudformation describe-stacks --stack-name \ + --query 'Stacks[0].Outputs[?OutputKey==`FunctionName`].OutputValue' --output text +``` + +2. Invoke the function with default URL: +```bash +aws lambda invoke \ + --function-name \ + --payload '{}' \ + response.json && cat response.json +``` + +3. Invoke with a custom URL: +```bash +aws lambda invoke \ + --function-name \ + --payload '{"url": "https://jsonplaceholder.typicode.com/users/1"}' \ + response.json && cat response.json +``` + +Example JSON Lambda test event: +```json +{ + "url": "https://jsonplaceholder.typicode.com/posts/1" +} +``` + +Expected response (success): +```json +{ + "statusCode": 200, + "headers": {"Content-Type": "application/json"}, + "body": "{\"message\": \"API call successful\", \"url\": \"https://jsonplaceholder.typicode.com/posts/1\", \"data\": {...}}" +} +``` + +The execution is durable - if the API call fails, AWS Lambda will automatically retry the `callRestApi` step without re-executing the entire function. + +## Cleanup + +1. Delete the stack: + ```bash + aws cloudformation delete-stack --stack-name + ``` +1. Confirm the stack has been deleted: + ```bash + aws cloudformation list-stacks --query "StackSummaries[?contains(StackName,'')].StackStatus" + ``` + +---- diff --git a/lambda-durable-rest-api-sam-js/example-pattern.json b/lambda-durable-rest-api-sam-js/example-pattern.json new file mode 100644 index 000000000..73698925f --- /dev/null +++ b/lambda-durable-rest-api-sam-js/example-pattern.json @@ -0,0 +1,64 @@ +{ + "title": "Lambda Durable Function - REST API Call (Node.js)", + "description": "A Lambda function that calls an external REST API using AWS Durable Execution SDK for automatic retries and state management.", + "language": "Node.js", + "level": "200", + "framework": "SAM", + "introBox": { + "headline": "How it works", + "text": [ + "This pattern demonstrates AWS Lambda Durable Execution for calling external REST APIs.", + "Uses the withDurableExecution wrapper to mark the Lambda handler as a durable workflow.", + "Uses context.step() to make the REST API call automatically retryable.", + "AWS automatically handles failures, retries, and state persistence without external services." + ] + }, + "gitHub": { + "template": { + "repoURL": "https://github.com/aws-samples/serverless-patterns/tree/main/lambda-durable-rest-api-sam-js", + "templateURL": "serverless-patterns/lambda-durable-rest-api-sam-js", + "projectFolder": "lambda-durable-rest-api-sam-js", + "templateFile": "template.yaml" + } + }, + "resources": { + "bullets": [ + { + "text": "AWS Lambda Durable Execution", + "link": "https://docs.aws.amazon.com/lambda/latest/dg/durable-functions.html" + }, + { + "text": "AWS Lambda Developer Guide", + "link": "https://docs.aws.amazon.com/lambda/latest/dg/welcome.html" + }, + { + "text": "Node.js Fetch API", + "link": "https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API" + } + ] + }, + "deploy": { + "text": [ + "sam build", + "sam deploy --guided" + ] + }, + "testing": { + "text": [ + "See the GitHub repo for detailed testing instructions." + ] + }, + "cleanup": { + "text": [ + "Delete the stack: aws cloudformation delete-stack --stack-name STACK_NAME." + ] + }, + "authors": [ + { + "name": "Theophilus Ajayi", + "image": "https://drive.google.com/file/d/1hUrUxWk2JqDTbPhl0DgUeUVd2uFWnAby/view?usp=drivesdk", + "bio": "Technical Account Manager @ AWS", + "linkedin": "tolutheo" + } + ] +} diff --git a/lambda-durable-rest-api-sam-js/src/app.mjs b/lambda-durable-rest-api-sam-js/src/app.mjs new file mode 100644 index 000000000..4484141a5 --- /dev/null +++ b/lambda-durable-rest-api-sam-js/src/app.mjs @@ -0,0 +1,84 @@ +/** + * Lambda Durable Function - Calls REST API using AWS Durable Execution SDK + * Note: Node.js 22.x has built-in durable execution support + */ + +/** + * Lambda Durable Function - Calls REST API using AWS Durable Execution SDK + */ +import { withDurableExecution } from '@aws/durable-execution-sdk-js'; + +const DEFAULT_API_URL = process.env.API_URL || 'https://jsonplaceholder.typicode.com/posts/1'; + +export const handler = withDurableExecution(async (event, context) => { + context.logger.info('Starting durable REST API call'); + + // Get API URL from event or use default + const apiUrl = event.url || DEFAULT_API_URL; + + context.logger.info(`Using API URL: ${apiUrl}`); + + // Execute the REST API call as a durable step + const result = await context.step('Call REST API', async (stepCtx) => { + stepCtx.logger.info(`Calling REST API: ${apiUrl}`); + + try { + const response = await fetch(apiUrl, { + method: 'GET', + signal: AbortSignal.timeout(30000) // 30 second timeout + }); + + if (!response.ok) { + throw new Error(`HTTP error! status: ${response.status}`); + } + + const data = await response.json(); + + stepCtx.logger.info(`API call successful: ${response.status}`); + + return { + status: 'success', + statusCode: response.status, + data: data + }; + + } catch (error) { + stepCtx.logger.error(`API call failed: ${error.message}`); + return { + status: 'error', + error: error.message + }; + } + }); + + // Pause for 2 seconds without consuming CPU cycles or incurring usage charges + await context.wait({ seconds: 2 }); + + // Context logger is replay aware and will not log the same message multiple times + context.logger.info('Waited for 2 seconds without consuming CPU.'); + + // Return response based on result + if (result.status === 'success') { + const response = { + statusCode: 200, + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify({ + message: 'API call successful', + url: apiUrl, + data: result.data + }) + }; + return response; + } else { + const response = { + statusCode: 500, + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify({ + error: 'API call failed', + url: apiUrl, + details: result.error + }) + }; + return response; + } +}); diff --git a/lambda-durable-rest-api-sam-js/src/package.json b/lambda-durable-rest-api-sam-js/src/package.json new file mode 100644 index 000000000..ded2b1bc8 --- /dev/null +++ b/lambda-durable-rest-api-sam-js/src/package.json @@ -0,0 +1,21 @@ +{ + "name": "lambda-durable-rest-api", + "version": "1.0.0", + "description": "Lambda Durable Function - REST API Call with Node.js", + "type": "module", + "main": "app.mjs", + "scripts": { + "test": "echo \"Error: no test specified\" && exit 1" + }, + "dependencies": { + "@aws/durable-execution-sdk-js": "^1.0.0" + }, + "keywords": [ + "aws", + "lambda", + "durable", + "rest-api" + ], + "author": "", + "license": "MIT-0" +} diff --git a/lambda-durable-rest-api-sam-js/template.yaml b/lambda-durable-rest-api-sam-js/template.yaml new file mode 100644 index 000000000..20f845374 --- /dev/null +++ b/lambda-durable-rest-api-sam-js/template.yaml @@ -0,0 +1,30 @@ +AWSTemplateFormatVersion: '2010-09-09' +Transform: AWS::Serverless-2016-10-31 +Description: Lambda Durable Function - Call REST API with Node.js + +Resources: + DurableFunction: + Type: AWS::Serverless::Function + Properties: + CodeUri: src/ + Handler: app.handler + Runtime: nodejs24.x + Timeout: 300 + DurableConfig: + ExecutionTimeout: 900 # 15 minutes - allows durablefunction to be invoked syncronously and asynchronously + RetentionPeriodInDays: 7 + MemorySize: 512 + Architectures: + - x86_64 + Environment: + Variables: + API_URL: https://jsonplaceholder.typicode.com/posts/1 + +Outputs: + FunctionArn: + Description: Lambda Function ARN + Value: !GetAtt DurableFunction.Arn + + FunctionName: + Description: Lambda Function Name + Value: !Ref DurableFunction From 51be0e453b6c849208e4f45ea0ff357921510f7e Mon Sep 17 00:00:00 2001 From: Theophilus Ajayi Date: Fri, 27 Feb 2026 10:32:04 +0100 Subject: [PATCH 2/2] seperate python and js PR --- lambda-durable-rest-api-sam-js/README.md | 31 +++++++++---------- .../example-pattern.json | 8 ++--- lambda-durable-rest-api-sam-js/src/app.mjs | 2 +- 3 files changed, 20 insertions(+), 21 deletions(-) diff --git a/lambda-durable-rest-api-sam-js/README.md b/lambda-durable-rest-api-sam-js/README.md index e6d006517..7418583a4 100644 --- a/lambda-durable-rest-api-sam-js/README.md +++ b/lambda-durable-rest-api-sam-js/README.md @@ -1,8 +1,8 @@ -# Lambda Durable Function - REST API Call with Node.js +# AWS Lambda durable function - REST API call with Node.js -This pattern demonstrates a Lambda durable function that calls an external REST API using Node.js. +This pattern demonstrates a AWS Lambda durable function that calls an external REST API using Node.js. -Learn more about this pattern at Serverless Land Patterns: << Add the live URL here >> +Learn more about this pattern at Serverless Land Patterns: https://serverlessland.com/patterns/lambda-durable-rest-api-sam-js Important: this application uses various AWS services and there are costs associated with these services after the Free Tier usage - please see the [AWS Pricing page](https://aws.amazon.com/pricing/) for details. You are responsible for any AWS costs incurred. No warranty is implied in this example. @@ -40,18 +40,7 @@ Important: this application uses various AWS services and there are costs associ ## How it works -This pattern demonstrates AWS Lambda Durable Execution for calling external REST APIs. It uses the AWS Durable Execution SDK to create a durable function that can: - -**AWS Durable Execution Features:** -- **Automatic State Management**: AWS manages execution state across invocations -- **Durable Steps**: The `context.step()` method marks functions that can be retried automatically -- **Durable Waits**: Use `context.wait()` to pause execution without consuming CPU or memory -- **Built-in Retry Logic**: Failed steps are automatically retried by AWS -- **Execution History**: AWS tracks the complete execution history and state - -The function uses the `withDurableExecution` wrapper to mark the Lambda handler as a durable execution workflow. All steps defined with `context.step()` are automatically retryable. - -AWS Lambda Durable Execution automatically handles failures, retries, and state persistence without requiring external services like DynamoDB or Step Functions. +This pattern demonstrates AWS Lambda durable execution for calling external REST APIs. The function uses the `withDurableExecution` wrapper to mark the Lambda handler as a durable execution workflow. All steps defined with `context.step()` are automatically retryable. **Note**: This pattern requires Node.js 24.x runtime which has native support for durable execution. @@ -108,4 +97,14 @@ The execution is durable - if the API call fails, AWS Lambda will automatically aws cloudformation list-stacks --query "StackSummaries[?contains(StackName,'')].StackStatus" ``` ----- +## Learn More + +- [AWS Lambda durable functions Documentation](https://docs.aws.amazon.com/lambda/latest/dg/durable-functions.html) +- [durable execution SDK (Python)](https://github.com/aws/aws-durable-execution-sdk-js) +- [Callback Operations](https://docs.aws.amazon.com/lambda/latest/dg/durable-callback.html) +--- + +Copyright 2025 Amazon.com, Inc. or its affiliates. All Rights Reserved. + +SPDX-License-Identifier: MIT-0 + diff --git a/lambda-durable-rest-api-sam-js/example-pattern.json b/lambda-durable-rest-api-sam-js/example-pattern.json index 73698925f..0baf227d6 100644 --- a/lambda-durable-rest-api-sam-js/example-pattern.json +++ b/lambda-durable-rest-api-sam-js/example-pattern.json @@ -1,13 +1,13 @@ { - "title": "Lambda Durable Function - REST API Call (Node.js)", - "description": "A Lambda function that calls an external REST API using AWS Durable Execution SDK for automatic retries and state management.", + "title": "AWS Lambda durable failures - REST API call (Node.js)", + "description": "A Lambda function that calls an external REST API using AWS durable execution SDK for automatic retries and state management.", "language": "Node.js", "level": "200", "framework": "SAM", "introBox": { "headline": "How it works", "text": [ - "This pattern demonstrates AWS Lambda Durable Execution for calling external REST APIs.", + "This pattern demonstrates AWS Lambda durable execution for calling external REST APIs.", "Uses the withDurableExecution wrapper to mark the Lambda handler as a durable workflow.", "Uses context.step() to make the REST API call automatically retryable.", "AWS automatically handles failures, retries, and state persistence without external services." @@ -24,7 +24,7 @@ "resources": { "bullets": [ { - "text": "AWS Lambda Durable Execution", + "text": "AWS Lambda durable execution", "link": "https://docs.aws.amazon.com/lambda/latest/dg/durable-functions.html" }, { diff --git a/lambda-durable-rest-api-sam-js/src/app.mjs b/lambda-durable-rest-api-sam-js/src/app.mjs index 4484141a5..7589500d3 100644 --- a/lambda-durable-rest-api-sam-js/src/app.mjs +++ b/lambda-durable-rest-api-sam-js/src/app.mjs @@ -4,7 +4,7 @@ */ /** - * Lambda Durable Function - Calls REST API using AWS Durable Execution SDK + * AWS Lambda durable function - Calls REST API using AWS durable execution SDK */ import { withDurableExecution } from '@aws/durable-execution-sdk-js';