Serverless Golang REST API with AWS Lambda
Tags: go, serverless, aws, tutorial
Published: 2022-01-29 18:48:06

In this post, let us go through the process of deploying a Golang API to **AWS Lambda** and handling requests through **AWS API Gateway**, a process which is very straight forward and takes a few minutes once we know the steps.

I am assuming that you have a basic knowledge of Go and an AWS account. If you don’t know anything about AWS Lambda and API Gateway, don’t worry, all we need to get started is explained here. If you already have a good understanding of Lambda and API Gateway, feel free to skip some of the explanations.

I was inspired from may other posts and one of the main reason I am writing this post is because most of the posts involves explanation of additional components like databases, cache, etc which is really useful but not a straightforward post on getting started with AWS Lambda Golang REST API methodologies with API gateway as there are many use-cases without the need of any database or state to be maintained.

So we can take a look at working with databases like dynamodb in a different post and in this post, we will create a simple rest api which does the below

| Method | Route | Action |
|--------|--------|--------|
| GET | /mirror?name=xxx | Returns the query parameter name like a mirror and an error if name is not found|
| POST | /mirror | Accepts a json payload and returns it back like a mirror and returns an error when json is invalid or payload is empty |

With these APIs, we will get an understanding of how to work with the http methods and parsing the incoming request.



<!--There are many approaches in working with AWS, CLI based approach and GUI console based approach. We will go through both the approaches, feel free to pick an approach of your choice.-->

## Table Of Contents
<!--* [CLI Based Approach](#cli)-->
  1. [Setting up the aws cli](#cli-step-1)
  2. [Creating Simple Golang REST API](#cli-step-2)
  3. [Creating and deploying an AWS Lambda function](#cli-step-3)
  4. [Setting up the HTTP API in AWS API Gateway](#cli-step-4)
  5. [Deploying the HTTP API created](#cli-step-5)

<!--## CLI <a name="cli"></a>-->
### Setting up the aws cli <a name="cli-step-1"></a>

1. Installation and basic usage instructions can be found [here](https://docs.aws.amazon.com/cli/latest/userguide/cli-chap-welcome.html).
We can verify the installation as below
```bash
aws --version

aws-cli/2.2.44 Python/3.8.8 Darwin/20.5.0 exe/x86_64 prompt/off
```
<br>
2. We need to setup an AWS IAM user with _programmatic access_ permission for the CLI to use. Detailed official instructions can be found here [here](https://docs.aws.amazon.com/IAM/latest/UserGuide/id_users_create.html) and you can also check this [post](https://dev.to/animeshkbhadra/aws-iam-user-536a) for a detailed walkthrough.
For getting started, we will attach _AdministratorAccess_ policy to this user but for production, recommended to use a restricted policy with only the necessary levels of access. 
Note down the _aws_access_key_id_ and _aws_secret_access_key_ obtained.
<br>
3. Configure the CLI to use the credentials of the IAM user we just created. We will use the region _us-east-2_ and the output format as json (json is easy to read).

```bash
$ aws configure
AWS Access Key ID (None): aws_access_key_id
AWS Secret Access Key (None): aws_secret_access_key
Default region name (None): us-east-2
Default output format (None): json
```
More detailed instructions can be found [here](https://dev.to/code2bits/how-to-configure-aws-cli-33fk)

### Creating Simple Golang REST API <a name="cli-step-2"></a>
#### 1 .GO Installation
Though the deployment mode is serverless in AWS cloud, we would need Go installed in your machine for compiling the go package. 
Follow the instructions in the [official page](https://go.dev/doc/install) for installing go in your machine.
Verify that Go is installed.
```bash
$ go version

go version go1.17.2 darwin/amd64
```
<br>
#### 2 .Creating the Folder structure
```bash
mkdir serverlessgolang
cd serverlessgolang
```
#### 3 .Initiate the project
```bash
go mod init <project-name>
```
The above command initiates the project and creates a file called _go.mod_ - which is similar to package.json in nodejs.
<br>
Its a good approach to give the project-name as a repo link (github.com/yourid/<repo-name> and also to keep this folder structure as part of git, but not compulsory.

#### 4 .Create the golang file and install the packages
Just proceed, we will create this file and proceed with the explanation of each part below.
```bash
vi main.go
```
and in the editor
```go
package main

import (
	"fmt"

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

func HandleRequest(request events.APIGatewayProxyRequest) (events.APIGatewayProxyResponse, error) {
	ApiResponse := events.APIGatewayProxyResponse{}
	// Switch for identifying the HTTP request
	switch request.HTTPMethod {
	case "GET":
		// Obtain the QueryStringParameter
		name := request.QueryStringParameters["name"]
		if name != "" {
			ApiResponse = events.APIGatewayProxyResponse{Body: "Hey " + name + " welcome! ", StatusCode: 200}
		} else {
			ApiResponse = events.APIGatewayProxyResponse{Body: "Error: Query Parameter name missing", StatusCode: 500}
		}

	case "POST":	
		//validates json and returns error if not working
		err := fastjson.Validate(request.Body)

		if err != nil {
			body := "Error: Invalid JSON payload ||| " + fmt.Sprint(err) + " Body Obtained" + "||||" + request.Body
			ApiResponse = events.APIGatewayProxyResponse{Body: body, StatusCode: 500}
		} else {
			ApiResponse = events.APIGatewayProxyResponse{Body: request.Body, StatusCode: 200}
		}

	}
	// Response
	return ApiResponse, nil
}

func main() {
	lambda.Start(HandleRequest)
}
```

Now after creating the file, we can install all the required modules using
```bash
go mod tidy
```
which will download and install the imported files in main.go or if any other go files present in the folder.
<br>
The alternative approach is to manually install the packages
```bash
go get <package-name>
---
go get github.com/aws/aws-lambda-go
go get github.com/valyala/fastjson
```

#### 4.1 .Code and Package Explanation 
Now, let us go through in detail the packages imported and the code

##### Package github.com/aws/aws-lambda-go/lambda
This package implements the Lambda programming model for Go and is used to start or initiate the lambda handler. Any Golang Lambda function must have this package.

In the function `main()`, we call `lambda.start()` and pass in the `HandleRequest()` as the lambda handler function. <br>Our handler function, as programmed will parse the request and returns a welcome message incase of Get method and returns the input body in case of POST method.

More details on this package can be found [here](https://pkg.go.dev/github.com/aws/aws-lambda-go/lambda).

##### Package github.com/aws/aws-lambda-go/events
This package provides input types for Lambda functions that process AWS events. There are a variety of events which can be consumed by the lambda function, in which we will be working on API Gateway events and the package also provides two useful types `APIGatewayProxyRequest` and `APIGatewayProxyResponse` which contain useful information about incoming HTTP requests and send responses that the API Gateway understands.

```go
type APIGatewayProxyRequest struct {
    Resource              string                        `json:"resource"` // The resource path defined in API Gateway
    Path                  string                        `json:"path"`     // The url path for the caller
    HTTPMethod            string                        `json:"httpMethod"`
    Headers               map[string]string             `json:"headers"`
    QueryStringParameters map[string]string             `json:"queryStringParameters"`
    PathParameters        map[string]string             `json:"pathParameters"`
    StageVariables        map[string]string             `json:"stageVariables"`
    RequestContext        APIGatewayProxyRequestContext `json:"requestContext"`
    Body                  string                        `json:"body"`
    IsBase64Encoded       bool                          `json:"isBase64Encoded,omitempty"`
}

type APIGatewayProxyResponse struct {
    StatusCode      int               `json:"statusCode"`
    Headers         map[string]string `json:"headers"`
    Body            string            `json:"body"`
    IsBase64Encoded bool              `json:"isBase64Encoded,omitempty"`
}
```

here,
`request` is of type `events.APIGatewayProxyRequest` and
`response` is of type `events.APIGatewayProxyResponse` <br>
and using 
* `request.HTTPMethod` we identify the GET and POST invocations
* `request.Body` we get the request body
* `request.QueryStringParameters` we get the query parameter name
* `response.Body` we send the response message to the user 
* `response.StatusCode` we send the response status code

Notice how in all cases the error value returned from our lambda handler is nil. This is because the AWS API Gateway doesn't accept error objects when you're using it in conjunction with a lambda proxy integration (they would result in a 'malformed response' errors again). So we need to manage errors fully within our lambda function and return the appropriate HTTP response. 

##### Package github.com/valyala/fastjson
This package contains json parsing and validating methods which are faster than default json package.
We are using the method `fastjson.Validate(request.Body)` to validate the json. 

> We can also validate the input payload in the API gateway, which is recommended as it will not invoke the lambda function on wrong inputs and saving cost. We will look at it later.

#### 5. Creating an executable and zipping <a name="cli-step-2-5"></a>
```bash
env GOOS=linux GOARCH=amd64 go build -o output/main
```
here, we are setting the environment variable
* `GOOS=linux`, implying build the executable for linux OS
* `GOARCH=amd64`, implying that the executable is build for execution in amd64 cpu architechture
* `-o <path/file>` is the output file name

Next, we need to zip the file to upload it to AWS Lambda function
```bash
zip -j output/function.zip output/main
```
this will create a zip file

### Creating and deploying an AWS Lambda function <a name="cli-step-3"></a>

#### 1. IAM Role
Before deploying a lambda function, We need to set up an IAM role which defines the permission that our lambda function will have when it is running.

For now let's set up a lambda-function-executor role and attach the AWSLambdaBasicExecutionRole managed policy to it. This will give our lambda function the basic permissions it need to run and log to the AWS cloudwatch service.

First we have to create a trust policy JSON file. This will essentially instruct AWS to allow lambda services to assume the lambda-function-executor role

**File:** ./tmp/trust-policy.json
```json
{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Effect": "Allow",
            "Principal": {
                "Service": "lambda.amazonaws.com"
            },
            "Action": "sts:AssumeRole"
        }
    ]
}
```
Then use the aws iam create-role command to create the role with this trust policy

```bash
aws iam create-role --role-name lambda-function-executor \
--assume-role-policy-document file://./tmp/trust-policy.json
```
Response:
```json
{
    "Role": {
        "Path": "/",
        "RoleName": "lambda-function-executor",
        "RoleId": "AROAZ5VSPHO6WRWZRHZBX",
        "Arn": "arn:aws:iam::682200349629:role/lambda-function-executor",
        "CreateDate": "2022-01-29T13:54:20+00:00",
        "AssumeRolePolicyDocument": {
            "Version": "2012-10-17",
            "Statement": [
                {
                    "Effect": "Allow",
                    "Principal": {
                        "Service": "lambda.amazonaws.com"
                    },
                    "Action": "sts:AssumeRole"
                }
            ]
        }
    }
}
```

Let us store the ARN value returned in a variable
```bash
rolearn=arn:aws:iam::682200349629:role/lambda-function-executor
``` 

Now the role has been created and we need to specify its permissions. we can attach the policy `AWSLambdaBasicExecutionRole` using the `aws iam attach-role-policy` command as below

```bash
aws iam attach-role-policy --role-name lambda-function-executor \
--policy-arn arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole
```
We can find a list of other lambda permission policies [here](https://docs.aws.amazon.com/lambda/latest/dg/lambda-permissions.html#lambda-intro-execution-role).

#### 2. Deploy the golang build into Lambda function
Now, let us deploy the actual Lambda function to AWS using the command `aws lambda create-function`. This function takes the below flags and would take a few minutes to complete.

| Flag | Description |
|------|-------------|
| **--function-name** | The name our lambda function will be called within AWS |
| **--runtime** | The runtime environment for the lambda function (in our case "go1.x") |
| **--role**| The ARN of the role we want the lambda function to assume when it is running (obtained in the above step) |
| **--handler** | The name of the executable in the root of the zip file (in our case its main) |
| **--zip-file**| Path to the zip file we created in [Step 2.5](#cli-step-2-5) |

```bash
functionName=gettingstarted #you can give your preferred name
aws lambda create-function --function-name $functionName --runtime go1.x \
--role $rolearn \
--handler main --zip-file fileb://./output/function.zip
```
Response
```json
{
    "FunctionName": "gettingstarted",
    "FunctionArn": "arn:aws:lambda:us-east-2:682200349629:function:gettingstarted",
    "Runtime": "go1.x",
    "Role": "arn:aws:iam::682200349629:role/lambda-function-executor",
    "Handler": "main",
    "CodeSize": 4458249,
    "Description": "",
    "Timeout": 3,
    "MemorySize": 128,
    "LastModified": "2022-01-29T14:13:10.826+0000",
    "CodeSha256": "VwOZE7cyuYdCtMS2MH7KFaM9NgrQn2fChDyw3B2rnxE=",
    "Version": "$LATEST",
    "TracingConfig": {
        "Mode": "PassThrough"
    },
    "RevisionId": "e07cccd1-42b3-4c7b-9524-fc18b067a4d3",
    "State": "Pending",
    "StateReason": "The function is being created.",
    "StateReasonCode": "Creating",
    "PackageType": "Zip",
    "Architectures": [
        "x86_64"
    ]
}
```
we can store the function arn in a variable
```bash
functionarn=arn:aws:lambda:us-east2:682200349629:function:gettingstarted
```
You can validate or list all the functions using
```bash
aws lambda list-functions
```

Now our lambda function has been deployed and is now ready to use. we can try it out by using the `aws lambda invoke` command (which requires us to specify an output file for the response — we are using ./output/output.json in the snippet below).

```bash
aws lambda invoke --function-name gettingstarted ./output/output.json
```

We got the output
```json
{
    "StatusCode": 200,
    "ExecutedVersion": "$LATEST"
}
```

Now the Lambda function has been created and deployed.

### Setting up the HTTPS API in AWS API Gateway <a name="cli-step-4"></a>

1 . We can create a REST API using the command `aws apigateway create-rest-api`. we are choosing type as REGIONAL as we want the API gateway to be reachable from the region. We can also choose EDGE - if we want our API gateway to be accessible via AWS Cloudfront.

```bash
aws apigateway create-rest-api --name apitest --endpoint-configuration types=REGIONAL
```

Response
```json
{
    "id": "knvvn50y71",
    "name": "firstapi",
    "createdDate": "2022-01-29T20:14:39+05:30",
    "apiKeySource": "HEADER",
    "endpointConfiguration": {
        "types": [
            "REGIONAL"
        ]
    },
    "disableExecuteApiEndpoint": false
}
```

Let us store the `id` returned, we will be using it a lot
```bash
restApiId=knvvn50y71
```

2 . Next we need to get the id of the root API resource ("/"). We can retrieve this using the command `aws apigateway get-resources` using the api id obtained above

```bash
aws apigateway get-resources --rest-api-id $restApiId
``` 
Response
```json
{
    "items": [
        {
            "id": "4hsrin7w54",
            "path": "/"
        }
    ]
}
```
Again, let us take a note of the root-path-id value this returns as parentId (root=> here is parent)
```bash
parentId=4hsrin7w54
```

3 . Now we need to create a new resource under the root path — specifically a resource for the URL path `/mirror`. We can do this by using the `aws apigateway create-resource` command with the `--path-part` parameter

```bash
aws apigateway create-resource --rest-api-id $restApiId \
--parent-id $parentId --path-part mirror
```
Response
```json
{
    "id": "rgrcck",
    "parentId": "4hsrin7w54",
    "pathPart": "mirror",
    "path": "/mirror"
}
```
Again, let us take a note of the resource-id this returns.
```bash
resourceId=rgrcck
```

Let us understand that it's possible to include placeholders within the path by wrapping part of the path in curly braces. For example, a `--path-part` parameter of `mirror/{name}` would match requests to `/mirror/goku` and `/mirror/vegeta`, and the value of name would be made available to our lambda function via the `events object`. We can also make a placeholder greedy by postfixing it with a +. A common idiom is to use the parameter --path-part {proxy+} if we want to match all requests regardless of their path.

4 . But we will not be doing it, but rather read the values from a query parameter as decided in the start. Let's get back to our `/mirror` resource and use the `aws apigateway put-method` command to register the HTTP method of `ANY`. This will mean that our /mirror resource will respond to all requests regardless of their HTTP method.

```bash
aws apigateway put-method --rest-api-id $restApiId \
--resource-id $resourceId --http-method ANY \
--authorization-type NONE
```
Response
```json
{
    "httpMethod": "ANY",
    "authorizationType": "NONE",
    "apiKeyRequired": false
}
```

5 . Now let us integrate the api gateway with the lambda function using the command `aws apigateway put-integration`.

| Flag | Description |
|------|-------------|
| **--type** | Value should always be **`AWS_PROXY`** and this ensures the AWS API Gateway sends information about the HTTP request as an **`event`** to the lambda function and also automatically transform lambda function output to a HTTP response. |
| **--integration-http-method** | Value should be **`POST`** Let us not confuse this with what HTTP methods our API resource responds to. |
| **--uri** | parameter needs to take the format below |
```bash
arn:aws:apigateway:<region>:lambda:path/2015-03-31/functions/<our-lambda-function-arn>/invocations
```

If we have not copied our lambda function arn, we can get it using 
```bash
aws lambda list-functions
```
The account id of the current cli can be found using the below command
```bash
aws sts get-caller-identity
```

Now call the command
```bash
region=us-east-2
accountId=682200349629
uri="arn:aws:apigateway:""$region"":lambda:path/2015-03-31/functions/arn:aws:lambda:""$region"":""$accountId"":function:""$functionName""/invocations"
####
aws apigateway put-integration --rest-api-id $restApiId \
--resource-id $resourceId --http-method ANY --type AWS_PROXY \
--integration-http-method POST \
--uri $uri
```
Response
```json
    "type": "AWS_PROXY",
    "httpMethod": "POST",
    "uri": "arn:aws:apigateway:us-east-2:lambda:path/2015-03-31/functions/arn:aws:lambda:us-east-2:682200349629:function:gettingstarted/invocations",
    "passthroughBehavior": "WHEN_NO_MATCH",
    "timeoutInMillis": 29000,
    "cacheNamespace": "rgrcck",
    "cacheKeyParameters": []
}
```

6 . Now, when we test the REST API with the command `aws apigateway test-invoke-method` we will see an error

```bash
aws apigateway test-invoke-method --rest-api-id $restApiId --resource-id $resourceId --http-method "GET"
```

Error Response
```json
{
    "status": 500,
    "body": "{\"message\": \"Internal server error\"}",
    "headers": {
        "x-amzn-ErrorType": "InternalServerErrorException"
    },
    "multiValueHeaders": {
        "x-amzn-ErrorType": [
            "InternalServerErrorException"
        ]
    },
    "log": "Execution log for request 198564f9-28e4-4578-895d-290af2f6dee6\nSat Jan 29 16:36:37 UTC 2022 : Starting execution for request: 198564f9-28e4-4578-895d-290af2f6dee6\nSat Jan 29 16:36:37 UTC 2022 : HTTP Method: GET, Resource Path: /mirror\nSat Jan 29 16:36:37 UTC 2022 : Method request path: {}\nSat Jan 29 16:36:37 UTC 2022 : Method request query string: {}\nSat Jan 29 16:36:37 UTC 2022 : Method request headers: {}\nSat Jan 29 16:36:37 UTC 2022 : Method request body before transformations: \nSat Jan 29 16:36:37 UTC 2022 : Endpoint request URI: https://lambda.us-east-2.amazonaws.com/2015-03-31/functions/arn:aws:lambda:us-east-2:682200349629:function:gettingstarted/invocations\nSat Jan 29 16:36:37 UTC 2022 : Endpoint request headers: {X-Amz-Date=20220129T163637Z, x-amzn-apigateway-api-id=knvvn50y71, Accept=application/json, User-Agent=AmazonAPIGateway_knvvn50y71, Host=lambda.us-east-2.amazonaws.com, X-Amz-Content-Sha256=a33d05de2f7337bb6ae5302f16240b5f3e42be56141d90f6954dc0372771ea72, X-Amzn-Trace-Id=Root=1-61f56d15-1e3289d5c10359a5bb24075f, x-amzn-lambda-integration-tag=198564f9-28e4-4578-895d-290af2f6dee6, Authorization=*********************************************************************************************************************************************************************************************************************************************************************************************************************************************21274e, X-Amz-Source-Arn=arn:aws:execute-api:us-east-2:682200349629:knvvn50y71/test-invoke-stage/GET/mirror, X-Amz-Security-Token=IQoJb3JpZ2luX2VjEMj//////////wEaCXVzLWVhc3QtMiJHMEUCIQCE/xgic5zfU9dT5XjUcGjHKmXwV+z2m36xVB2BxHKFqgIgOjEBR08BSPnA6inOTdhJ2P92c0OA9cJaaQzA/zS [TRUNCATED]\nSat Jan 29 16:36:37 UTC 2022 : Endpoint request body after transformations: {\"resource\":\"/mirror\",\"path\":\"/mirror\",\"httpMethod\":\"GET\",\"headers\":null,\"multiValueHeaders\":null,\"queryStringParameters\":null,\"multiValueQueryStringParameters\":null,\"pathParameters\":null,\"stageVariables\":null,\"requestContext\":{\"resourceId\":\"rgrcck\",\"resourcePath\":\"/mirror\",\"httpMethod\":\"GET\",\"extendedRequestId\":\"Mt37aHfKiYcF_QQ=\",\"requestTime\":\"29/Jan/2022:16:36:37 +0000\",\"path\":\"/mirror\",\"accountId\":\"682200349629\",\"protocol\":\"HTTP/1.1\",\"stage\":\"test-invoke-stage\",\"domainPrefix\":\"testPrefix\",\"requestTimeEpoch\":1643474197654,\"requestId\":\"198564f9-28e4-4578-895d-290af2f6dee6\",\"identity\":{\"cognitoIdentityPoolId\":null,\"cognitoIdentityId\":null,\"apiKey\":\"test-invoke-api-key\",\"principalOrgId\":null,\"cognitoAuthenticationType\":null,\"userArn\":\"arn:aws:iam::682200349629:user/preetham\",\"apiKeyId\":\"test-invoke-api-key-id\",\"userAgent\":\"aws-cli/2.2.44 Python/3.8.8 Darwin/20.5.0 exe/x86_64 prompt/off command/apigateway.test-invoke-method\",\"accountId\":\"682200349629\",\"caller\":\"AIDA [TRUNCATED]\nSat Jan 29 16:36:37 UTC 2022 : Sending request to https://lambda.us-east-2.amazonaws.com/2015-03-31/functions/arn:aws:lambda:us-east-2:682200349629:function:gettingstarted/invocations\nSat Jan 29 16:36:37 UTC 2022 : Execution failed due to configuration error: Invalid permissions on Lambda function\nSat Jan 29 16:36:37 UTC 2022 : Method completed with status: 500\n",
"latency": 36
}
```

So if we try to isolate the error, it says
`Execution failed due to configuration error: Invalid permissions on Lambda function`

This is because our API gateway doesn't have permission to execute our lambda function

7 . we can use `aws lambda add-permission` command to give the API permissions to invoke the lambda

we are generating a random uid using uuidgen command and the region and accountId is part of the session variable as we have defined in one of the previous steps

```bash
uid=`uuidgen`
sourceArn="arn:aws:execute-api:""$region"":""$accountId"":""$restApiId""/*/*/*"
aws lambda add-permission --profile preetham --function-name $functionName --statement-id $uid \
--action lambda:InvokeFunction --principal apigateway.amazonaws.com \
--source-arn $sourceArn
```
Response
```json
{
    "Statement": "{\"Sid\":\"E7B0C866-B985-4F0D-8DEA-AE560A04EBFE\",\"Effect\":\"Allow\",\"Principal\":{\"Service\":\"apigateway.amazonaws.com\"},\"Action\":\"lambda:InvokeFunction\",\"Resource\":\"arn:aws:lambda:us-east-2:682200349629:function:gettingstarted\",\"Condition\":{\"ArnLike\":{\"AWS:SourceArn\":\"arn:aws:execute-api:us-east-2:682200349629:knvvn50y71/*/*/*\"}}}"
}
```

8 . we can test the api gateway again
```bash
aws apigateway test-invoke-method --rest-api-id $restApiId --resource-id $resourceId --http-method "GET"
```
Response
```json
{
    "status": 500,
    "body": "Error: Query Parameter name missing",
    "headers": {
        "X-Amzn-Trace-Id": "Root=1-61f57957-74340f97bf9f0046a2c78983;Sampled=0"
    },
    "multiValueHeaders": {
        "X-Amzn-Trace-Id": [
            "Root=1-61f57957-74340f97bf9f0046a2c78983;Sampled=0"
        ]
    },
    "log": "Execution log for request 0f285ec1-58ec-46be-be7b-2f002efcdc75\nSat Jan 29 17:28:55 UTC 2022 : Starting execution for request: 0f285ec1-58ec-46be-be7b-2f002efcdc75\nSat Jan 29 17:28:55 UTC 2022 : HTTP Method: GET, Resource Path: /mirror\nSat Jan 29 17:28:55 UTC 2022 : Method request path: {}\nSat Jan 29 17:28:55 UTC 2022 : Method request query string: {}\nSat Jan 29 17:28:55 UTC 2022 : Method request headers: {}\nSat Jan 29 17:28:55 UTC 2022 : Method request body before transformations: \nSat Jan 29 17:28:55 UTC 2022 : Endpoint request URI: https://lambda.us-east-2.amazonaws.com/2015-03-31/functions/arn:aws:lambda:us-east-2:682200349629:function:gettingstarted/invocations\nSat Jan 29 17:28:55 UTC 2022 : Endpoint request headers: {X-Amz-Date=20220129T172855Z, x-amzn-apigateway-api-id=knvvn50y71, Accept=application/json, User-Agent=AmazonAPIGateway_knvvn50y71, Host=lambda.us-east-2.amazonaws.com, X-Amz-Content-Sha256=7890b63bd5654eb21207f4af1bcb751ba84c0498a9a9929e309dafd30f9e8cc8, X-Amzn-Trace-Id=Root=1-61f57957-74340f97bf9f0046a2c78983, x-amzn-lambda-integration-tag=0f285ec1-58ec-46be-be7b-2f002efcdc75, Authorization=*********************************************************************************************************************************************************************************************************************************************************************************************************************************************649380, X-Amz-Source-Arn=arn:aws:execute-api:us-east-2:682200349629:knvvn50y71/test-invoke-stage/GET/mirror, X-Amz-Security-Token=IQoJb3JpZ2luX2VjEMn//////////wEaCXVzLWVhc3QtMiJHMEUCIBki4ycUlnDnl5O2JNLu3QZ4YxX6e+oAoPgBMXAZDNb+AiEA6DzZsiRL6GIEAmFjwfPISLGV2cQiMW+IIGk0AG5 [TRUNCATED]\nSat Jan 29 17:28:55 UTC 2022 : Endpoint request body after transformations: {\"resource\":\"/mirror\",\"path\":\"/mirror\",\"httpMethod\":\"GET\",\"headers\":null,\"multiValueHeaders\":null,\"queryStringParameters\":null,\"multiValueQueryStringParameters\":null,\"pathParameters\":null,\"stageVariables\":null,\"requestContext\":{\"resourceId\":\"rgrcck\",\"resourcePath\":\"/mirror\",\"httpMethod\":\"GET\",\"extendedRequestId\":\"Mt_lvGoICYcF6zQ=\",\"requestTime\":\"29/Jan/2022:17:28:55 +0000\",\"path\":\"/mirror\",\"accountId\":\"682200349629\",\"protocol\":\"HTTP/1.1\",\"stage\":\"test-invoke-stage\",\"domainPrefix\":\"testPrefix\",\"requestTimeEpoch\":1643477335759,\"requestId\":\"0f285ec1-58ec-46be-be7b-2f002efcdc75\",\"identity\":{\"cognitoIdentityPoolId\":null,\"cognitoIdentityId\":null,\"apiKey\":\"test-invoke-api-key\",\"principalOrgId\":null,\"cognitoAuthenticationType\":null,\"userArn\":\"arn:aws:iam::682200349629:user/preetham\",\"apiKeyId\":\"test-invoke-api-key-id\",\"userAgent\":\"aws-cli/2.2.44 Python/3.8.8 Darwin/20.5.0 exe/x86_64 prompt/off command/apigateway.test-invoke-method\",\"accountId\":\"682200349629\",\"caller\":\"AIDA [TRUNCATED]\nSat Jan 29 17:28:55 UTC 2022 : Sending request to https://lambda.us-east-2.amazonaws.com/2015-03-31/functions/arn:aws:lambda:us-east-2:682200349629:function:gettingstarted/invocations\nSat Jan 29 17:28:56 UTC 2022 : Received response. Status: 200, Integration latency: 292 ms\nSat Jan 29 17:28:56 UTC 2022 : Endpoint response headers: {Date=Sat, 29 Jan 2022 17:28:56 GMT, Content-Type=application/json, Content-Length=103, Connection=keep-alive, x-amzn-RequestId=60d92f89-317c-4c29-894d-c157bcc9b79f, x-amzn-Remapped-Content-Length=0, X-Amz-Executed-Version=$LATEST, X-Amzn-Trace-Id=root=1-61f57957-74340f97bf9f0046a2c78983;sampled=0}\nSat Jan 29 17:28:56 UTC 2022 : Endpoint response body before transformations: {\"statusCode\":500,\"headers\":null,\"multiValueHeaders\":null,\"body\":\"Error: Query Parameter name missing\"}\nSat Jan 29 17:28:56 UTC 2022 : Method response body after transformations: Error: Query Parameter name missing\nSat Jan 29 17:28:56 UTC 2022 : Method response headers: {X-Amzn-Trace-Id=Root=1-61f57957-74340f97bf9f0046a2c78983;Sampled=0}\nSat Jan 29 17:28:56 UTC 2022 : Method completed with status: 500\n",
    "latency": 307
}
```

We can see that the error 
```bash
Error: Query Parameter name missing
``` 
which is the same as configured in our golang code. Yay !!

This is fine, but now we cannot execute this via an URL.

### Deploying the HTTP API created <a name="cli-step-5"></a>

We can run the below command to make it live
```bash
aws apigateway create-deployment --rest-api-id $restApiId \
--stage-name staging
```
Response
```json
{
    "id": "4pdblq",
    "createdDate": 1522929303
}
```

Now, the url can be accessed by
`https://<rest-api-id>.execute-api.<region>.amazonaws.com/staging/mirror`

```bash
url="https://""$restApiId"".execute-api.""$region"".amazonaws.com/staging/mirror"
```
**GET Method**
```bash
curl --location --request GET $url
```
Response
```json
Hey Goku welcome!
```
**POST Method**
```bash
curl --location --request POST $url \
--header 'Content-Type: application/json' \
--data-raw '{
    "country" : "India"
}'
```
Response
```json
{
    "country" : "India"
}
```

Now, for our function, if we want to make any changes or rebuild the code, we can rebuild it, zip it and upload it by by the command

```bash
aws lambda update-function-code --function-name $functionName \
--zip-file fileb:///tmp/function.zip
```

With this, we will be able to build a simple AWS Lambda REST API with Golang. Now, you can edit the handler function to suit your requirement.


Cover Image Source: https://blog.travelex.io/blazing-fast-microservice-with-go-and-lambda-d30d95290f28