Creating a Serverless Greetings Function with AWS and Spring Boot
Written on
Chapter 1: Introduction
In this guide, we will develop a serverless AWS Lambda function designed to return a greeting message. When provided with a name, the function will respond with "Hello <name>!!!". We’ll create a Spring Boot application called Greetings Lambda Function, leveraging Spring Cloud Function and the AWS Adapter to prepare the application for AWS Lambda deployment.
To facilitate testing, we will generate a Lambda Function URL that can be accessed using curl. For a smoother development experience, we will utilize LocalStack to simulate essential AWS services on our local environment.
Project Overview
So, let’s dive in!
Prerequisites
Before starting, ensure that you have Java 17+, Docker, and jq installed on your machine.
Creating the Greetings Lambda Function
We will initiate a Spring Boot application using Spring Initializr, naming it greetings-lambda-function without any additional dependencies for now. We will incorporate those later. We will use Spring Boot version 3.2.4 and Java 17. You can find the setup instructions linked here.
After clicking the GENERATE button, download the zip file, extract it to your preferred directory, and open the greetings-lambda-function project in your IDE.
Updating the pom.xml File
Next, let's modify the pom.xml to include the necessary dependencies. Below is the updated configuration:
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>3.2.4</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.example</groupId>
<artifactId>greetings-lambda-function</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>greetings-lambda-function</name>
<description>Demo project for Spring Boot</description>
<properties>
<java.version>17</java.version>
<spring-cloud.version>2023.0.0</spring-cloud.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-function-adapter-aws</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>${spring-cloud.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-deploy-plugin</artifactId>
<configuration>
<skip>true</skip></configuration>
</plugin>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<configuration>
<layout>thin</layout></configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-shade-plugin</artifactId>
<executions>
<execution>
<phase>package</phase>
<goals>
<goal>shade</goal></goals>
<configuration>
<outputFile>${project.build.directory}/${project.artifactId}-${project.version}-aws.jar</outputFile></configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>
</project>
We have introduced new properties to define the versions for Spring Cloud and the Spring Boot Thin Layout. A dependency for the Spring Cloud Function adapter for AWS Lambda has been added. This setup will allow us to manage dependencies more effectively.
Creating the Handler Package
To maintain an organized code structure, let’s create a handler package within the com.example.greetingslambdafunction root package.
Creating the Handler Class
In the handler package, we will create the GreetingsHandler class, which will contain the logic to process incoming messages and return formatted greetings.
package com.example.greetingslambdafunction.handler;
import java.util.function.Function;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.messaging.Message;
@Configuration
public class GreetingsHandler {
private static final Logger log = LoggerFactory.getLogger(GreetingsHandler.class);
@Bean
public Function<Message<String>, String> greetings() {
return message -> {
log.info("==> Headers: {}", message.getHeaders());
log.info("==> Payload: {}", message.getPayload());
return String.format("Hello %s!!!", message.getPayload());
};
}
}
The GreetingsHandler class is a configuration class that defines a function bean to process incoming messages, logging the headers and payload while returning a greeting message.
Creating the JAR Package Script
In the root directory of greetings-lambda-function, create the jar-package.sh script with the following content:
#!/usr/bin/env bash
./mvnw clean package -DskipTests
mkdir -p shared
cp target/greetings-lambda-function-java17-aws.jar shared
This script uses Maven to clean, compile, and package the project while skipping tests. It then creates a shared directory and copies the generated JAR file into it.
chmod +x jar-package.sh
Creating Docker Compose File
In the root directory of greetings-lambda-function, create the docker-compose.yml file with the following content:
version: "3.8"
services:
localstack:
container_name: localstack
image: localstack/localstack:3.2.0
ports:
- "127.0.0.1:4510-4559:4510-4559"
- "127.0.0.1:4566:4566"
environment:
- LOCALSTACK_HOSTNAME=localhost.localstack.cloud
- AWS_ACCESS_KEY_ID=key
- AWS_SECRET_ACCESS_KEY=secret
- AWS_DEFAULT_REGION=eu-west-1
- SERVICES=lambda
- DEBUG=${DEBUG-}
volumes:
- "$PWD/tmp/localstack:/var/lib/localstack"
- "$PWD/shared:/shared"
- "/var/run/docker.sock:/var/run/docker.sock"
networks:
default:
aliases:
- localhost.localstack.cloud
This file is based on LocalStack's official documentation, configuring Lambda as a service and mapping necessary volumes for shared access.
Creating LocalStack Initialization Script
In the greetings-lambda-function root folder, create the init-localstack.sh script with the following content:
#!/usr/bin/env bash
if ! [[ $(docker ps -q -f name=localstack) ]]; then
echo "WARNING: The localstack Docker container is not running. Please, start it first."
exit 1
fi
echo "Initializing LocalStack"
echo "======================="
echo "Creating Lambda Function called GreetingsLambdaFunction"
docker exec -t localstack aws --endpoint-url=http://localhost:4566 lambda create-function
--function-name GreetingsLambdaFunction
—runtime java17
--memory-size 512
—handler org.springframework.cloud.function.adapter.aws.FunctionInvoker::handleRequest
--zip-file fileb:///shared/greetings-lambda-function-java17-aws.jar
—environment "Variables={AWS_REGION=eu-west-1,AWS_ACCESS_KEY_ID=key,AWS_SECRET_ACCESS_KEY=secret}"
--role arn:aws:iam::000000000000:role/service-role/irrelevant
—timeout 60
echo "Creating a Function URL to GreetingsLambdaFunction"
docker exec -t localstack aws --endpoint-url=http://localhost:4566 lambda create-function-url-config
--function-name GreetingsLambdaFunction
—auth-type NONE
echo "LocalStack initialized successfully"
This script initializes LocalStack and creates the Lambda function, setting up the necessary configurations.
chmod +x init-localstack.sh
Packaging the Greetings Lambda Function JAR
Ensure you are in the greetings-lambda-function root folder. Run the jar-package.sh script:
./jar-package.sh
The greetings-lambda-function-java17-aws.jar file should now be present in the shared folder.
Starting the Environment
In a terminal, navigate to the greetings-lambda-function root folder and run:
DEBUG=1 docker compose up -d
Then, execute the init-localstack.sh script:
./init-localstack.sh
If everything is set up correctly, you should see confirmation messages in the terminal regarding the creation of the Lambda function and its URL.
Testing Endpoints
To interact with the Greetings Lambda function endpoint, retrieve the Function URL using the AWS CLI:
GREETINGS_LAMBDA_FUNCTION_URL=$(docker exec -t localstack aws --endpoint-url=http://localhost:4566 lambda get-function-url-config --function-name GreetingsLambdaFunction | jq -r '.FunctionUrl')
echo $GREETINGS_LAMBDA_FUNCTION_URL
You can then send a greeting request using curl:
curl -i -X POST $GREETINGS_LAMBDA_FUNCTION_URL
-H 'Content-Type: application/json'
-d 'Ivan'
The expected response should be:
HTTP/1.1 200
...
"Hello Ivan!!!"
Note that the first request may take some time as Docker initializes the container for the Greetings Lambda Function.
Shutdown
To stop LocalStack and the Greetings Lambda Function Docker containers, run:
docker compose down -v
Conclusion
In this article, we successfully built a serverless AWS Lambda function for greetings. We developed a Spring Boot application named Greetings Lambda Function, adapted it for AWS Lambda using Spring Cloud Function and the AWS Adapter, and deployed it to AWS LocalStack. Finally, we tested the endpoint and received a personalized greeting.
Additional Readings
Support and Engagement
If you found this article helpful, consider the following actions:
👏 Engage by clapping, highlighting, and replying to my story. I'm happy to answer your questions.
🌐 Share this article on social media.
🔔 Follow me on: Medium | LinkedIn | Twitter | GitHub.
✉️ Subscribe to my newsletter to stay updated with my latest posts.
Chapter 2: Videos and More Resources
This video provides an overview of using Spring Cloud Function on AWS Lambda with API Gateway, which aligns with the concepts discussed in this guide.
In this video, learn how to deploy serverless functions across various platforms using Spring Cloud Function, expanding upon the topics we've covered.