Serverless Application in Golang with AWS October 29, 2021 • Muhammad Hamza Hameed What is Serverless Architecture? Serverless architecture is a way to build and run applications and services without having to manage infrastructure. Your application still runs on servers, but all the server management is done by AWS. AWS Lambda AWS Lambda is a serverless, event-driven compute service that lets you run code for virtually any type of application or backend service without provisioning or managing servers. You can trigger Lambda from over 200 AWS services and software as a service (SaaS) applications and only pay for what you use. Advantages of Using AWS Lambda Run code without provisioning or managing infrastructure. Simply write and upload code as a .zip file or container image. Automatically respond to code execution requests at any scale, from a dozen events per day to hundreds of thousands per second. Save costs by paying only for computing time you use — by per millisecond — instead of provisioning infrastructure upfront for peak capacity. Optimize code execution time and performance with the right function memory size. Respond to high demand in double-digit milliseconds with Provisioned Concurrency. Serverless Application in Go with AWS Prerequisites You’ll need an AWS account for this. If you don’t yet have one, sign up for a free account here. For building and deploying your functions, you’ll be using the Serverless Framework, which is the most widely used tool for the job. Assuming you have a recent version of Node.js installed, you can install the Serverless CLI with the following npm command: npm install -g serverless Once you have the Serverless CLI installed, you must configure it to use the AWS access keys of your account: serverless config credentials --provider aws --key <access key ID> --secret <secret access key> You can get the access key and secret key from the My Security Credentials option. Use Create New Access Key if you do not have one already. If you don’t have Go installed yet, you can either download an installer from the official website or use your favorite package manager to install it The Lambda-Time Function Now that you have everything you need, let’s create a new project using Go modules for your function. For example, let’s name the module lambda-time. In a new, empty directory, initialize the Go module, and install the AWS Lambda for Go library: go mod init lambda-time go get github.com/aws/aws-lambda-go After this, you can proceed to create a main.go file that implements your handler function and starts the process: package main import ( "context" "encoding/json" "log" "time" "github.com/aws/aws-lambda-go/events" "github.com/aws/aws-lambda-go/lambda" ) func main() { lambda.Start(handleRequest) } func handleRequest(ctx context.Context, request events.APIGatewayProxyRequest) (events.APIGatewayProxyResponse, error) { resp := &response{ UTC: time.Now().UTC(), } body, err := json.Marshal(resp) if err != nil { return events.APIGatewayProxyResponse{}, err } return events.APIGatewayProxyResponse{Body: string(body), StatusCode: 200}, nil } type response struct { UTC time.Time `json:"utc"` } This previous code can be broken into a few simple steps: Define a response struct that supports JSON serialization and defines the HTTP response body of a successful invocation of your AWS Lambda function. Create a request handler function, which creates a response struct containing the current time in UTC and then proceeds to serialize it as JSON. In case the serialization fails, you return the error; if everything goes well, you respond with your serialized JSON as the response body and a status code of 200. Register your handler function in the main function using the AWS Lambda for Go library. The Handler Function It’s worth taking some time to understand how the handler function works. While there are multiple valid handler signatures, the one you used is the complete one. The context argument provides information on the invoked function, its environment and the deadline of the invocation. Returning an error value from the handler function signals that the invocation failed and automatically logs the value of the error. That leaves the request and response structs in your handler function signature. Lambda functions are invoked either by AWS services or by using an AWS SDK (e.g., from another Lambda function). Data passed in and out of a Lambda function is in JSON format. In your case, the AWS Lambda for Go library automatically handles the serialization and deserialization between JSON and Go values. When calling Lambda functions using the AWS SDK, the structure of the input and output JSON data is up to the developer. For AWS Lambda functions invoked by AWS services, the data structure depends on the invoking service. Amazon API Gateway is the service that triggers Lambda functions in response to HTTP calls. For API Gateway, this means the request is always of type events.APIGatewayProxyRequest and the response will always be of type events.APIGatewayProxyResponse. The AWS Lambda for Go library contains the data definitions for each AWS service that can invoke Lambda functions. Integration with Golang Routing Libraries If you are using some package for routing, there is a package available that may be used to connect their routing library with lambda. github.com/awslabs/aws-lambda-go-api-proxy It currently supports the following routing libraries. chi gin gorillamux fiber echo iris negroni Lets see the chi example for routing: package main import ( "context" "encoding/json" "log" "time" chiadapter "github.com/awslabs/aws-lambda-go-api-proxy/chi" "github.com/aws/aws-lambda-go/events" "github.com/aws/aws-lambda-go/lambda" ) func main() { lambda.Start(handleRequests) } func handleRequests(ctx context.Context, request events.APIGatewayProxyRequest) (events.APIGatewayProxyResponse, error) { router := chi.NewRouter() router.Get("/", func (w http.ResponseWriter, _ *http.Request) { resp := &response{ UTC: time.Now().UTC(), } body, err := json.Marshal(resp) if err != nil { w.WriteHeader(http.StatusInternalServerError) return } w.Header().Set("Content-Type", "application/json; charset=utf-8") w.WriteHeader(http.StatusOK) w.Write(body) }) return chiadapter.New(router).ProxyWithContext(ctx, req) } type response struct { UTC time.Time `json:"utc"` } Deployment Your function is now ready and you can proceed by deploying it with the Serverless Framework. Your application deployed using the Serverless framework based on the serverless.yml configuration file. If you are not familiar with the .yml syntax, you can read this serverless.yml guide. We must first create a serverless.yml file that defines what we are deploying. service: lambda-time provider: name: aws runtime: go1.x package: exclude: - ./** include: - ./bin/** functions: lambda-time: handler: bin/lambda-time events: - http: path: / method: get Here you name both your service and the function lambda-time, but the service could instead contain multiple functions with different names. You also need to configure your API Gateway by specifying that the function responds to HTTP events of a particular HTTP method and at a given request path. Next up, build the code as an x86-64 Linux executable, and deploy it: GOOS=linux GOARCH=amd64 go build -o bin/lambda-time . serverless deploy Once finished, the command prints the URL for the endpoint. Open it, and make sure it responds with the current time. Now you can visit your aws account. You can see your application at AWS services > Lambda > Applications In your application you can see your ApiGatewayRestApi , Lambda Function and ServerlessDeploymentBucket where your api is deployed. In ApiGatewayRestApi there are api paths Method Execution where you can run Test. Stage where you can create multiple stages, By Default we have a dev stage. In Lambda Functions you can see your lambda functions and in your function there is runtime settings where your handler file and runtime language already selected. Let’s visit Cloud Watch where you can see your logs: References Go Support for AWS Lambda AWS Lambda and Golang AWS Lambda function handler in Go Serverless Architecture See full source-code at this github repository. Please enable JavaScript to view the comments powered by Disqus.