In the previous entry of the series, we build a very basic AWS Lambda Function in Go & tested it using the AWS Console. In this one, we’ll expand on that by updating our function to support what’s called the API Gateway Proxy Event, as well as connecting API Gateway to the Lambda function & testing it using the VSCode Rest Client plugin.

🔗
If you aren’t familiar with the VSCode Rest Client plugin, feel free to check out my video ‘

Lambda & Events

Before we get into the main portion of the article, I want to take a moment to explain how events & Lambda work. One of the best and most confusing things about Lambda is they are really designed to be agnostic as to what's executing them, meaning they really don’t care what service in AWS calls them.

Each service that calls a Lambda has a specific type of Event associated with it. For example, you can configure an AWS Lambda Function to trigger when a file is uploaded to S3 (a file hosting service in AWS) and the parameters that are passed into Lambda will be different than if API Gateway calls the function. In Go, the AWS SDK has a different struct designed for each caller of the Lambda function so you get a strongly typed object to work with when parsing data from the caller.

If this is confusing, it will make more sense as we progress in this article.

Wiring up API Gateway

Let's start in AWS by navigating to the API Gateway service and creating a new API. On the next screen, select the Build button in the HTTP API card.

Now click Add Integration, select Lambda, and use the Lambda function box to search for the Lambda you created previously. Also make sure to give your API a name. Click Next.

AWS will automatically create a resource path for you and default the Method to ANY. I just want to note that Method field. You have the option to create individual lambdas that are handled differently based on the incoming HTTP Method on a specific route/path, OR in this case pass all Methods into a single Lambda. I prefer the latter but it definitely gives you some flexibility if needed. You can click Next without changing anything.

You can click Next on this screen as well as we won't be configuring any additional stages in this tutorial. Finally, click Create on the last screen.

In the following screen after the API Gateway instance has been created, you’ll notice there is an Invoke URL listed. Note that down as we’ll be using that to test our API using the VS Code Rest Client plugin.

Testing our Integration

If you followed along from the previous article, we actually already setup our function to handle API Gateway Proxy Events as shown in the request parameter of our handler function. So at this point, we can simply execute a GET request to our endpoint. Simply take the route created earlier and append it to the Invoke URL to test it properly.

package main

import (
    "log"

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

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

func handler(request events.APIGatewayProxyRequest) (events.APIGatewayProxyResponse, error) {
    log.Println("Hello world")

    response := events.APIGatewayProxyResponse{
        StatusCode: 200,
    }

    return response, nil
}

We received a 200 response with a null body, which is perfectly fine since we didn't actually return any data from the function. Let's fix that now.

Handling Incoming & Outgoing Data

Most APIs don't just return a 200 without being able to handle some kind of input & output, so let's explore how we can update our function to both receive & respond with some JSON data. To handle incoming data, there is a field in the request object called, as expected, Body. Body is a string value which means we’ll need to marshal it to an object if we’re passing in some kind of data. First thing I’ll do is create struct called Person and add a few fields to it. You can add this anywhere in your main.go file.

type Person struct {
    FirstName *string json:"firstName"
    LastName  *string json:"lastName"
}

Now update the handler function to parse the body into an instance of the struct.

func handler(request events.APIGatewayProxyRequest) (events.APIGatewayProxyResponse, error) {
    var person Person
    err := json.Unmarshal([]byte(request.Body), &person)
    if err != nil {
        return events.APIGatewayProxyResponse{}, err
    }

    response := events.APIGatewayProxyResponse{
        StatusCode: 200,
    }

    return response, nil
}

Let's also add a response struct that returns a message to the caller with the data passed in.

type ResponseBody struct {
    Message *string json:"message"
}

Update the handler to create the response body object and add a message. You’ll also need to convert that object to a string before it can be returned. Once you do that, simply assign it to the Body field of the response object. Below is what the final version of the function should look like.

package main

import (
    "encoding/json"
    "fmt"

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

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

func handler(request events.APIGatewayProxyRequest) (events.APIGatewayProxyResponse, error) {
    var person Person
    err := json.Unmarshal([]byte(request.Body), &person)
    if err != nil {
        return events.APIGatewayProxyResponse{}, err
    }

    msg := fmt.Sprintf("Hello %v %v", *person.FirstName, *person.LastName)
    responseBody := ResponseBody {
        Message: &msg,
    }
    jbytes, err := json.Marshal(responseBody)
    jstr := string(jbytes)

    response := events.APIGatewayProxyResponse{
        StatusCode: 200,
        Body: jstr,
    }

    return response, nil
}

type ResponseBody struct {
    Message *string json:"message"
}

type Person struct {
    FirstName *string json:"firstName"
    LastName  *string json:"lastName"
}

Now let’s deploy our function out by zipping it up and uploading it to AWS. Follow steps in the previous article if you aren’t sure how to do that. Once done, create a POST request so you can send some data to the Lambda function.

In the next entry of the series, we’ll explore how to use Cognito to implement serverless Authentication into the API.