Golang AWS Lambda Example [In-depth Tutorial]


GO

Author: Tuan Nguyen
Reviewer: Deepak Prasad

In this series, we will learn how to set up a serverless framework example for Golang and AWS Lambda. First of all, we are going to walk through how to create an AWS account and then set up a serverless framework with AWS (Lamba).

Lambda is a compute service that lets you run code without provisioning or managing servers. Lambda runs your code on a high-availability compute infrastructure and performs all of the administration of the compute resources, including server and operating system maintenance, capacity provisioning and automatic scaling, and logging. With Lambda, you can run code for virtually any type of application or backend service. All you need to do is supply your code in one of the languages that Lambda supports.

 

Introduction to AWS

Reason to use Lambda AWS

As long as you can run your application code in the Lambda standard runtime environment and use the resources that Lambda offers, Lambda is an excellent computing solution for many application scenarios.

When employing Lambda, your code is the only thing you are accountable for. The compute fleet, which provides a balance of memory, CPU, network, and other resources to run your code is managed by Lambda. You cannot log in to compute instances or alter the operating system on the given runtimes since Lambda maintains these resources. Lambda manages capacity, monitors, and logs your Lambda functions on your behalf, among other operational and administrative tasks.

 

Lambda features

You can create Lambda applications that are readily expandable, secure, and scalable by using the characteristics listed below:

  1. Concurrency and scaling controls: You have perfectly alright control over the scaling and responsiveness of your production applications thanks to concurrency and scaling features like concurrency limits and provisioned concurrency.
  2. Functions defined as container images: Build, test, and deploy your Lambda functions using the container image tooling, processes, and dependencies that you choose.
  3. Code signing: Code signing for Lambda offers trust and integrity rules that enable you to confirm that only original code released by authorized developers is used in your Lambda services.
  4. Extensions for lambdas: To improve your Lambda functions, use Lambda extensions. Use extensions, for instance, to more easily combine Lambda with your preferred monitoring, observability, and security solutions.
  5. Database access: A database proxy manages a pool of database connections and relays queries from a function. This enables a function to reach high concurrency levels without exhausting database connections.
  6. File systems access: You can configure a function to mount an Amazon Elastic File System (Amazon EFS) file system to a local directory. With Amazon EFS, your function code can access and modify shared resources safely and at high concurrency.

 

Create a Lambda function with the console

To get started with Lambda, use the Lambda console to create a function. You can author functions in the Lambda console, or with an IDE toolkit, command line tools, or the AWS SDKs. The Lambda console provides a code editor for non-compiled languages that lets you modify and test code quickly. The AWS Command Line Interface (AWS CLI) gives you direct access to the Lambda API for advanced configuration and automation use cases.

 

Create the function

You use the console to design a Lambda function in this introductory exercise. The default code that Lambda generates is used by the function. For non-compiled languages, the Lambda console offers a code editor that makes it simple to change and test code.

 

To create a Lambda function with the console

If you do not have an AWS account, go to the signup page. Once you got your account, follow the below steps to create lambda functions:

  1. Open the Functions page of the Lambda console.
  2. Choose to Create function.
  3. Under Basic information, do the following:
    1. For the Function name, enter hello-lambda-func.
    2. For Runtime, confirm that Go1.x is selected. Note that Lambda provides runtimes for .NET (PowerShell, C#), Java, Node.js, Python, and other programming languages
  4. Choose to Create function.

Golang AWS Lambda Example [In-depth Tutorial]

Lambda creates a Go function and an execution role that grants the function permission to upload logs. The Lambda function assumes the execution role when you invoke your function and uses the execution role to create credentials for the AWS SDK and to read data from event sources.

 

Upload source code

Here is an example of creating a function to query print out the welcome message. First, you have to install was library:

go get github.com/aws/aws-lambda-go/lambda

We will write a function called handler (you can name anything here) and use the function lambda.Start(handler) so that Lambda can run the above function.  The maximum of the zip file is 10MB.

package main

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

func handler() (string, error) {
	return "Welcome to Serverless world", nil
}

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

Zip it back into a zip file that can be uploaded to AWS Lambda:

GOOS=linux go build -o main main.go

After creating a lambda, our next step is defining a function by uploading a zip file or choosing from the Amazon S3 location.

Golang AWS Lambda Example [In-depth Tutorial]

Golang AWS Lambda Example [In-depth Tutorial]

 

Invoke the Lambda function

Use the sample event data provided in the console to call your Lambda function. To invoke a function:

After selecting your function, choose the Test tab.

Select New event from the Test event menu. Leave the default hello-world selection in Template. For this test, give it a name, and take notice of the sample event template below:

{
    "key1": "value1",
    "key2": "value2",
    "key3": "value3"
  }

Select Test, then select Save changes. Up to ten test events per function can be created by each user. Other users cannot access those test cases.

Upon successful completion, view the results in the console. We have multiple helpful information such as:

  • Execution result: hows the execution status has succeeded. To view the function execution results, expand Details.
  • Summary: the key information reported
  • Log output: the log that Lambda generates for each invocation

Run the function (choose Test) a few more times to gather some metrics that you can view in the next step. Choose the Monitor tab. This page shows graphs for the metrics that Lambda sends to CloudWatch.

Golang AWS Lambda Example [In-depth Tutorial]

 

Create a lambda function to get people list

Here is an example of writing a function to return a list of people:

package main

import (
	"encoding/json"

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

type Person struct {
	Id    int    `json:"id"`
	Name  string `json:"name"`
	Email string `json:"email"`
}

func list() (string, error) {
	people := []Person{
		{Id: 1, Name: "GoLinuxCloud", Email: "golinu@gmail.com"},
		{Id: 2, Name: "Admin", Email: "admin@gmail.com"},
	}

	res, _ := json.Marshal(&people)
	return string(res), nil
}

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

Now, we can build a binary file by running the below command:

GOOS=linux go build -o listAll listAll.go

Add it to a zip folder then upload it to AWS lambda as instructed in the previous chapter:

Golang AWS Lambda Example [In-depth Tutorial]

 

And we can invoke this function from the Test menu:

Golang AWS Lambda Example [In-depth Tutorial]

 

Amazon API Gateway

What is AWS API Gateway

Amazon API Gateway is an AWS service for creating, publishing, maintaining, monitoring, and securing REST, HTTP, and WebSocket APIs at any scale. API developers can create APIs that access AWS or other web services, as well as data stored in the AWS Cloud. As an API Gateway API developer, you can create APIs for use in your own client applications. Or you can make your APIs available to third-party app developers.

API Gateway creates RESTful APIs that:

  • Are HTTP-based.
  • Enable stateless client-server communication.
  • Implement standard HTTP methods such as GET, POST, PUT, PATCH, and DELETE.

 

Part of AWS serverless infrastructure

Together with AWS Lambda, API Gateway forms the app-facing part of the AWS serverless infrastructure.

For an app to call publicly available AWS services, you can use Lambda to interact with required services and expose Lambda functions through API methods in API Gateway. AWS Lambda runs your code on a highly available computing infrastructure. It performs the necessary execution and administration of computing resources. To enable serverless applications, API Gateway supports streamlined proxy integrations with AWS Lambda and HTTP endpoints.

 

Create an AWS API Gateway

Open Web Console and look for API Gateway and then Build a REST API (without private)

Golang AWS Lambda Example [In-depth Tutorial]

 

Set up your protocol and then click Create API

Golang AWS Lambda Example [In-depth Tutorial]

 

The next step is setting all the resources and endpoint URLs for your gateway.

Golang AWS Lambda Example [In-depth Tutorial]

Golang AWS Lambda Example [In-depth Tutorial]

 

Because we are integrating the gateway with AWS lambda functions so we can tick Use Lambda Proxy integration: Requests will be proxied to Lambda with request details available in the `event` of your handler function.

In the next step, we will deploy our REST API. Go to Actions tab, select Deploy API and follow the instructions:

Golang AWS Lambda Example [In-depth Tutorial]

 

Now we can see the URL path for our deployment:

Golang AWS Lambda Example [In-depth Tutorial]

 

We can test our get people list API using Postman:

Golang AWS Lambda Example [In-depth Tutorial]

We can see that there is an error here. The Lambda function's response must adhere to API Gateway's guidelines in order for Lambda and API Gateway to work together. We have to change our code to return a Response struct:

type Response struct {
    StatusCode int `json:"statusCode"`
    Body string `json:"body"`
}

Our lambda function will be:

package main

import (
	"encoding/json"

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

type Person struct {
	Id    int    `json:"id"`
	Name  string `json:"name"`
	Email string `json:"email"`
}

type Response struct {
	StatusCode int    `json:"statusCode"`
	Body       string `json:"body"`
}

func list() (Response, error) {
	people := []Person{
		{Id: 1, Name: "GoLinuxCloud", Email: "golinu@gmail.com"},
		{Id: 2, Name: "Admin", Email: "admin@gmail.com"},
	}

	res, _ := json.Marshal(&people)
	return Response{
		StatusCode: 200,
		Body:       string(res),
	}, nil

}

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

If we import the function again, now we can test our API:

Golang AWS Lambda Example [In-depth Tutorial]

 

For standardization, AWS also provides us with the Golang SDK so that we can avoid these errors and write code faster. Instead of having to create the struct Response ourselves, we use the SDK as follows, download the package go get github.com/aws/aws-lambda-go/events, update main.go, we use the built-in struct APIGatewayProxyResponse:

package main

import (
	"encoding/json"

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

type Person struct {
	Id    int    `json:"id"`
	Name  string `json:"name"`
	Email string `json:"email"`
}

func list() (events.APIGatewayProxyResponse, error) {
	people := []Person{
		{Id: 1, Name: "GoLinuxCloud", Email: "golinu@gmail.com"},
		{Id: 2, Name: "Admin", Email: "admin@gmail.com"},
	}

	res, _ := json.Marshal(&people)
	return events.APIGatewayProxyResponse{
		StatusCode: 200,
		Headers: map[string]string{
			"Content-Type": "application/json",
		},
		Body: string(res),
	}, nil

}

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

 

API query person by id

Next we will create API get person by id. We have to send the id as a parameter and then use APIGatewayProxyRequest to extract this parameter from the request. We define an endpoint to handle this API for example /{id}. The function code:

package main

import (
	"encoding/json"
	"strconv"

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

type Person struct {
	Id    int    `json:"id"`
	Name  string `json:"name"`
	Email string `json:"email"`
}

func getPerson(req events.APIGatewayProxyRequest) (events.APIGatewayProxyResponse, error) {
	people := []Person{
		{Id: 1, Name: "GoLinuxCloud", Email: "golinu@gmail.com"},
		{Id: 2, Name: "Admin", Email: "admin@gmail.com"},
		{Id: 3, Name: "Test", Email: "test@gmail.com"},
	}

	// get the query param
	id, err := strconv.Atoi(string(req.PathParameters["id"]))
	if err != nil {
		return events.APIGatewayProxyResponse{
			StatusCode: 400,
			Body:       err.Error(),
		}, nil
	}

	res, err := json.Marshal(people[id-1])

	// return an error if can not found person
	if err != nil {
		return events.APIGatewayProxyResponse{
			StatusCode: 500,
			Body:       err.Error(),
		}, nil
	}

	return events.APIGatewayProxyResponse{
		StatusCode: 200,
		Headers: map[string]string{
			"Content-Type": "application/json",
		},
		Body: string(res),
	}, nil
}

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

Create a new resource for our gateway:

Golang AWS Lambda Example [In-depth Tutorial]

 

Create GET method for that resource:

Golang AWS Lambda Example [In-depth Tutorial]

 

Deploy it to the existed one that we created in the above section:

Golang AWS Lambda Example [In-depth Tutorial]

 

Now we can test the API using Postman:

Golang AWS Lambda Example [In-depth Tutorial]

 

Serverless architecture

You can create a web API with an HTTP endpoint for your Lambda function by using Amazon API Gateway. API Gateway provides tools for creating and documenting web APIs that route HTTP requests to Lambda functions. You can secure access to your API with authentication and authorization controls. Your APIs can serve traffic over the internet or can be accessible only within your VPC.

Resources in your API define one or more methods, such as GET or POST. Methods have an integration that routes requests to a Lambda function or another integration type. You can define each resource and method individually, or use special resource and method types to match all requests that fit a pattern. A proxy resource catches all paths beneath a resource. The ANY method catches all HTTP methods.

So now our server architecture will be:

Golang AWS Lambda Example [In-depth Tutorial]

 

Simple Go Http Client to test APIs

Here is an example of HTTP client to test our API:

package main

import (
	"fmt"
	"io/ioutil"
	"net/http"
)

func main() {
	// get all people request
	url := "https://2edzqmpfc1.execute-api.us-east-1.amazonaws.com/stage/"
	method := "GET"

	client := &http.Client{}
	req, err := http.NewRequest(method, url, nil)

	if err != nil {
		fmt.Println(err)
		return
	}
	res, err := client.Do(req)
	if err != nil {
		fmt.Println(err)
		return
	}
	defer res.Body.Close()

	body, err := ioutil.ReadAll(res.Body)
	if err != nil {
		fmt.Println(err)
		return
	}
	fmt.Println("All people:")
	fmt.Println(string(body))
	fmt.Println("------------------")

	// get one person request
	url1 := "https://2edzqmpfc1.execute-api.us-east-1.amazonaws.com/stage/3"
	req1, err := http.NewRequest(method, url1, nil)

	if err != nil {
		fmt.Println(err)
		return
	}
	res1, err := client.Do(req1)
	if err != nil {
		fmt.Println(err)
		return
	}
	defer res1.Body.Close()

	body, err = ioutil.ReadAll(res1.Body)
	if err != nil {
		fmt.Println(err)
		return
	}
	fmt.Println("Query person with id=3")
	fmt.Println(string(body))
	fmt.Println("------------------")

	url2 := "https://2edzqmpfc1.execute-api.us-east-1.amazonaws.com/stage/10"
	req2, err := http.NewRequest(method, url2, nil)
	if err != nil {
		fmt.Println(err)
		return
	}
	res2, err := client.Do(req2)
	if err != nil {
		fmt.Println(err)
		return
	}
	defer res2.Body.Close()

	body, err = ioutil.ReadAll(res2.Body)
	if err != nil {
		fmt.Println(err)
		return
	}
	fmt.Println("Query person with id=10")
	fmt.Println(string(body))
}

Output:

All people request:
[{"id":1,"name":"GoLinuxCloud","email":"golinu@gmail.com"},{"id":2,"name":"Admin","email":"admin@gmail.com"}]
------------------
Query person with id=3
{"id":3,"name":"Test","email":"test@gmail.com"}
------------------
Query person with id=10
{"message": "Internal server error"}

 

Summary

In this article, we learned about building a REST API in a Serverless model with API Gateway and AWS Lambda. This is a very simple example to illustrate the components of a serverless system; in reality, we need to use tools to automate deployment like terraform, write CI/CD to automate code deployment, and various adjustments to improve system performance and reduce operational risks. If you have more questions or need any clarification, you can ask below in the comments section.

 

References

https://aws.amazon.com/api-gateway/
https://aws.amazon.com/lambda/
https://docs.aws.amazon.com/lambda/latest/dg/getting-started.html
https://www.golinuxcloud.com/aws-cli-tutorial/
https://docs.aws.amazon.com/lambda/latest/dg/lambda-golang.html
https://docs.aws.amazon.com/lambda/latest/dg/golang-package.html

 

Tuan Nguyen

Tuan Nguyen

He is proficient in Golang, Python, Java, MongoDB, Selenium, Spring Boot, Kubernetes, Scrapy, API development, Docker, Data Scraping, PrimeFaces, Linux, Data Structures, and Data Mining. With expertise spanning these technologies, he develops robust solutions and implements efficient data processing and management strategies across various projects and platforms. You can connect with him on his LinkedIn profile.

Can't find what you're searching for? Let us assist you.

Enter your query below, and we'll provide instant results tailored to your needs.

If my articles on GoLinuxCloud has helped you, kindly consider buying me a coffee as a token of appreciation.

Buy GoLinuxCloud a Coffee

For any other feedbacks or questions you can send mail to admin@golinuxcloud.com

Thank You for your support!!

Leave a Comment