Running Go AWS Lambdas locally with SLS framework and SAM

In my previous post, I described an example of manually creating an AWS Lambda function implementing a GraphQL endpoint. Since I’m a big fan of the Serverless Framework, I wanted to write an example of using it to create a project using Go. Also, one of the comments on my previous post by Brian Bates asked about using SAM with Go. He wrote a post about this integration and I wanted to try the same with a SLS project.

SAM (Serverless Application Model)

SAM is a way to define serverless applications created by AWS, similar to CloudFormation. It too follows a very similar format to the serverless.yml file provided by SLS where you declare all your functions and resources they use, like they endpoints for API Gateway.

One of the big features provided by SAM is that you can run your serverless application locally, similar to what serverless-offline offers for Node projects. Being officially supported by AWS, SAM is a great way to have a developer environment if you use AWS offering of Serverless.

Requisites

In order to follow this example, you will need to install SAM in your computer, and have a working installation of Docker. You also need to have the Serverless framework and the Go SKD installed in your machine.

Also, since the SLS project for go uses dep for dependency management, you will need to install it too.

If you have NPM, you can install SAM with the following command:

npm install -g aws-sam-local

Creating the project

Let’s generate a Go-based project with SLS. Navigate to your GOPATH (the project needs to live inside Go’s path) and run the following command:

serverless create -t aws-go-dep -p go-taco-gallery

The name of my project is go-taco-gallery. This will generate a project inside GOPATH. Please notice that we used aws-go-dep as the template for our project, which means that this project will only work with AWS, just like SAM.

The project contains two function handlers: hello/main and world/main.

Since this is a Go project, you can build it running the make command:

make

This will generate a bin folder in the root of your project, equivalent to the dist folder generated for Node SLS applications.

Technically, you could use the SLS framework to deploy the application. However, we want to run it locally for development. So let’s enable SAM.

Changing our sample function for API Gateway

As generated by SLS, your main.go is only a Lambda function. If we want to serve it through the API Gateway, we need to modify it to have it return the appropriate type of data. Add the following code to hello/main.go:

package main
import (
"github.com/aws/aws-lambda-go/events"
"github.com/aws/aws-lambda-go/lambda"
)
type Response struct {
Message string `json:"message"`
}
func Handler(request events.APIGatewayProxyRequest) (events.APIGatewayProxyResponse, error) {
return events.APIGatewayProxyResponse{
Body: "Go Serverless v1.0! Your function executed successfully!",
StatusCode: 200,
}, nil
}
func main() {
lambda.Start(Handler)
}

Configuring SAM

Since we already installed SAM CLI, all we need is define a template.yml file at the root of your project. SAM will use this file to find the executables of your application, and define their required resources.

Create template.yml in your project’s root with the following content:

AWSTemplateFormatVersion : '2010-09-09'
Transform: AWS::Serverless-2016-10-31
Description: A hello world application.
Resources:
HelloWorldFunction:
Type: AWS::Serverless::Function
Properties:
Handler: bin/hello
Runtime: go1.x
Events:
Vote:
Type: Api
Properties:
Path: /
Method: get

The important parts here are the Handler and Runtime properties.

Handler must point to the compiled binary of your function handler. You can use the same handler property defined in serverless.yml. In our case, this is bin/hello as that’s were our binary was created. Don’t point it to the main.go file.

Runtime must be go1.x so SAM can interpret that our functions being written in Go.

Running the functions locally

We are now ready to run locally our functions. Just run the following command:

sam local start-api

If this is the first time you execute this command, it will take some time while it downloads the appropriate Docker images that SAM local needs to serve the application in your computer. At the end, you should see in your console an output similar to this:

Mounting bin/hello (go1.x) at http://127.0.0.1:3000/ [GET]
You can now browse to the above endpoints to invoke your functions.
You do not need to restart/reload SAM CLI while working on your functions,
changes will be reflected instantly/automatically. You only need to restart
SAM CLI if you update your AWS SAM template.

Now, navigate tohttp://127.0.0.1:3000/ and you should see the following message:

Go Serverless v1.0! Your function executed successfully!

If you decided to use AWS for your serverless app, SAM is a great option for having a local development environment.

Edit

After playing around with SAM for a while, I could find a small weakness: It doesn’t support creating resources like DynamoDB. Only Lambdas will be emulated. You can still start local servers for databases or other services, but this is a big disadvantage against other services like serverless-offline which integrate out of the box with services like serverless-dynamodb-local.