Deploy Serverless Golang Functions with Netlify
Tags: jamstack, go, gatsby, softwaredevelopment
Published: 2020-08-03 13:09:49

---
title: Deploy Serverless Golang Functions with Netlify
published: true
date: 2020-08-03 09:09:00 EDT
tags: JAMStack,Golang,GatsbyJS,SoftwareDevelopment
canonical_url: https://hackersandslackers.com/deploy-serverless-golang-functions-with-netlify/
---

![Deploy Serverless Golang Functions with Netlify](https://hackersandslackers-cdn.storage.googleapis.com/2020/08/netlify-lambda-go@2x.jpg)

The race to monetize static-site hype was over before it began — if you're a JAMStack developer, you're a Netlify customer. Surprisingly, the outcome of this unintentional vendor lock-in has been working out pretty well. JAMStack's paradigm of webhook-driven actions rewrites the narrative of static sites as dynamic entities. By either luck or foresight, Netlify is comfortably positioned to provide a home for static-sites as the only cloud host to focus solely on this model of development by providing features, like serverless functions.

Any market which finds itself dominated by a single vendor is bound to have some downsides. In Netlify's case, one such violation comes in the form of occasional poor documentation, _especially_ regarding Golang function deployment. Netlify has dedicated precisely [one page of documentation](https://docs.netlify.com/functions/build-with-go/) dedicated to writing serverless functions in Go with zero mention of _how to actually deploy said functions_. Compare this to Netlify's commitment to JavaScript function development, which includes a [dedicated CLI](https://github.com/netlify/cli/blob/master/docs/netlify-dev.md#netlify-functions), a [build plugin](https://github.com/netlify/netlify-lambda), and [documentation](https://docs.netlify.com/functions/build-with-javascript/#format) that covers useful details.

This tutorial assumes you have a basic understanding of Golang and GatsbyJS —this post is _not_ a tutorial about learning Go from scratch. We _will_ cover:

- The basics of Go development in the context of Lambda functions.
- Deploying Go functions as part of a GatsbyJS site hosted on Netlify.
- Interacting with and monitoring deployed Go functions.

## Creating a Go Serverless Function

We'll start by creating a new Go project on our GOPATH as we normally would:

```shell
$ mkdir ~/go/src/github.com/toddbirchard/netlify-serverless-tutorial
$ cd ~/go/src/github.com/toddbirchard/netlify-serverless-tutorial
```
<figcaption>Create a directory to store your function source.</figcaption>

Next, we initialize our module by running `go mod init` in the current folder, which should produce the following outcome:

```shell
module github.com/toddbirchard/netlify-serverless-tutorial
  
go 1.14
```
<figcaption>go.mod</figcaption>

Naming your Go module to match the URL of a Github repository is a common practice you should already abide by. Still, it's _especially_ important to do so for our purposes. Netlify creates serverless functions by taking the Go source code we give and building executable binaries. To do so, Netlify needs to be aware of the dependencies our project might have. Instead of uploading our Go source with all its dependencies, Netlify intelligently looks to the dependencies found in our module's Github repository and pulls them itself.

Get started by pasting the minimum boilerplate into a new file called **main.go** :

```go
package main

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

func Handler(request events.APIGatewayProxyRequest) (*events.APIGatewayProxyResponse, error) {
    return &events.APIGatewayProxyResponse{
        StatusCode: 200,
        Body: "Hello World!",
    }, nil
}

func main() {
    // Initiate AWS Lambda handler
    lambda.Start(Handler)
}
```
<figcaption>main.go</figcaption>

Oh snap, we've imported an external library! The **aws-lambda-go** library is essential for our function to work. Get and install the library by running `go get` in your project directory. This will update your **go.mod** file accordingly:

```shell
module github.com/toddbirchard/netlify-serverless-tutorial

go 1.14

require github.com/aws/aws-lambda-go v1.18.0
```
<figcaption>go.mod</figcaption>
### Working with Request Data

Netlify Lambda functions take the form of HTTP endpoints, so odds are you're building something that takes the input of a request (whether it be a request's params or body) to produce a response. We can access information about an incoming request via **aws-lambda-go**'s `APIGatewayProxyRequest` struct: Lambda's default parameter passed to our handler.

Let's see this in action by grabbing a query string parameter from an incoming request and outputting the result. We can easily modify our original handler to check an incoming request for a parameter called `name` (i.e., `example.com/function?name=todd`):

```go
...

func Handler(request events.APIGatewayProxyRequest) (*events.APIGatewayProxyResponse, error) {
    name := request.QueryStringParameters["name"]
    response := fmt.Sprintf("Hello %s!", name)

    return &events.APIGatewayProxyResponse{
        StatusCode: 200,
        Headers: map[string]string{"Content-Type": "text/html; charset=UTF-8"},
        Body: response,
    }, nil
}

...
```
<figcaption>main.go</figcaption>

`APIGatewayProxyRequest` has a variable called `QueryStringParameters`, which is a key/value **map** of query sting names and their values. The above attempts to extract the value of a query string called **name** to create a cheerful response for the user. If an incoming request were not to contain a query string called **name**, the value would return `nil`.

We could extract a lot more from a `APIGatewayProxyRequest` struct if we so chose:

- **Path** : URL requested by the user which generated the request.
- **HTTPMethod** : `GET`, `POST`, `PUT`, etc.
- **Headers** & **MultiValueHeaders** : HTTP headers sent by the user's request.
- **QueryStringParameters** & **QueryStringParameters** : Query strings with single or multiple values.
- **Body** : Data (such as JSON) sent in the body of the request.

The output of a Lambda function is created by returning `APIGatewayProxyResponse`. Our response returns a simple **200** status code, along with a text response reading `"Hello {name}!"`. We specify the content type of the response via our response's `Headers` variable, which accepts a map of headers such as `Content-Type`.

### Testing our Function

I promised to spend minimal time on the actual Golang aspect of this post, but writing unit tests for Lambda functions is particularly tricky as it involves forming fake requests and evaluating their responses. **aws-lambda-go** has a recommended syntax for writing multiple tests against a single handler which I found quite useful:

```go
package main_test

import (
    "github.com/aws/aws-lambda-go/events"
    "github.com/stretchr/testify/assert"
    main "github.com/toddbirchard/netlify-serverless-tutorial"
    "log"
    "testing"
)

func TestHandler(t *testing.T) {
    tests := []struct {
        request events.APIGatewayProxyRequest
        expect string
        err error
    } {
        {
            // Test name has value
            request: events.APIGatewayProxyRequest{QueryStringParameters: map[string]string{"name": "Paul"}},
            expect: "Hello Paul!",
            err: nil,
        },
        {
            // Test name is null
            request: events.APIGatewayProxyRequest{},
            expect: "Hello !",
            err: nil,
        },
    }

    for i, test := range tests {
        response, err := main.Handler(test.request)
        assert.IsType(t, test.err, err)
        assert.Equal(t, test.expect, response.Body)
        log.Printf("Test %d: %s", i, response.Body)
    }
}
```
<figcaption>main_test.go</figcaption>

`TestHandler` creates two hypothetical situations (aka tests) to run against our `Handler` function. The first test passes a query string parameter `"Paul"`, while the second passes none at all. These scenarios are then evaluated in a loop, which outputs the following when tested with `go test`:

```shell
2020/08/01 03:17:47 Test 0: Hello Paul!
2020/08/01 03:17:47 Test 1: Hello !
PASS
ok github.com/toddbirchard/netlify-serverless-tutorial 0.139s
```
<figcaption>main_test.go output</figcaption>

The validity of each test is validated via assertions belonging to the [testify](https://github.com/stretchr/testify) module, which I recommend for unit tests like these. Be sure to `go get` this module if you chose to include it:

```shell
module github.com/toddbirchard/netlify-serverless-tutorial

go 1.14

require (
    github.com/aws/aws-lambda-go v1.18.0
    github.com/stretchr/testify v1.4.0
)
```
<figcaption>go.mod</figcaption>

We now have a Lambda function which is complete enough to deploy.... but how do we go about that?

## Serverless Functions in GatsbyJS

The structure of a minimal GatsbyJS project looks something like this:

```shell
/my-gatsby-project
├── /src
├── /public
├── /static
├── gatsby-browser.js
├── gatsby-config.js
├── gatsby-node.js
└── package.json
```
<figcaption>GatsbyJS project structure.</figcaption>

All of the files and directories above are standard Gatsby stuff doing Gatsby things. Things become a little more interesting when we deploy a Gatsby site with Lambda functions, as our Lambda's source code and binary is going to live side-by-side with our Gatsby code. This takes the form of two directories: a **/functions** directory and a **/go-src** directory:

```shell
/my-gatsby-project
├── /functions
| └── helloworld
├── /go-src
| └── /helloworld
| └── main.go
├── /src
├── /public
├── /static
├── gatsby-browser.js
├── gatsby-config.js
├── gatsby-node.js
└── package.json
```
<figcaption>GatsbyJS project structure.</figcaption>

**/go-src** is where we dump the source code of our Golang functions ( **.go** files). We create subdirectories in **go-src** for each Lambda function we want to deploy. Go source code in each subdirectory will be compiled to a binary sharing the name of the directory in the **/functions** folder. In the above example, the **/helloworld** subdirectory compiles to a binary in **functions/helloworld**, which ultimately dictates the eventual URL of our endpoint as well, as such:

```shell
https://hackersandslackers.com/.netlify/functions/helloworld
```
<figcaption>URL of a deployed Golang Lambda function.</figcaption>

If you're wondering how the source of **main.go** got into our new **/go-src/helloworld** destination, the underwhelming answer is a simple copy-and-paste. No tricks there.

### Building Netlify Lambda Binaries in GatsbyJS

Let's recap where we're at:

- **GatsbyJS project**: Check ✅
- **Lambda source code**: Check ✅
- **Binaries built from source upon project builds**: Not check 🚫

When we deploy to Netlify, we want our project to find the source in **/go-src** and build the resulting binaries to the **/functions** directory. Netlify looks for a build command in [netlify.toml](https://docs.netlify.com/configure-builds/file-based-configuration/), which would typically be `gatsby build` under normal circumstances. Since we're now building our site _and_ functions, we're going to recruit the help of a **Makefile** to enable a more involved build process.

Make sure you have a **netlify.toml** file in your GatsbyJS project root where we can specify the build command of our Makefile, as well as define our functions folder:

```toml
[build]
  base = "/"
  command = "make build"
  publish = "/public"
  functions = "/functions"

[build.environment]
  GO_VERSION = "1.14.5"
```
<figcaption>netlify.toml</figcaption>

We've changed what you most likely had as `command = "gatsby build"` to `command = "make build"`, which looks for a Makefile containing a **build** command to execute. Let's create that now.

### Making a Makefile

If you're new to [Makefiles](https://en.wikipedia.org/wiki/Makefile), they're a standard fixture in projects that allow us to define one-liners that kick off a series of events, such as building a project. Our Makefile is going to look like this:

```makefile
build:
   npm run-script build
   mkdir -p functions
   GOOS=linux
   GOARCH=amd64
   GO111MODULE=on
   GOBIN=${PWD}/functions go get ./...
   GOBIN=${PWD}/functions go install ./...
```
<figcaption>Makefile</figcaption>

Hopefully, you find this string of commands to be confusing as shit as I did (you might be overqualified for reading this post otherwise). We'll break down this `build` command line-by-line:

- `npm run-script build`: This command runs a script in **package.json** labeled as "build," which is likely running `gatsby build`. This step is building your Gatsby site as you normally would for production, so nothing special there.
- `mkdir -p functions`: Creates a directory called **functions** in your project's root directory, in case it doesn't exist. Since I don't recommend uploading your compiled binaries to Github (Netlify will rebuild them regardless), there's a good chance this folder won't exist upon committing. As a result, we need this command as a way to create a fresh **functions** folder on our Netlify server.
- `GOOS=linux` & `GOARCH=amd64`: Golang's [**GOOS** and **GOARCH**](https://gist.github.com/asukakenji/f15ba7e588ac42795f421b48b8aede63) environment variables tell Go which target operating system (Linux) and architecture (AMD 64-bit) our binaries are intended to be built for. The values we're passing here align with the OS and architecture of Netlify's server, thus we're ensuring Go is targeting the correct infrastructure to compile binaries for. 
- `GO111MODULE=on`: Things get a bit interesting here as Go source we're building in **/go-src** is not actually on our GOPATH, nor will it be upon deployment to Netlify. [GO111MODULE](https://insujang.github.io/2020-04-04/go-modules/) is a workaround for building Go projects outside of the designated GOPATH; this allows us to get dependencies of functions living in our Gatsby project and build the resulting binaries without worrying about being shackled to our (or Netlify's) GOPATH.
- `GOBIN=${PWD}/functions go install ./...`: Lastly, we need to `get` and `install` all of our function's dependencies to build them into our final binary. This line is effectively looking for all go modules to build into binaries and builds them in the **/functions** folder.

### Give it a Whirl

We're ready to test our function locally! Run `make build` in your Gatsby root folder to make sure things run smoothly. If you're successful, you should see an output like this towards the end of your build:

```shell
info Done building in 63.568185195 sec
mkdir -p functions
GOOS=linux
GOARCH=amd64
GO111MODULE=on
GOBIN=/Users/toddbirchard/projects/stockholm/functions go get ./...
GOBIN=/Users/toddbirchard/projects/stockholm/functions go install ./...
```
<figcaption>Output of <code>make build</code></figcaption>

You should now see Go binaries in your local **/functions** directory! Add your **/functions directory** to your `.gitignore` file and commit that masterpiece.

## See it in Action

To demonstrate a GatsbyJS site utilizing a Netlify function, I set up a demo for your pleasure [here](https://serverless-golang-tutorial.netlify.app/):

![Deploy Serverless Golang Functions with Netlify](https://hackersandslackers-cdn.storage.googleapis.com/2020/08/netlify-functions-demo.png)<figcaption>[https://serverless-golang-tutorial.netlify.app](https://serverless-golang-tutorial.netlify.app)</figcaption>

This is a simple GatsbyJS site that takes the output of our helloworld function and writes it to the page via some very simple frontend JavaScript. I busted some ass to put together a somewhat respectable demo for you, because copying is way easier than reading. Don't say I've never done anything for you:

{% github https://github.com/hackersandslackers/netlify-functions-tutorial %}



## Why is this So Convoluted?

Compared to the workflow of deploying JavaScript functions on Netlify, Golang is an undocumented and seemingly half-supported nightmare. Until just this week, inspecting your Go Lambda functions on Netlify's UI would append a **.js** file extension to every function by default with the presumed assumption that everybody is picking JS over Go:

![Deploy Serverless Golang Functions with Netlify](https://hackersandslackers-cdn.storage.googleapis.com/2020/07/netlify_golang_serverless_dotjs.png)<figcaption>Netlify doesn't care about Golang™</figcaption>

This is generally in line with Netlify's actions of half-assing their Go documentation, and even releasing a CLI to assist in function development called [netlify-lambda](https://www.npmjs.com/package/netlify-lambda), which _only supports JavaScript._

Other than feeling like a second-class citizen, these gaffs are more amusing than they are harmful. Once your functions are deployed, they work as you'd expect. All is well that ends well.

If you're looking for more resources on Go Lambda development, [AWS has a Github repo](https://github.com/aws/aws-lambda-go) of examples that might prove useful. Aside from that and this tutorial, I think you have everything you need 😉. Until next time.