AWS Lambda Managed Instances with Java 25 and AWS SAM – Part 1 Introduction and sample application

AWS Lambda Managed Instances with Java 25 and AWS SAM

AWS Lambda Managed Instances with Java 25 and AWS SAM article series article series - Part 1 Introduction and sample application

AWS Lambda Managed Instances with Java 25 and AWS SAM – Part 1 Introduction and sample application

AWS Lambda Managed Instances with Java 25 and AWS SAM article series article series - Part 2

AWS Lambda Managed Instances with Java 25 and AWS SAM – Part 2 Create Capacity Provider

AWS Lambda Managed Instances with Java 25 and AWS SAM – Part 1 Introduction and sample application

Introduction

In this article series, we’ll introduce the concepts behind the AWS Lambda Managed Instances (LMI). After it, we’ll develop a sample application that uses LMIs with Java 25 and AWS SAM. In this article, we’ll explain the ideas behind LMI and introduce our sample application.

AWS Lambda Managed Instances

Lambda Managed Instances enables us to run Lambda functions on our current-generation Amazon EC2 instances, including Graviton4, network-optimized instances, and other specialized compute options. This all without managing instance lifecycles, operating system and language runtime patching, routing, load balancing, or scaling policies. With Lambda Managed Instances, we benefit from EC2 pricing advantages, including EC2 Savings Plans and Reserved Instances.

For a list of supported instance types, go to the AWS Lambda Pricing page and select our AWS Region.

Key capabilities :

  • Choose suitable instances – Select appropriate instances based on performance and cost requirements, including access to the latest CPUs like Graviton4, configurable memory-CPU ratios, and high-bandwidth networking.
  • Automatic provisioning – AWS automatically provisions suitable instances and spins up function execution environments.
  • Dynamic scaling – Instances scale dynamically based on your function’s traffic patterns.
  • Fully managed experience – AWS handles infrastructure management, scaling, patching, and routing, with the same extensive event-source integrations you’re familiar with.

When to use Lambda Managed Instances :

  • High volume-predictable workloads – Best suited for steady-state workloads without unexpected traffic spikes. Lambda Managed Instances scale to handle traffic doubling within five minutes by default.
  • Performance-critical applications – Access to the latest CPUs, varying memory-CPU ratios, and high network throughput.
  • Regulatory requirements – Granular governance needs with control over VPC and instance placement.

Sample Application

Architecture, description and prerequisites

You can find a code example of our sample application in my GitHub  aws-lambda-java-25-lmi repository.

The architecture of our sample application is shown below:

Architecture diagram of the sample application

In this application, we will create products and retrieve them by their ID, and use Amazon DynamoDB as a NoSQL database for the persistence layer. We use Amazon API Gateway, which makes it easy for developers to create, publish, maintain, monitor, and secure APIs. Of course, we rely on AWS Lambda to execute code without the need to provision or manage servers. We also use AWS SAM, which provides a short syntax optimised for defining infrastructure as code (hereafter IaC) for serverless applications. For this article, I assume a basic understanding of the mentioned AWS services, serverless architectures on AWS, and AWS SAM.

To build and deploy the sample application, we need the following local installations: Java 25, Maven, AWS CLI, and SAM CLI.

Business logic

First, let’s look at the source code of the GetProductByIdHandler Lambda function. This Lambda function determines the product based on its ID and returns it:

@Override
public APIGatewayProxyResponseEvent handleRequest(APIGatewayProxyRequestEvent requestEvent, Context context) {
   var id = requestEvent.getPathParameters().get("id");
   var optionalProduct = productDao.getProduct(id);
   if (optionalProduct.isEmpty()) {
	  return new APIGatewayProxyResponseEvent()
           .withStatusCode(HttpStatusCode.NOT_FOUND)
	   .withBody("Product with id = " + id + " not found");
    }
    return new APIGatewayProxyResponseEvent()
         .withStatusCode(HttpStatusCode.OK)				       
         .withBody(objectMapper.writeValueAsString(optionalProduct.get()));
 }

The only method handleRequest receives an object of type APIGatewayProxyRequestEvent as input, as APIGatewayRequest invokes the Lambda function. From this input object, we retrieve the product ID by invoking requestEvent.getPathParameters().get(“id”) and ask our DynamoProductDao to find the product with this ID in the DynamoDB by invoking productDao.getProduct(id). Depending on whether the product exists or not, we wrap the Jackson serialised response in an object of type APIGatewayProxyResponseEvent and send it back to Amazon API Gateway as a response. The source code of the Lambda function CreateProductHandler, which we use to create and persist products, looks similar.

The source code of the Product entity looks very simple:

public record Product(String id, String name, BigDecimal price) {}

The implementation of the DynamoProductDao persistence layer uses AWS SDK for Java 2.0 to write to or read from the DynamoDB. Here is an example of the source code of the getProductById method, which we used in the GetProductByIdHandler Lambda function described above:

public Optional<Product> getProduct(String id) {
    GetItemResponse getItemResponse= dynamoDbClient.getItem(GetItemRequest.builder()
      .key(Map.of("PK", AttributeValue.builder().s(id).build()))
      .tableName(PRODUCT_TABLE_NAME)
      .build());
    if (getItemResponse.hasItem()) {
      return Optional.of(ProductMapper.productFromDynamoDB(getItemResponse.item()));
    } else {
      return Optional.empty();
    }
  }

Here we use the instance of DynamoDbClient Client to build GetItemRequest to query the DynamoDB table. We get the name of the table from an environment variable (which we will set in the AWS SAM template) by invoking System.getenv(“PRODUCT_TABLE_NAME”), for the product based on its ID. If the product is found, we use the custom-written ProductMapper to map the DynamoDB item to the attributes of the product entity.

We’ll cover the Infrastructure as Code (IaC) part described in the AWS SAM template.yaml in the next articles of the series, because it contains the LMI-specific parts.

Build and deploy

Now we have to build the application with mvn clean package and deploy it with sam deploy -g. We will see our customised Amazon API Gateway URL in the return. We can use it to create products and retrieve them by ID. The interface is secured with the API key. We have to send the following as HTTP header: “X-API-Key: a6ZbcDefQW12BN56WEV7LMI”, see MyApiKey definition in template.yaml.

To create the product with ID=1, we can use the following curl query:

Create and retrieve products

To create the product with ID=1, we can use the following curl query:

curl -m PUT -d '{ "id": 1, "name": "Print 10x13", "price": 0.15 }' 
-H "X-API-Key: a6ZbcDefQW12BN56WEV7LMI" https://{$API_GATEWAY_URL}/prod/products

For example, to query the existing product with ID=1, we can use the following curl query:

curl -H "X-API-Key: a6ZbcDefQW12BN56WEV7LMI" https://{$API_GATEWAY_URL}/prod/products/1

Conclusion

In this article, we explained the ideas behind AWS Lambda Managed Instances and introduced our sample application. In the next article, we’ll explain what a Lambda Capacity Provider is and how to create it.

If you like my content, please follow me on GitHub and give my repositories a star!

AWS Lambda Managed Instances with Java 25 and AWS SAM

AWS Lambda Managed Instances with Java 25 and AWS SAM – Part 2 Create Capacity Provider