Netlify Form Submissions With Golang Lambda Functions
Tags: go, webdev, serverless, beginners
Published: 2019-06-18 16:44:20


Static websites are a great way to recapture some of the best parts of the history of the web. They provide low overhead and simple deployments. One of the things that a static website loses, however, is the ability to use your backend to handle form data. Netlify, a static site hosting service, solves this problem by leveraging AWS Lambda. AWS Lambda lets you run backend code without needing to host a whole application: you only run the code you need.

This tutorial will start by setting up a Netlify form. Next we’ll create the Lambda function that processes our form submission. Finally, we’ll deploy that on Netlify. Our use case is going to be a simple contact form that emails the submitter that their contact form was received.

### Form Setup

[Netlify Forms]( https://www.netlify.com/docs/form-handling/ ) are forms that Netlify will handle automatically and they are already plenty powerful. By default, a form handled by Netlify has its data dumped into the Netlify admin area for your website, then that data is accessible either through the admin area or through the Netlify API. If you don’t need to do immediate work on your form data, that might be a good way to go!

Our contact form is going to need some basic information: name, email, and a message. To make a form a Netlify form, all we have to do is add `data-netlify=true` to our form element.

    <form name="contact" method="POST" data-netlify="true">
      <p>
        <label>Your Name: <input type="text" name="name" /></label>   
      </p>
      <p>
        <label>Your Email: <input type="email" name="email" /></label>
      </p>
      <p>
        <label>Message: <textarea name="message"></textarea></label>
      </p>
      <p>
        <button type="submit">Send</button>
      </p>
    </form>

Pretty simple, right? Now this information will get dumped into our Netlify admin area without any changes, but we want to email the form to the submitter and ourselves as well.

### Forms and Functions

AWS Lambda functions give us the ability to run server-side code without needing to run a server. Netlify wraps up AWS Lambda for certain use cases with [Event Triggers]( https://www.netlify.com/docs/functions/#event-triggered-functions ), mostly around deployments and service coordination. However, there is also a hook for form submissions! That gives us an in. Netlify supports Javascript or Go for these functions; we’re going to use Go.

For this we need some Go code to run, then a way for Netlify to build our Go code into the correctly named file, and then away we go!

Our use case is simple. We want to take the input from the contact form and mirror it back to the submitter, with ourselves BCC’d.

The requirements for a very basic Go function on Netlify are a main function that sets up your lambda handler, and that handler function. You can read the whole code for our function here: <a href="https://github.com/camilopayan/netlify-go-function-example/blob/master/main.go" rel="me">https://github.com/camilopayan/netlify-go-function-example/blob/master/main.go</a>

### Getting Form Data

Netlify injects the form data into the request body as part of a JSON object. If you’d like a more detailed look at the entire JSON object, then check out https://open-api.netlify.com/

For our use case however, we’ll be using Go’s built-in JSON Unmarshaler to change our request body into Go structs.

    type Form struct {
        Name    string `json:"name"`
        Email   string `json:"email"`
        Message string `json:"message"`
    }
    
    type Payload struct {
        Form Form `json:"data"`
    }
    
    type Body struct {
        Payload Payload `json:"payload"`
    }

By using nested structs, with fields tagged as their fields inside the JSON object, we can use json.Unmarshal to slurp our form data into a usable struct!

### Getting Secret Data
If we’re going to send emails or plug into any 3rd party service (maybe you want to dump your customer information into Airtable!) then we need a secure way to get those secrets like API keys or SMTP credentials.

For this, Netlify provides Environment Variables, which will come along with your Function. You can set some variables in your netlify.toml file, but this is meant for build variables (npm versions and so on). Secrets should be stored via your Netlify admin dashboard. [You can read more about environment vars in the Netlify docs.](https://www.netlify.com/docs/continuous-deployment/#environment-variables)

To access those environment variables, you can use the built in Go `os` library.

    port, _      := strconv.Atoi(os.Getenv("MAIL_PORT"))
    host         := os.Getenv("MAIL_HOST")
    senderemail  := os.Getenv("MAIL_SENDER_EMAIL")
    smtppassword := os.Getenv("MAIL_PASSWORD")
    smtpuser     := os.Getenv("MAIL_USERNAME")

### Building Your Function

One of the coolest things about Netlify is its built-in continuous deployment pipeline. If it’s possible for you (using the latest Go), I recommend keeping your life easy and using Go modules.

First, locally you need to run `go mod init` to create your go.mod file and bring in your dependencies. Commit that to your git repo.

Next I use a Makefile with a very simple set of commands:

    mkdir -p functions
    go build -o functions/submission-created ./...

This creates the functions directory, where Netlify will look for any functions and also build your go files and compile them into one file called `submission-created`. You might remember from earlier that `submission-created` is one of the supported event-triggered functions.

You should also modify your netlify.toml file to run `make build` as part of your deployment.

With this, you should have enough base information to start writing your own form-crunching functions, all without having to lurch through the AWS Lambda documentation. Netlify Functions are more flexible than this, though, and you could even deploy Functions that implement a full API or just some RPC endpoints to hit from a JS frontend.