Micronaut JPA Application Performance on AWS Lambda

In this article, we would be looking into how we can deploy a Micronaut application providing GET, PUT and POST which can be called using an API Gateway. Then we would compare its performance when deployed with JVM runtime and as a native image.


In this article, I would be focusing on creating the application that can be deployed on an AWS Lambda and then call the application as if it's being called using an API Gateway.

I have previously written an article here which will help you to create a Micronaut function handler from scratch. I would recommend you to read it if you want to start from the beginning. If you want to skip it, you can use the code directly from my Github repo.

With this let’s get started.

Code Modifications

Now from the application you just created or got from the GitHub repo above, We would make some modifications to the code to start serving requests.

We can apply a simple MVC pattern, wherein we can create a controller, a service that has some business logic and does the transformation from a DTO to a model, and then a repository to persist it. This pattern is totally optional to implement. You can also apply a Domain-Driven Design(DDD) pattern to structure your code. If you would like to see a simple DDD code structure pattern using Micronaut you can have a look at this GitHub repo.

But we will keep things simple and call the repository directly from the controller, similar to our previous example wherein we called the repository from the handler function.

So the first change we will do is delete two classes.

  • OrderLambdaRuntime
  • OrderRequestHandler

With this, we will be left with only the model and repository.

Now, let's create our controller to handle the incoming request. I will create two methods, PUT to add orders and GET to get all the orders.

public class WebController {

final private OrderRepository orderRepository;

public WebController(OrderRepository orderRepository) {
this.orderRepository = orderRepository;

public HttpResponse<Order> getOrder(@PathVariable("id") Long id) {

Optional<Order> mayBeOrder = this.orderRepository.findById(id);
if (mayBeOrder.isPresent()) {
return HttpResponse.created(mayBeOrder.get());
return HttpResponse.notFound();

public HttpResponse<List<Order>> getAllOrders() {
return HttpResponse.ok(this.orderRepository.getAllOrders());

public HttpResponse<Order> addOrder(@Body @Valid Order order) {
Order savedOrder = orderRepository.save(order);
return HttpResponse.ok(savedOrder);

We also would now change the database from MySQL to Postgres. For this, we would add the Postgres driver dependency and the corresponding connection string in the properties file.

url: jdbc:postgresql://database.ashdjsirje.eu-central-1.rds.amazonaws.com/orders_db?characterEncoding=UTF-8
username: postgres
password: nopass
driverClassName: org.postgresql.Driver

I had to switch to Postgres because the MySQL driver was unstable when using it in a native image. It could not deserialize the object properly when it was being fetched.

With this, we are ready to build the application and deploy it on the Lambda JVM runtime

Deploying with JVM runtime.

Let’s build the application using mvn clean package , and then deploy it to an AWS Lambda using Java 11 runtime. Once created, set the handler function to a class provided by Micronaut i.e. io.micronaut.function.aws.proxy.MicronautLambdaHandler . Now, this class comes from the dependency micronaut-function-aws-api-proxy which we had already added previously.

Once we are ready, We can then trigger the lambda in the test section using the following payload.

"body": "{\"name\":\"Order from Lambda application\"}",
"resource": "/",
"path": "/",
"httpMethod": "PUT"

We can now also retrieve the persisted orders by making a GET call as follows.

"resource": "/",
"path": "/",
"httpMethod": "GET"

Once you hook the lambda function with an API getaway, the input request is similar to the one we just sent to the function as an input.

Now with this, let's look at the statistics I got for lambda configured with 512MB

Let’s now look at improving the application performance by creating a Native image.

Deploying Native Image on AWS Lambda with Custom Runtime

To build the native image, we would use a GraalVM JDK. I have used GraalVM CE 21.1.0 (build 11.0.11) for Java 11.

Before building, we have to set one extra property in the pom.xml file. we need to set the main class.


Now we can then run the following command to build the image.

./mvnw clean package -Dpackaging=docker-native -Dmicronaut.runtime=lambda

It may take around 3–5 minutes to build the zip file containing the native image depending on your system.

Next, let's create an AWS lambda function with the custom runtime option “Provide your own bootstrap on Amazon Linux 2”, and then upload the zip file.

This time you don’t need to set any handler function as the bootstrap file in the zip will start the application.

You can now trigger the application with the same tests from above to persist an order or to get a list of orders.

With native images, we get a significant improvement with the init time being only around 932ms during cold start and the subsequent PUT requests came close to 16ms.

Here are the statistics I got.

With this, we are at the end of series of articles exploring Micronaut. But this is not the end. I would be exploring more frameworks and Micronaut will be on my radar for new features.

Stay tuned with new trends in frameworks and software development in general by subscribing to my newsletter on RefactorFirst.com to get updates on more such articles. Also, follow me on Twitter.




Software Craftsman, Tech Enthusiast. I run https://refactorfirst.com to post all my articles

Love podcasts or audiobooks? Learn on the go with our new app.

Recommended from Medium

Is application/service running in cost effective way: public cloud?

Insurance App Development — Costs & Tips

Insurance App Development

My goals for Hngi8.

SLI’s and SLO’s, how to wrap your head around it and actually use them to calculate availability

Scalable Platform Performance Testing with Azure — Part 1

Create an AWS EC2 Instance with an Apache Webserver Using the AWS CLI

Building a Watch Face with Garmin Monkey C

How to revert to a previous commit and push to the remote repository.

Github logo

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store
Amrut Prabhu

Amrut Prabhu

Software Craftsman, Tech Enthusiast. I run https://refactorfirst.com to post all my articles

More from Medium

How to Use AWS Parameter Store

Example of Spring Cloud Function with AWS Lambda

Liquibase — Keep your database on track

Eventbus written in Python based on Kafka