Skip to content

Quickstart

Create and deploy your first durable function using the AWS CLI. This guide covers TypeScript, Python, Java, and C#.

Adding all your dependencies to the deployment package

This guide shows you how to package all your dependencies, including the Durable Execution SDK, and deploy together with your custom code as a zip archive. This ensures that you control the exact version of the Durable Execution SDK that your code uses. You can create a durable function for quick testing purposes in the AWS Console, but then the version of the SDK might be older and it might not contain the latest features and optimizations.

Prerequisites

  • AWS CLI installed and configured with credentials
  • Node.js 22+
  • Python 3.13+
  • Java 17+ and Maven 3.8+
  • .NET 10 SDK

Create the execution role

Create an IAM role that grants your function permission to perform checkpoint operations.

Save the following as trust-policy.json:

{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Effect": "Allow",
            "Principal": {
                "Service": "lambda.amazonaws.com"
            },
            "Action": "sts:AssumeRole"
        }
    ]
}

Create the role and attach the AWSLambdaBasicDurableExecutionRolePolicy managed policy:

# Replace durable-function-role with your preferred role name
aws iam create-role \
  --role-name durable-function-role \
  --assume-role-policy-document file://trust-policy.json

aws iam attach-role-policy \
  --role-name durable-function-role \
  --policy-arn arn:aws:iam::aws:policy/service-role/AWSLambdaBasicDurableExecutionRolePolicy

Note the role ARN returned. You'll need it in the next step.

Write the durable function

Save as index.mjs

import { withDurableExecution } from "@aws/durable-execution-sdk-js";

export const handler = withDurableExecution(async (event, context) => {
  const message = await context.step("step-1", (stepCtx) => {
    stepCtx.logger.info("Hello from step-1");
    return "Hello from Durable Lambda!";
  });

  // Pause for 10 seconds without consuming CPU or incurring usage charges
  await context.wait({ seconds: 10 });

  // Replay-aware: logs once even though the function replays after the wait
  context.logger.info("Resumed after wait");

  return { statusCode: 200, body: message };
});

Save as lambda_function.py

from aws_durable_execution_sdk_python.config import Duration
from aws_durable_execution_sdk_python.context import DurableContext, StepContext, durable_step
from aws_durable_execution_sdk_python.execution import durable_execution


@durable_step
def my_step(step_context: StepContext) -> str:
    step_context.logger.info("Hello from my_step")
    return "Hello from Durable Lambda!"


@durable_execution
def lambda_handler(event, context: DurableContext) -> dict:
    message: str = context.step(my_step())

    # Pause for 10 seconds without consuming CPU or incurring usage charges
    context.wait(Duration.from_seconds(10))

    # Replay-aware: logs once even though the function replays after the wait
    context.logger.info("Resumed after wait")

    return {"statusCode": 200, "body": message}

Save as QuickstartFunction.java

import java.time.Duration;
import java.util.Map;
import software.amazon.lambda.durable.DurableContext;
import software.amazon.lambda.durable.DurableHandler;

public class QuickstartFunction extends DurableHandler<Map<String, String>, Map<String, Object>> {

    @Override
    public Map<String, Object> handleRequest(Map<String, String> event, DurableContext context) {
        String message = context.step("step-1", String.class, stepCtx -> {
            stepCtx.getLogger().info("Hello from step-1");
            return "Hello from Durable Lambda!";
        });

        // Pause for 10 seconds without consuming CPU or incurring usage charges
        context.wait("wait-10s", Duration.ofSeconds(10));

        // Replay-aware: logs once even though the function replays after the wait
        context.getLogger().info("Resumed after wait");

        return Map.of("statusCode", 200, "body", message);
    }
}

Save as Function.cs

using Amazon.Lambda.Core;
using Amazon.Lambda.DurableExecution;
using Microsoft.Extensions.Logging;

public class QuickstartFunction
{
    public Task<DurableExecutionInvocationOutput> Handler(
        DurableExecutionInvocationInput input, ILambdaContext context)
        => DurableFunction.WrapAsync<object, Response>(Workflow, input, context);

    private async Task<Response> Workflow(object input, IDurableContext ctx)
    {
        string message = await ctx.StepAsync(
            async (stepCtx, _) =>
            {
                stepCtx.Logger.LogInformation("Hello from step-1");
                return "Hello from Durable Lambda!";
            },
            name: "step-1");

        // Pause for 10 seconds without consuming CPU or incurring usage charges
        await ctx.WaitAsync(TimeSpan.FromSeconds(10), name: "wait-10s");

        // Replay-aware: logs once even though the function replays after the wait
        ctx.Logger.LogInformation("Resumed after wait");

        return new Response(200, message);
    }
}

public record Response(int StatusCode, string Body);

This shows the workflow and handler. For the entry point (Main + LambdaBootstrap + serializer), the class-library alternative, and the full project setup, see the C# SDK guide.

The wait here is for 10 seconds just for an easy quick example, but it could as easily be 10 days without incurring extra compute.

Package and deploy

Replace 123456789012 with your AWS account ID and the role arn with that of the execution role you just created.

mkdir my-function && cd my-function
npm init -y
npm install @aws/durable-execution-sdk-js

Save the function code above as index.mjs, then package and deploy:

zip -r function.zip index.mjs node_modules/

aws lambda create-function \
  --function-name my-durable-function \
  --runtime nodejs22.x \
  --role arn:aws:iam::123456789012:role/durable-function-role \
  --handler index.handler \
  --zip-file fileb://function.zip \
  --durable-config '{"ExecutionTimeout": 900, "RetentionPeriodInDays": 1}'
mkdir -p package
pip install aws-durable-execution-sdk-python --target package/
cp lambda_function.py package/
cd package && zip -r ../function.zip . && cd ..

aws lambda create-function \
  --function-name my-durable-function \
  --runtime python3.14 \
  --role arn:aws:iam::123456789012:role/durable-function-role \
  --handler lambda_function.lambda_handler \
  --zip-file fileb://function.zip \
  --durable-config '{"ExecutionTimeout": 900, "RetentionPeriodInDays": 1}'

Set up a Maven project with the following pom.xml dependencies:

<dependency>
    <groupId>software.amazon.lambda.durable</groupId>
    <artifactId>aws-durable-execution-sdk-java</artifactId>
    <version>1.1.0</version>
</dependency>
<dependency>
    <groupId>com.amazonaws</groupId>
    <artifactId>aws-lambda-java-core</artifactId>
    <version>1.4.0</version>
</dependency>

Add the maven-shade-plugin to produce a fat jar with all dependencies bundled:

<plugin>
    <groupId>org.apache.maven.plugins</groupId>
    <artifactId>maven-shade-plugin</artifactId>
    <version>3.6.2</version>
    <configuration>
        <createDependencyReducedPom>false</createDependencyReducedPom>
    </configuration>
    <executions>
        <execution>
            <phase>package</phase>
            <goals><goal>shade</goal></goals>
        </execution>
    </executions>
</plugin>

Build the fat jar and deploy:

mvn clean package -DskipTests

aws lambda create-function \
  --function-name my-durable-function \
  --runtime java21 \
  --role arn:aws:iam::123456789012:role/durable-function-role \
  --handler QuickstartFunction::handleRequest \
  --zip-file fileb://target/*.jar \
  --durable-config '{"ExecutionTimeout": 900, "RetentionPeriodInDays": 1}'

Add the SDK to your project, then publish and package the output:

dotnet add package Amazon.Lambda.DurableExecution

dotnet publish -c Release -o publish
cd publish && zip -r ../function.zip . && cd ..

aws lambda create-function \
  --function-name my-durable-function \
  --runtime dotnet10 \
  --role arn:aws:iam::123456789012:role/durable-function-role \
  --handler MyDurableFunction \
  --zip-file fileb://function.zip \
  --durable-config '{"ExecutionTimeout": 900, "RetentionPeriodInDays": 1}'

The --handler value depends on your programming model: the assembly name for the executable model, or Assembly::Namespace.Type::Method for a class library. See the C# SDK guide for the programming models, serializer registration, and handler string for each.

Publish a version

You must invoke a durable functions with a published version or alias to ensure deterministic replay.

For quick testing here in the Quickstart we can just invoke the durable function with $LATEST. Note that you should NOT do this for production workloads.

Be sure to publish a version if this is for production. Invoking $LATEST directly is not supported for production workloads.

aws lambda publish-version --function-name my-durable-function

Note the version number in the returned ARN (for example, :1).

Invoke

For synchronous invocation:

aws lambda invoke \
  --function-name 'my-durable-function:$LATEST' \
  --cli-binary-format raw-in-base64-out \
  --payload '{}' \
  response.json

cat response.json

The function runs step-1, then pauses for 10 seconds without consuming compute. After the wait, it resumes and returns the result.

Clean up

See delete durable functions to clean up your function and IAM role.

Next steps