Golang with AWS Lambda

Golang with AWS Lambda

I have been using Golang on production environment for a few years now, the language is awesome and I absolutely love it. Recently, my team started adapting the serverless framework to our platform, we are using the industry standard AWS Lambda and Golang is still my language of choice.

The advantages of AWS Lambda over the traditional virtual machine are cost saving and no-brainer infrastructure maintenance. Basically, the idea is to break down server-side applications to small modules and run them as functions, AWS will take care of the heavy work on infrastructure for us, including scaling, monioring, logging, etc. No server, no docker required after giving the function configurations to the Lambda. AWS has a decent set of materials to get things up and running. I was able to build my first function within just a few minutes after reading the docs. But, here is when the problem comes.

AWS Lambda is meant for small functions and Golang was born for big, complicated and scalable applications. As a result, my very first issue I encountered when I was implementing a medium-size project with AWS Lambda in Go is I wasn't able to implement multiple functions in 1 Go package. The reason is that AWS Lambda requires a main function for every lambda function, and in one Go package we can only have 1 main function. It was pretty frustrating for me at first since this doesn't really make sense at all to write a Lambda function in Go.

After reading for a while, I found two solutions

  1. Invoke a function with the AWS SDK.
  2. Call a function via the API gateway.

both have advantages and disadvantages, how we call it will depends on how we structure we entire stack. Here I list them all.

1. Invoke a function with AWS SDK

The idea for the first solution is to write a wrapper for the start function, provided by the Lambda library for Go. When a request comes, it will come with a AWS_LAMBDA_FUNCTION_NAME environment variable and we can parse it to initialize the responding function. Here is the example

func start(handler ...interface{}) {
	Func := os.Getenv('AWS_LAMBDA_FUNCTION_NAME')
    switch FuncN {
	case "func_a":
		lambda.Start(handler[0].(func(req Request) (Output, error)))
	case "func_b":
		lambda.Start(handler[1].(func(req Request) error))
	default:
		lambda.Start(handler[2].(func() error))
	}
}

func main() {
	start(FuncAHandler, FuncBHandler, ErrorHandler)
}

we can have as many functions as we want within that main function. Now we can invoke our functions as we would normally do.

Call a function via AWS API gateway

The solution for this will make thing a bit simpler but we will lose the good side of invoking functions via the AWS SDK. The idea is to establish a function with the AWS API gateway and using mux to route the request. The main looks like  so

import (
	"github.com/aws/aws-lambda-go/events"
	"github.com/aws/aws-lambda-go/lambda"

	"github.com/awslabs/aws-lambda-go-api-proxy/gorillamux"
	"github.com/gorilla/mux"
)

func Handler(req events.APIGatewayProxyRequest) (events.APIGatewayProxyResponse, error) {
	r := mux.NewRouter()
	r.HandleFunc("/endpoint1", EndPoint1Handler).Methods("POST")
	r.HandleFunc("/endpoint2", EndPoint2Handler).Methods("GET")

	adapter := gorillamux.New(r)
	res, err := adapter.Proxy(req)
	if err != nil {
		LogError.Println(err.Error())
	}
	return res, err
}

func main() {
	lambda.Start(Handler)
}

Now, we can call the function with the endpoints that AWS provided when we deploy the package. With this method, we don't need to have many functions, a function can serve for multiple endpoints depending how big the project is. However, we kind of break the fundamental idea of Lambda if we implement too many endpoints into this one function.

AWS Lambda with serverless framework is a promising technology, but I think the Go support is still limited. Happy coding everyone!