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:
$ 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. 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:
$ npm install -g serverless
Lambda Function
Now, let's create our lambda function. Create a folder for this project and then:
$ 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:
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:
$ 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:
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:
$ 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:
$ 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 to test Lambda functions locally. You can read more about the available tools at the AWS Serverless developer tools page.