Serverless Framework with Spring Boot & AWS Lambda

Ahmet Caliskan
5 min readOct 20, 2021

Nowadays cloud became hot topic for software engineers. If you want to deploy an app on cloud (it doesn’t matter which architecture selected for building the app), most probably you will have to choose IaC (Infrastructure as a Code) approach for deployment.

Terraform is widely used in the industry to hadle IaC deployments. Do you think is it the right choice for serverless architecture?

Two years ago when I developed my first serverless app on AWS, I tried to handle the infrastructure with Terraform. I did it, but it cost me lots of time to create all relations between Lambda functions and API GW. When I first saw an app deployed with Serverless Framework, It was really easy to create your Lambda Functions and link with API GW. Creating Dynamo DB Tables, SQS Ques, SNS Events just with 3 lines of codes. I became a huge fan of it.

Today I want to show you how you can run a simple Spring Boot Application on Lambda Functions and how you can create and deploy required resources with Serverless Framework.

Ok, Let’s start with requirements for our Demo API.

We will build a simple Spring Boot Application with a REST API for Quotes with methods for:

  1. Fetching all existing quotes
  2. Fetching a specific existing quote
  3. Creation of a new quote
  4. Updating of an existing quote
  5. Deletion of an existing quote

Each quote has to have at least the following properties:

  • author
  • quote

Ok, Let’s continue with Architecture.

To be able to achieve this requirements we need a spring boot controller which handles the CRUD operations for us. To fulfill all needed steps at least we need a controller with 5 different methods.

  • get /quotes -> Fetching all existing quotes
  • get /quote/{id} -> Fetching a specific existing quote
  • post /quote -> Creation of a new quote
  • put /quote/{id} -> Updating of an existing quote
  • delete /quote/{id} -> Deletion of an existing quote

I will not dig into details on how to create these endpoints with Spring Boot, just think about you want to move this demo app to AWS with Serverless Application Model. Than you will need to map every rest endpoints to a lambda functions and you will need a database to store your data. Of course you will need a centralized logging place to see your application activities. Than you will end up with a deployment diagram like below.

Let’s continue with implementation.

Requirements

  • AWS Account
  • aws cli (must be configured with correct permissions to be able to deploy the app)
  • npm (It is required to install serverless framework dependencies)
  • serverless framework (npm install -g serverless)
  • java11 and any IDE acceptable

Building the infrastructure

Serverless Framework builded on top of Cloud Formation. The resources are created similarly with YAML files with less effort. Our template file is named serverless.yml

Let’s start with defining a lambda function with serverless framework.

service: serverless-spring-boot

provider:
name: aws
runtime: java11
region: eu-central-1
stage: ${opt:stage, 'dev'}

functions:
getQuotes:
handler: com.serverless.example.StreamLambdaHandler::handleRequest
events:
- http:
path: quotes
method: get
cors: true

In this yaml file we defined a single lambda function which provider configured as AWS and region as Frankfurt and runtime selected as java11. Than we defined our function as getQuote which consumes the API GW event for http get methods on quotes endpoint. By the way CORS configurations are enabled on API GW. Besides that entry point of lambda function is configured as “com.serverless.example.StreamLambdaHandler::handleRequest” method.

Great! Let’s check how to create a DynamoDB table;

resources:
Resources:
QuotesTable:
Type: AWS::DynamoDB::Table
Properties:
TableName: ${self:custom.settings.quotesTableName}
AttributeDefinitions:
- AttributeName: id
AttributeType: S
KeySchema:
- AttributeName: id
KeyType: HASH
BillingMode: PAY_PER_REQUEST

This block will create a DynamoDB Table with name passed as parameter and with properties of specified attributes. And configured our billing method as “PAY_PER_REQUEST”.

We have the basics for creating the infrastructure. Let’s continue on Spring Boot how to adapt for Lambda Functions.

I will use aws-serverless-java-container-springboot2 maven package to run Spring Boot Application on AWS Lambda Function.

What we need to know is Lambda Functions are triggered via Events. We have a challenge here to get this API GW event and pass it to spring boot controller. To handle this functionality we need to implement RequestStreamHandler class from lambda.runtime package and implement the handleRequest method for receiving Lambda Input Stream and fill the Lamda OutputStream.

package com.serverless.example;


import com.amazonaws.serverless.exceptions.ContainerInitializationException;
import com.amazonaws.serverless.proxy.model.*;
import com.amazonaws.serverless.proxy.spring.SpringBootLambdaContainerHandler;
import com.amazonaws.services.lambda.runtime.Context;
import com.amazonaws.services.lambda.runtime.RequestStreamHandler;

import java.io.*;

public class StreamLambdaHandler implements RequestStreamHandler {
private static SpringBootLambdaContainerHandler<AwsProxyRequest, AwsProxyResponse> handler;
static {
try {
handler = SpringBootLambdaContainerHandler.getAwsProxyHandler(ServerlessApplication.class);
// If you are using HTTP APIs with the version 2.0 of the proxy model, use the getHttpApiV2ProxyHandler
// method: handler = SpringBootLambdaContainerHandler.getHttpApiV2ProxyHandler(Application.class);
} catch (ContainerInitializationException e) {
// if we fail here. We re-throw the exception to force another cold start
e.printStackTrace();
throw new RuntimeException("Could not initialize Spring Boot application", e);
}
}

@Override
public void handleRequest(InputStream inputStream, OutputStream outputStream, Context context)
throws IOException {
handler.proxyStream(inputStream, outputStream, context);
}
}

SpringBootLambdaContainerHandler will receive the AWS API GW event and will pass it Rest Controllers.

Note: Keep in mind this handler only consumes API GW Events, if you want to consume any other events like SNS, SQS etc.. it is little bit tricky. You can find example for CloudWatch Events inside the code repository.

With all this information we are ready to cook :)

You can find the completed code on GitLab:

(https://gitlab.com/medium-projects/serverless-spring-boot)

Deployment

If you installed all required tools and packages specified on requirements section, deployment is really easy.

First build the project with “gradle build” than just type “sls deploy”

ahmets-mbp:serverless-spring-boot ahmetcaliskan$ sls deploy 
Serverless: Packaging service...
Serverless: Service files not changed. Skipping deployment...
Service Information
service: serverless-spring-boot
stage: dev
region: eu-central-1
stack: serverless-spring-boot-dev
resources: 47
api keys:
None
endpoints:
GET - https://peutgreo34.execute-api.eu-central-1.amazonaws.com/dev/quotes
GET - https://peutgreo34.execute-api.eu-central-1.amazonaws.com/dev/quote/{id}
POST - https://peutgreo34.execute-api.eu-central-1.amazonaws.com/dev/quote
PUT - https://peutgreo34.execute-api.eu-central-1.amazonaws.com/dev/quote/{id}
DELETE - https://peutgreo34.execute-api.eu-central-1.amazonaws.com/dev/quote/{id}
functions:
getQuotes: serverless-spring-boot-dev-getQuotes
getQuote: serverless-spring-boot-dev-getQuote
createQuote: serverless-spring-boot-dev-createQuote
updateQuote: serverless-spring-boot-dev-updateQuote
deleteQuote: serverless-spring-boot-dev-deleteQuote
layers:
None

You will see all Endpoint URLs and created lambda functions for your projects.

After deployment your API GW will be look like;

Destroy

Destroy is also easy as deployment step. Just you need to type “sls remove” and thats all

Further Topics

When you go Serverless, cold start-up times are really important for quick response on API GW. As you can imagine Spring Boot cold start time is not that fast. If you want to optimize it you can find more details on: https://github.com/awslabs/aws-serverless-java-container/wiki/Quick-start---Spring-Boot

Links

Feel free to play with it.

Enjoy :)

--

--