AWS Lambda + Kotlin
Tags: kotlin, aws, serverless, programming
Published: 2023-01-26 11:54:15

## Run AWS Lambda with Kotlin

Lambda is a buzz word nowadays, for good reasons: simple, fast and pay as you go.
Majority of lambda functions examples are written in TypeScript and, for the JVM side, Java only. That's why I'm creating this example, to anyone interested in running a Kotlin function on AWS.
I'll be using the AWS CLI only, not the AWS online console.

## Create the kotlin project – gradle

First of all we need to create our kotlin project. We'll use Gradle to do it, but translate it to Maven should be trivial.
Create a folder wherever you want, navigate to it in the command shell and execute the following:

`gradle init`

After that, select the following options:

- Select type of project to generate: `library`
-     Select implementation language: `Kotlin`
-     Select build script DSL: `Groovy`
-     Generate build using new APIs and behavior 
(some features may change in the next minor release): `no`

## Dependencies

To run our function on AWS we need to add some dependencies to our project.

`implementation 'com.amazonaws:aws-lambda-java-core:1.2.2'`

Apart from AWS specific, we'll need a fat jar, so we need to add a **plugin** to generate it:

`id "com.github.johnrengelman.shadow" version "7.1.2"`

That's all we need to have our proof of concept.

## Create the actual Kotlin function

> I'm using IntelliJ IDE

Our Lambda function will be very simple:
Implement an echo function, i.e., it will return the received input.

Let's create our main Class. I'll create the package `com.ruimptech.echo` inside `src/main/kotlin`. Inside that package I'll create the main kotlin class, named `App`.

![Project structure](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/6rflo93lw0gpc6ddrpuz.png)

In order to run in AWS, our main class needs to implement the `RequestHandler` interface.

![Implementing RequestHandler interface](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/pottxa4c5lz1nhfmok97.png)

As you can see in the image, there's an error, that's because the RequestHandler interface requires to specify the input and output data type. To fix that, we'll create a class for input and another for output and use them to fix the interface error.

### Input class

```
package com.ruimptech.echo

class EchoIn {
    var content: String = ""
}
```
Receives a simple content String.
Example: `{ "content": "hello" }`

### Output class

```
package com.ruimptech.echo

data class EchoOut(val content: String)
```
Very similar to the input class, contains the output content String, that in our proof of concept will be the received input.

Note that our `EchoIn` class is **not** a data class, that's because a data class will not work on AWS. If we use a data class as the input, lambda execution will terminate immediately due to a initialization error.

Now that we have input and output classes, we can fix our lambda function (App class).

```
package com.ruimptech.echo

import com.amazonaws.services.lambda.runtime.Context
import com.amazonaws.services.lambda.runtime.LambdaLogger
import com.amazonaws.services.lambda.runtime.RequestHandler

class App : RequestHandler<EchoIn, EchoOut> {
    override fun handleRequest(input: EchoIn, context: Context): EchoOut {
        val logger: LambdaLogger = context.logger
        logger.log("ECHO INPUT RECEIVED: " + input.content)
        return EchoOut("Echo: ${input.content}")
    }
}
```
At this point, the function is ready! Let's go over what is going on:

1. `RequestHandler<EchoIn, EchoOut>`
  - We say to our lambda function that EchoIn and EchoOut are the input and output data types.
3. `override fun handleRequest`
  - The actual code to be run on each incoming request
  - As mentioned before, the function is very simple. We print a log with the received input and return it inside a EchoOut instance.

## Create a fat jar

The *com.github.johnrengelman.shadow* plugin allow us to create a fat jar with a simple command:
`./gradlew shadowJar`
As a result, we'll find a file named **lib-all.jar** inside the `build/libs` directory, that's our fat jar.

## Deploy the lambda function

We have everything in place in our computer, now let's send it to AWS.
As any AWS deploy, we need a template to specify what we want to do. We'll use an existing AWS template (you can find the original version [here](https://github.com/awsdocs/aws-lambda-developer-guide/blob/main/sample-apps/java-basic/template.yml)) and tweak some properties:

```
AWSTemplateFormatVersion: '2010-09-09'
Transform: 'AWS::Serverless-2016-10-31'
Description: Kotlin Lambda Function
Resources:
  function:
    Type: AWS::Serverless::Function
    Properties:
      CodeUri: build/libs/lib-all.jar
      Handler: com.ruimptech.echo.App::handleRequest
      Description: Kotlin function
      Runtime: java11
      MemorySize: 512
      Timeout: 10
      # Function's execution role
      Policies:
        - AWSLambdaBasicExecutionRole
        - AWSLambda_ReadOnlyAccess
        - AWSXrayWriteOnlyAccess
        - AWSLambdaVPCAccessExecutionRole
      Tracing: Active
```
The main changed parameters are:
- CodeUri
  - Where is the fat jar to send to AWS
- Handler
  - What is the function to run
- Description

Save this template in a file named `template.yml`, place it in the lib folder of the project:

![Lambda template file location](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/scfc221zir7l77lggzev.png)

> From that point on, I assume you have an AWS account and the CLI is correctly configured in your computer. If not, please check [this](https://docs.aws.amazon.com/cli/latest/userguide/getting-started-install.html) out

Now, to deploy the function we need 3 steps:

1. Create a S3 bucket where the final template will be stored (the template.yml file we just created is a template to generate the final template, don't worry too much about it)
2. Generate the final template and save it on the bucket created before
3. Deploy the final template

These 3 steps are directly converted to 3 CLI commands:

1. `aws s3 mb s3://lambda-kotlin`
2. `aws cloudformation package –template-file template.yml –s3-bucket lambda-kotlin –output-template-file template-out.yml`
  - Based on our template.yml, the CLI will generate the final template - template-out.yml - that we can use in the final step
3. `aws cloudformation deploy –template-file template-out.yml –stack-name lambda-kotlin –capabilities CAPABILITY_NAMED_IAM`
  - Use the final template to deploy the lambda function

That' it! Our Kotlin Lambda function is on AWS!

## Testing
Now that we have our lambda online, we can test it. To do it we need to know the name of our function. The simplest way to find it is going into AWS console and copy the name:

![Finding lambda function name](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/r867kfyc3ug7gquedco3.png)

Now that we have our function name, let's call it from the CLI.

> In order to run our test, we need to enable the literal input, that means we'll pass the payload from the command line. To enable it, open the CLI config file:
> MacOS and Linux: `~/.aws/config`
> Windows: `%USERPROFILE%\.aws\config`
> Add the line:
> `cli_binary_format = raw-in-base64-out`
> If you want more information, check it [here](https://docs.aws.amazon.com/cli/latest/userguide/cli-configure-files.html#cli-config-cli_binary_format)

The payload needs to match our EchoIn class, so to test the lambda we need to run something like:

```
aws lambda invoke \
  --function-name lambda-kotlin-function-R4M2Bf9fxxBf \
  --payload '{ "content": "Hey!" }' \
  out.json
```

The content of the response will be stored in `out.json` file and the content will be:

`{"content":"Echo: Hey!"}`

That's it, you just deployed and run a Kotlin Lambda function on AWS!

You can find the code on [GitHub](https://github.com/rmpt/aws-lambda-kotlin).