AWS Lambda Functions in Scala
Initialize Scala Project
Assuming we are familiar with SBT and have that installed
$ sbt new scala/scala-seed.g8
A minimal Scala project.
name [Scala Seed Project]: Lambda Scala Seed
Template applied in /Users/vpanusuwan/projects/./lambda-scala-seed
Add Lambda Library
The libraries contains event classes which is a typed input for lambda. Check latest version at mvnrepository for java-core and java-events separately.
libraryDependencies ++= Seq(
"com.amazonaws" % "aws-lambda-java-core" % awsLambdaVersion,
"com.amazonaws" % "aws-lambda-java-events" % awsLambdaEventsVersion,
scalaTest % Test
)
Create handler function
Create a class with method of any name in any package. The important thing is that the method accepts a lambda event
package example
import com.amazonaws.services.lambda.runtime.Context
import com.amazonaws.services.lambda.runtime.events.{APIGatewayV2HTTPEvent, APIGatewayV2HTTPResponse}
class Main {
def handler(apiGatewayEvent: APIGatewayV2HTTPEvent, context: Context): APIGatewayV2HTTPResponse = {
println(s"body = ${apiGatewayEvent.getBody()}")
return APIGatewayV2HTTPResponse.builder()
.withStatusCode(200)
.withBody("okay")
.build()
}
}
Configure Assembly Plugin
Next is to configure the assembly plugin to produce a single fat jar containing our code and all the dependencies together. See latest version of the plugin at https://github.com/sbt/sbt-assembly
In project/plugins.sbt
addSbtPlugin("com.eed3si9n" % "sbt-assembly" % "0.15.0")
In build.sbt
, configure the fat jar merging strategies. At very minimum we should discard contents in META-INF
. Also overrides a jar name.
assemblyJarName in assembly := "lambda-scala-seed.jar"
assemblyMergeStrategy in assembly := {
case PathList("META-INF", xs @ _*) => MergeStrategy.discard
case x => MergeStrategy.first
}
Package
sbt assembly
Then finds the packaged jar at target/scala-2.13/<jarname>
Create Function
Install aws cli tool https://aws.amazon.com/cli/. Uses following commands to deploy / update the function.
First, Lambda needs a role to execute, which should at least have the AWSLambdaBasicExecutionRole
policy attached. The policy simply enables a write to CloudWatch logs. A cdk snipplet to create such role would be.
lambda_basic_policy = iam.ManagedPolicy.from_aws_managed_policy_name("service-role/AWSLambdaBasicExecutionRole")
role = iam.Role(self, "fn_lambda_role", role_name="fn_lambda_role", assumed_by=iam.ServicePrincipal("lambda.amazonaws.com"))
role.add_managed_policy(lambda_basic_policy)
Then we can deploy the jar to lambda
aws lambda create-function \
--function-name lambda-scala-seed \
--role "arn:aws:iam::<aws_account_no>:role/fn_lambda_role" \
--zip fileb://target/scala-2.13/lambda-scala-seed.jar \
--runtime java11 \
--memory 256 \
--handler "example.Main::handler"
Test Function
aws lambda invoke --function-name "lambda-scala-seed" /dev/stdout
Update Function
aws lambda update-function-code \
--function-name "lambda-scala-seed" \
--zip fileb://target/scala-2.13/lambda-scala-seed.jar
References
- https://aws.amazon.com/blogs/compute/writing-aws-lambda-functions-in-scala/
- Events Example – https://github.com/awsdocs/aws-lambda-developer-guide/tree/main/sample-apps/java-events