Lambda Go Starter Project

Lambda Go Starter Project

Serverless development allows deploying low-cost, low-maintenance applications. Go is an ever-more popular language for developing backend applications. With first rate support both in AWS Lambda and GCP Cloud Functions, Go is an excellent choice for serverless development.

Unfortunately, setting up a flexible infrastructure and efficient deployment pipelines for Serverless applications is not always easy. Especially if we are interested in not just deploying a function but exposing it through HTTP for other services to use. This post describes the key elements of a little starter project for deploying a Go Lambda that exposes a simple, extensible REST API.

This project uses the following technologies:

  • Go 1.16
  • AWS Lambda
  • AWS API Gateway
  • Terraform for defining all infrastructure
  • Goldstack Template Framework for deployment orchestration (note this is based on Node.js/Yarn)

The source code for the project is available on GitHub: go-lambda-starter-project

The live API is deployed here: API root

To quickly create a customised starter project like this one: Go Gin Lambda Template on Goldstack

Go Project Setup

To setup a new Go project is very straightforward and can be achieved using the go mod init command. This results in the creation of a go.mod file. This file helps manage all dependencies for the project. For our project, we have also added a number of dependencies that are viewable in this file:

This is the resulting go.mod file for the project:

module goldstack.party/templates/lambda-go-gin

go 1.16

require (
    github.com/aws/aws-lambda-go v1.23.0
    github.com/awslabs/aws-lambda-go-api-proxy v0.9.0
    github.com/gin-contrib/cors v1.3.1
    github.com/gin-gonic/gin v1.6.3
)

Server Implementation

The HTTP server deployed through the Lambda is defined in a couple of Go files.

main.go

main.go is the file that is run when the Lambda is invoked. It also supports being invoked locally for easy testing of the Lambda. When run locally, it will start a server equivalent to the Lambda on localhost.

package main

import (
    "os"

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

func main() {
    // when no 'PORT' environment variable defined, process lambda
    if os.Getenv("PORT") == "" {
        lambda.Start(Handler)
        return
    }
    // otherwise start a local server
    StartLocal()
}

server.go

The server.go file is where the HTTP server and its routes are defined. In this example, it just provides one endpoint /status that will return a hard-coded JSON. This file also configures the CORS config, in case we want to call the API from a frontend application hosted on a different domain.

package main

import (
    "os"

    "github.com/gin-contrib/cors"
    "github.com/gin-gonic/gin"
)

func CreateServer() *gin.Engine {
    r := gin.Default()

    corsEnv := os.Getenv("CORS")
    if corsEnv != "" {
        config := cors.DefaultConfig()
        config.AllowOrigins = []string{corsEnv}
        r.Use(cors.New(config))
    }
    r.GET("/status", func(c *gin.Context) {
        c.JSON(200, gin.H{
            "status": "ok",
        })
    })
    return r
}

Infrastructure

The infrastructure for this starter project is defined using Terraform. There are a couple of things we need to configure to get this project running, these are:

  • Route 53 Mappings for the domain we want to deploy the API to as well as a SSL certificate for being able to call the API via HTTPS: domain.tf
  • An API Gateway for exposing our Lambda through a public endpoint: api_gateway.tf
  • The definition of the Lambda function that we will deploy our code into: lambda.tf

The details of the infrastructure are configured in a config file: goldstack.json.

{
  "$schema": "./schemas/package.schema.json",
  "name": "lambda-go-gin",
  "template": "lambda-go-gin",
  "templateVersion": "0.1.1",
  "configuration": {},
  "deployments": [
    {
      "name": "dev",
      "configuration": {
        "lambdaName": "go-gin-starter-project",
        "apiDomain": "go-gin-starter-project.dev.goldstack.party",
        "hostedZoneDomain": "dev.goldstack.party"
      },
      "awsUser": "awsUser",
      "awsRegion": "us-west-2"
    }
  ]
}

The configuration options are documented in the Goldstack documentation as well as in the project readme. Note that these configuration options can be created using the Goldstack project builder or manually in the JSON file.

The infrastructure can easily be stood up by using a Yarn script:

yarn
cd packages/lambda-go-gin
yarn infra up dev

dev denotes here for which environment we want to stand up the infrastructure. Currently the project only contains one environment, dev, but it is easy to define (and stand up) other ones by defining them in the goldstack.json file quoted above.

Note it may seem unconventional here to use a Yarn script, and ultimately an npm module, to deploy the infrastructure for our Go lambda. However, using a scripting language to support the build and deployment of a compiled language is nothing unusual, and using Yarn allows us to use one language/framework for managing more complex projects that also involve frontend modules defined in React.

Deployment

Deployment like standing up the infrastructure can easily be achieved with a Yarn script referencing the environment we want to deploy to:

yarn deploy dev

This will build our Go project, package and zip it as well as upload it to AWS. The credentials for the upload need to be defined in a file config/infra/aws/config.json, with contents such as the following:

{
  "users": [
    {
      "name": "awsUser",
      "type": "apiKey",
      "config": {
        "awsAccessKeyId": "[Access Key Id]",
        "awsSecretAccessKey": "[Secret Access Key]",
        "awsDefaultRegion": "us-west-2"
      }
    }
  ]
}

A guide how to obtain these credentials is available on the Goldstack documentation. It is also possible to provide these credentials in environment variables, which can be useful for CI/CD.

Development

To adapt this starter project for your requirements, you will need to do the following:

  • Provide a config file with AWS credentials (or environment variables with the same)
  • Update packages/lambda-go-gin/goldstack.json with the infrastructure definitions for your project
  • Initialise the Yarn project with yarn
  • Deploy infrastructure using cd packages/lambda-go-gin; yarn infra up dev
  • Deploy the lambda using cd packages/lambda-go-gin; yarn deploy dev
  • Start developing your server code in packages/lambda-go-gin/server.go

This project also contains a script for local development. Simply run it with

cd packages/lambda-go-gin
yarn watch

This will spin up a local Gin server on http://localhost:8084 that will provide the same API that is exposed via API gateway and makes for easy local testing of the Lambda. Also deployment of the Lambda should only take a few seconds and can be triggered anytime using yarn deploy dev.

If you want to skip the steps listed above to configure the project, you can generate a customised project with the Goldstack project builder.

This template is just a very basic Go project to be deployed to Lambda and actually my first foray into Go development. I haven't based any larger projects on this yet to test it out, so any feedback to improve the template is welcome. I will also be keep on updating the template on Goldstack with any future learnings.