Going Serverless with Spring Function AWS Lambda and DynamoDB

26 Jan, 2021 | 6 minutes read

Spring cloud functions and Serverless give us the ability to easily develop, test, build, and deploy Lambda functions on multiple cloud providers. In the following blog, we will demonstrate how to create lambda function using Spring cloud functions, build AWS infrastructure, deploy the application on AWS save data into Dynamo DB.

More specifically, we will review the following things:

Before we jump to the demo, let’s remind ourselves of serverless architecture, what is it, and serverless framework.

Serverless architecture

Serverless architecture (function as a service, FaaS) is a software design pattern where applications are hosted by a third-party service, eliminating the need for setting up server or hardware management by the developers. This does not mean that we run the applications without servers. (www.twilio.com)

What ‘serverless’ really means is that as a developer you don’t have to think about servers. You just focus on code. An example of this is AWS Lambda.

Key points of serverless

  • Zero administration
  • Auto-scaling
  • Pay-per-use
  • Increased velocity

Serverless framework

Serverless framework helps us to quickly and easily build the infrastructure and deploy our Lambda function in the cloud, in our case we will use AWS as a cloud provider.

All the infrastructure configurations are defined in the serverless.yml file placed in the root of the project, when we execute the “serverless deploy” command, the framework looks for the serverless.yml file, follows the configurations and instructions defined.

Spring Cloud function

Spring Cloud functions projects offer us the ability to easy build components that can be deploy and run on AWS Lambda, Azure Functions, Apache Openwhisk by providing different adapter for each cloud provider, also there is an adapter where you can test the function locally.

Writing the Spring Cloud Function

FunctionConfiguration class

The most important class in our project is Configuration class called “FunctionConfiguration”. This is the place where we defined a Bean called createStudent that defines our lambda function. Full code of the configuration class can be found on Github.

There are multiple ways how we can trigger one function, in our example, we are using the AWS API Gateway request to trigger the function, this event is defined under APIGatewayProxyRequestEvent class.

Sample code of the Definition of the function with comments:

123456789101112131415161718192021222324252627282930313233@Configuration    public class FunctionConfiguration {    private static Logger logger = LoggerFactory.getLogger(FunctionConfiguration.class);    @Autowired    StudentService studentService;        @Bean    public Function<APIGatewayProxyRequestEvent, APIGatewayProxyResponseEvent> createStudent() {        logger.info("Execute Lambda createStudent");          // value variable is APIGatewayProxyRequestEvent, this object represents the           // AWS API Gateway request that triggers the lambda function, from this object           // we are getting the payload send as a body        return value -> {            try {                ObjectMapper mapper = new ObjectMapper();                                  // student mapper function is getting the POST request body from the                                   // value object and converts into pojo class                  StudentPojo studentPojo = Utils.studentMapper(value, mapper);                                  // next step is to save the studentPojo into Dynamo DB,                                   //by calling the saveStudent function                Student student = studentService.saveStudent(studentPojo);                                  // last step is to create the response object                                   // that the lambda function will return back                 return createResponseEvent(student);            } catch (Exception e) {                logger.error("Error executing createStudent function", e);                e.printStackTrace();                return new APIGatewayProxyResponseEvent().withStatusCode(500);            }        };    }

application.property

This is how the application.property file looks like with default values. Before we compile and build our application, we have to fill up the amazon.aws.accesskey and amazon.aws.secretkey properties with the correct values that we get from our AWS account.

1234spring.main.banner-mode=offamazon.aws.accesskey=amazon.aws.secretkey=

Maven dependencies (list of the most important dependencies related to AWS and Lambda)

  • Local adapter “spring-cloud-starter-function-web” (see sample code below) module has autoconfiguration that activates when it is included in a Spring Boot web application, this will act as our local adapter and brings in the necessary dependencies to run our function locally as a spring boot application or we are trying to create junit tests (scope test).
    If we want to test the function locally, remove the test scope from the “spring-cloud-starter-function-web” and comment out the “spring-cloud-function-adapter-aws” dependency then run the project as a Java application using the Application.class, the project will start and you can access the function locally.
<dependency>        <groupId>org.springframework.cloud</groupId>        <artifactId>spring-cloud-starter-function-web</artifactId>        <version>${spring-cloud-function.version}</version>    </dependency>
  • AWS adapter “spring-cloud-function-adapter-aws” dependency adapter takes a Spring Cloud Function app and converts it to a form that can run in AWS Lambda. Without this adapter the function will not work on AWS.
<dependency>        <groupId>org.springframework.cloud</groupId>        <artifactId>spring-cloud-function-adapter-aws</artifactId>        <version>${spring-cloud-function.version}</version>    </dependency>
  • AWS dependencies to handle Lambda events
<dependency>            <groupId>com.amazonaws</groupId>            <artifactId>aws-lambda-java-core</artifactId>            <version>${aws-lambda-java-core.version}</version>        </dependency>        <dependency>            <groupId>com.amazonaws</groupId>            <artifactId>aws-lambda-java-events</artifactId>            <version>${aws-lambda-java-events.version}</version>        </dependency>

Setup the project in your local environment, deploy on AWS, and trigger a lambda function

1. Install Serverless please follow the install instructions https://www.serverless.com/framework/docs/providers/aws/guide/installation/
2. Get the project is from GitHub, use this command to clone the repository “git clone https://github.com/popovski/spring-cloud-function-aws.git”
3. Get amazon accesskey from your AWS account, set the value in “/iw-aws-lambda-blog/src/main/resources/application.properties -> amazon.aws.accesskey”
4. Get amazon secretkey from your AWS account, set the value in “/iw-aws-lambda-blog/src/main/resources/application.properties -> amazon.aws.secretkey”
5. In the root of the project run “.\mvnw clean package”, this will compile the code and build the jar file, this jar file is then used in the serverless.yml file and deployed on AWS as a Lambda Function
6. Create AWS environment and deploy the application on AWS use the command
“serverless deploy –region us-east-1 –aws-profile YOUR_PROFILE”
a) Serverless framework will look for serverless.yml configuration file and based on this configuration will start creating the AWS Infrastructure and deploy the jar file on AWS
b) After the build and deployment process finish, the serverless framework will return deployment status and POST rest endpoint URL of the AWS API Gateway, the example below (picture 2)

Picture 2

7. Testing – trigger the Lambda function
a) In order to trigger the lambda function, use the POST URL (picture 2) of the AWS API Gateway that serverless returned and make a HTTP post request using http client (example Postman).
b) As a body in use json payload example { “firstName”:”Nikola”, “lastName”:”Popovski” } (Picture 3)

Picture 3

8. Verify the results – a new record should be created in the Dynamo DB table student
a) Login into AWS console and open Dynamo DB dashboard (picture 4), open table student, new records should be created in the table

Picture 4

Running the Junit tests

  • Run the command “.\mvnw test”
  • The Junit test will start the function locally, execute http post request with json payload and trigger the function,
  • The function then will return a result back that is validated in the junit.

Removing the AWS infrastructure that we just build

“serverless remove –region us-east-1 –aws-profile YOUR_PROFILE”

Using this command serverless will remove everything we just build and deployed on AWS. Use this when you want to start over or in case of some error.

Conclusion

Spring cloud functions and Serverless give us the ability to easy develop, test, build and deploy lambda functions on multiple cloud providers. The only drawback from this setup is that the lambda function warm-up time is a couple of seconds. There are some tips and tricks on how to lower down the warm-up time which will be covered in some other post.

The full functional project described in this post can be found on github repository.