Development Environment for AWS Lambda and Go

When it comes to developing serverless applications, we have many options in terms of the workflow we can adopt in the process, as well as the tools to support it.

Today, I will present a sample development environment for a very simple API developed in Golang for the AWS Lambda.

# Requirements

* python (both Python 2.x and 3.x supported)
* pip
* Docker
* Node
* Golang 1.x

# Localstack

Having our requirements in place, let's first install and start [LocalStack](https://github.com/localstack/localstack):

```bash
$ pip install localstack
$ localstack start
```

On a mac, you may need to run the command `TMPDIR=/private$TMPDIR localstack start --docker` if `$TMPDIR` contains a symbolic link that cannot be mounted by Docker.

This is a handy tool that provides us with a local AWS cloud stack, so this will allow us to test API Gateway/Lambda calls locally.

Because LocalStack mocks many other AWS services, our functions could also interact with a DynamoDB table or a SQS queue locally with this single tool.

# Serverless Framework

Next, we are going to install the [Serverless Framework](https://github.com/serverless/serverless). This is a set of tools to support the development of serverless applications. In this case, it will be used to automate the deployment process. It is extremely convenient as it automatically creates all the resources that are needed by our API to run.

This tool supports other providers, such as Google Cloud Platform and Azure. When we work with AWS, it automates the deployment process by creating a CloudFormation stack based on our application and configuration.

Ultimately, we can use the Serverless Framework to deploy our API to AWS, but this will not be covered in this article. Instead, we are going to target our LocalStack installation to test our functions locally. What is interesting about this approach is that we have a very similar process when working locally, compared to what we do when we want to deploy our application to AWS.

To install the Serverless Framework, we just need to:

```bash
$ npm install -g serverless
```

# Lambda Function

Now, let's create our lambda function. Create a folder for this project and then:

```bash
$ go mod init hello-lambda
$ go get github.com/aws/aws-lambda-go
```

This will initialize the Go module and install the necessary library. Next, create a `main.go` file with our lambda function:

```golang
package main

import (
	"context"
	"encoding/json"
	"time"

	"github.com/aws/aws-lambda-go/events"
	"github.com/aws/aws-lambda-go/lambda"
)

type HelloLambdaResponse struct {
	Greeting    string    `json:"greeting"`
	CurrentTime time.Time `json:"current_time"`
}

func handle(ctx context.Context, request events.APIGatewayProxyRequest) (events.APIGatewayProxyResponse, error) {
	resp := &HelloLambdaResponse{
		Greeting:    "Hello World!",
		CurrentTime: time.Now().UTC(),
	}

	responseBody, err := json.Marshal(resp)
	if err != nil {
		return events.APIGatewayProxyResponse{}, err
	}

	return events.APIGatewayProxyResponse{
		StatusCode: 200,
		Headers: map[string]string{
			"Content-Type": "application/json",
		},
		Body: string(responseBody),
	}, nil

}

func main() {
	lambda.Start(handle)
}
```

# Deployment

We can now deploy our function to LocalStack. Let's first, install the plugin that will make the Serverless Framework target the LocalStack installation instead of the cloud:

```bash
$ npm install --save-dev serverless-localstack
```

Now, let's create a `serverless.yml` config file with the instruction on how the deployment has to be executed:

```yml
service: hello-lambda
plugins:
  - serverless-localstack
provider:
    name: aws
    runtime: go1.x
    stage: ${opt:stage, 'local'}
custom:
  localstack:
    debug: true
    stages:
      # list of stages for which the plugin should be enabled
      - local
package:
  exclude:
    - ./**
  include:
    - ./bin/**
functions:
  hello-lambda:
    handler: bin/hello-lambda
    events:
      - http:
          path: hello
          method: get
```

In this file, we are describing our functions below the `functions:` key, specifying that a GET method in the `hello` path will invoke the function we created in the previous step.

We are also specifying that the LocalStack environment has to be targeted when deploying to the `local` stage with the configuration under the `custom:` key.

Now, let's compile our code and run the deployment:

```bash
$ GOOS=linux GOARCH=amd64 go build -o bin/hello-lambda .
$ serverless deploy --stage local
```

If there are no errors, you should see a `Service Information` section like below, describing our function:

```
Service Information
service: hello-lambda
stage: local
region: us-east-1
stack: hello-lambda-local
resources: 11
api keys:
  None
endpoints:
  GET - http://localhost:4567/restapis/onw2twhxmr/local/_user_request_/hello
functions:
  hello-lambda: hello-lambda-local-hello-lambda
layers:
  None
Serverless: Run the "serverless" command to setup monitoring, troubleshooting and testing.
```

Now, it should be possible to call the function through the endpoint above:

```bash
$ curl http://localhost:4567/restapis/onw2twhxmr/local/_user_request_/hello
{"greeting":"Hello World!","current_time":"2019-12-16T14:44:35.086894535Z"}
```

# Conclusion

This was an example on how to work locally with AWS Lambda functions, but definitely not the only approach. I'm still experimenting the available solutions myself, but an obvious alternative would to use the [AWS SAM Local](https://github.com/awslabs/aws-sam-cli) to test Lambda functions locally. You can read more about the available tools at the [AWS Serverless developer tools](https://aws.amazon.com/serverless/developer-tools/) page.