11 min read

Building a serverless application allows you to focus on your application code instead of managing and operating infrastructure. If you choose AWS for this purpose, you do not have to think about provisioning or configuring servers since AWS will handle all of this for you. This reduces your infrastructure management burden and helps you get faster time-to-market.

This tutorial is an excerpt taken from the book Hands-On Serverless Applications with Go written by Mohamed Labouardy. In this book, you will learn how to design and build a production-ready application in Go using AWS serverless services with zero upfront infrastructure investment.

This article will cover the following points:

  • Build, deploy, and manage our Lambda functions going through some advanced AWS CLI commands
  • Publish multiple versions of the API
  • Learn how to separate multiple deployment environments (sandbox, staging, and production) with aliases
  • Cover the usage of the API Gateway stage variables to change the method endpoint’s behavior.

Lambda CLI commands

In this section, we will go through the various AWS Lambda commands that you might use while building your Lambda functions. We will also learn how you can use them to automate your deployment process.

The list-functions command

As its name implies, it lists all Lambda functions in the AWS region you provided. The following command will return all Lambda functions in the North Virginia region:

aws lambda list-functions --region us-east-1

For each function, the response includes the function’s configuration information (FunctionName, Resources usage, Environment variables, IAM Role, Runtime environment, and so on), as shown in the following screenshot:

To list only some attributes, such as the function name, you can use the query filter option, as follows:

aws lambda list-functions --query Functions[].FunctionName[]

The create-function command

You should be familiar with this command as it has been used multiple times to create a new Lambda function from scratch.

In addition to the function’s configuration, you can use the command to provide the deployment package (ZIP) in two ways:

  • ZIP file: It provides the path to the ZIP file of the code you are uploading with the --zip-file option:
aws lambda create-function --function-name UpdateMovie \
    --description "Update an existing movie" \
    --runtime go1.x \
    --role arn:aws:iam::ACCOUNT_ID:role/UpdateMovieRole \
    --handler main \
    --environment Variables={TABLE_NAME=movies} \
    --zip-file fileb://./deployment.zip \
    --region us-east-1a
  • S3 Bucket object: It  provides the S3 bucket and object name with the --code option:
aws lambda create-function --function-name UpdateMovie \
    --description "Update an existing movie" \
    --runtime go1.x \
    --role arn:aws:iam::ACCOUNT_ID:role/UpdateMovieRole \
    --handler main \
    --environment Variables={TABLE_NAME=movies} \
    --code S3Bucket=movies-api-deployment-package,S3Key=deployment.zip \
    --region us-east-1

The as-mentioned commands will return a summary of the function’s settings in a JSON format, as follows:

It’s worth mentioning that while creating your Lambda function, you might override the compute usage and network settings based on your function’s behavior with the following options:

  • --timeout: The default execution timeout is three seconds. When the three seconds are reached, AWS Lambda terminates your function. The maximum timeout you can set is five minutes.
  • --memory-size: The amount of memory given to your function when executed. The default value is 128 MB and the maximum is 3,008 MB (increments of 64 MB).
  • --vpc-config: This deploys the Lambda function in a private VPC. While it might be useful if the function requires communication with internal resources, it should ideally be avoided as it impacts the Lambda performance and scaling.

AWS doesn’t allow you to set the CPU usage of your function as it’s calculated automatically based on the memory allocated for your function. CPU usage is proportional to the memory.

The update-function-code command

In addition to AWS Management Console, you can update your Lambda function’s code with AWS CLI. The command requires the target Lambda function name and the new deployment package. Similarly to the previous command, you can provide the package as follows:

  • The path to the new .zip file:
aws lambda update-function-code --function-name UpdateMovie \
    --zip-file fileb://./deployment-1.0.0.zip \
    --region us-east-1
  • The S3 bucket where the .zip file is stored:
aws lambda update-function-code --function-name UpdateMovie \
    --s3-bucket movies-api-deployment-packages \
    --s3-key deployment-1.0.0.zip \
    --region us-east-1

 

This operation prints a new unique ID (called RevisionId) for each change in the Lambda function’s code:

The get-function-configuration command

In order to retrieve the configuration information of a Lambda function, issue the following command:

aws lambda get-function-configuration --function-name UpdateMovie --region us-east-1

The preceding command will provide the same information in the output that was displayed when the create-function command was used.

To retrieve configuration information for a specific Lambda version or alias (following section), you can use the --qualifier option.

The invoke command

So far, we invoked our Lambda functions directly from AWS Lambda Console and through HTTP events with API Gateway. In addition to that, Lambda can be invoked from the AWS CLI with the invoke command:

aws lambda invoke --function-name UpdateMovie result.json

The preceding command will invoke the UpdateMovie function and save the function’s output in result.json file:

The status code is 400, which is normal, as UpdateFunction is expecting a JSON input. Let’s see how to provide a JSON to our function with the invoke command.

Head back to the DynamoDB movies table, and pick up a movie that you want to update. In this example, we will update the movie with the ID as 13, shown as follows:

Create a JSON file with a body attribute that contains the new movie item attribute, as the Lambda function is expecting the input to be in the API Gateway Proxy request format:

{
  "body": "{\"id\":\"13\", \"name\":\"Deadpool 2\"}"
}

Finally, run the invoke function command again with the JSON file as the input parameter:

aws lambda invoke --function UpdateMovie --payload file://input.json result.json

If you print the result.json content, the updated movie should be returned, shown as follows:

You can verify that the movie’s name is updated in the DynamoDB table by invoking the FindAllMovies function:

aws lambda invoke --function-name FindAllMovies result.json

The body attribute should contain the new updated movie, shown as follows:

Head back to DynamoDB Console; the movie with the ID of 13 should have a new name, as shown in the following  screenshot:

The delete-function command

To delete a Lambda function, you can use the following command:

aws lambda delete-function --function-name UpdateMovie

By default, the command will delete all function versions and aliases. To delete a specific version or alias, you might want to use the --qualifier option.

By now, you should be familiar with all the AWS CLI commands you might use and need while building your serverless applications in AWS Lambda. In the upcoming section, we will see how to create different versions of your Lambda functions and maintain multiple environments with aliases.

Versions and aliases

When you’re building your serverless application, you must separate your deployment environments to test new changes without impacting your production. Therefore, having multiple versions of your Lambda functions makes sense.

Versioning

A version represents a state of your function’s code and configuration in time. By default, each Lambda function has the $LATEST version pointing to the latest changes of your function, as shown in the following screenshot:

In order to create a new version from the $LATEST version, click on Actions and Publish new version. Let’s call it 1.0.0, as shown in  the next screenshot:

 

The new version will be created with an ID=1 (incremental). Note the ARN Lambda function at the top of the window in the following screenshot; it has the version ID:

Once the version is created, you cannot update the function code, shown as follows:

Moreover, advanced settings, such as IAM roles, network configuration, and compute usage, cannot be changed, shown as follows:

Versions are called immutable, which means they cannot be changed once they’re published; only the $LATEST version is editable.

Now, we know how to publish a new version from the console. Let’s publish a new version with the AWS CLI. But first, we need to update the FindAllMovies function as we cannot publish a new version if no changes were made to $LATEST since publishing version 1.0.0.

The new version will have a pagination system. The function will return only the number of items requested by the user. The following code will read the Count header parameter, convert it to a number, and use the Scan operation with the Limit parameter to fetch the movies from DynamoDB:

func findAll(request events.APIGatewayProxyRequest) (events.APIGatewayProxyResponse, error) {
  size, err := strconv.Atoi(request.Headers["Count"])
  if err != nil {
    return events.APIGatewayProxyResponse{
      StatusCode: http.StatusBadRequest,
      Body: "Count Header should be a number",
    }, nil
  }

svc := dynamodb.New(cfg)
req := svc.ScanRequest(&dynamodb.ScanInput{
TableName: aws.String(os.Getenv(“TABLE_NAME”)),
Limit: aws.Int64(int64(size)),
})


}

Next, we update the FindAllMovies Lambda function’s code with the update-function-code command:

aws lambda update-function-code --function-name FindAllMovies \
    --zip-file fileb://./deployment.zip

Then, publish a new version, 1.1.0, based on the current configuration and code with the following command:

aws lambda publish-version --function-name FindAllMovies --description 1.1.0

Go back to AWS Lambda Console and navigate to your FindAllMovies; a new version should be created with a new ID=2, as shown in the following screenshot:

Now that our versions are created, let’s test them out by using the AWS CLI invoke command.

FindAllMovies v1.0.0

Invoke the FindAllMovies v1.0.0 version with its ID in the qualifier parameter with the following command:

aws lambda invoke --function-name FindAllMovies --qualifier 1 result.json

result.json should have all the movies in the DynamoDB movies table, shown as follows:

The output showing all the movies in the DynamoDB movies tableTo know more about the output in the FindAllMovies v1.1.0 and more about Semantic versioning, head over to the book.

Aliases

The alias is a pointer to a specific version, it allows you to promote a function from one environment to another (such as staging to production). Aliases are mutable, unlike versions, which are immutable.

To illustrate the concept of aliases, we will create two aliases, as illustrated in the following diagram: a Production alias pointing to FindAllMovies Lambda function 1.0.0 version, and a Staging alias that points to function 1.1.0 version. Then, we will configure API Gateway to use these aliases instead of the $LATEST version:

Head back to the FindAllMovies configuration page. If you click on the Qualifiers drop-down list, you should see a default alias called Unqualified pointing to your $LATEST version, as shown in the following screenshot:

To create a new alias, click on Actions and then Create a new alias called Staging. Select the 5 version as the target, shown as follows:

Once created, the new version should be added to the list of Aliases, shown as follows:

Next, create a new alias for the Production environment that points to version 1.0.0 using the AWS command line:

aws lambda create-alias --function-name FindAllMovies \
    --name Production --description "Production environment" \
    --function-version 1

Similarly, the new alias should be successfully created:

Now that our aliases have been created, let’s configure the API Gateway to use those aliases with Stage variables.

Stage variables

Stage variables are environment variables that can be used to change the behavior at runtime of the API Gateway methods for each deployment stage. The following section will illustrate how to use stage variables with API Gateway.

 

On the API Gateway Console, navigate to the Movies API, click on the GET method, and update the target Lambda Function to use a stage variable instead of a hardcoded Lambda function name, as shown in the following screenshot:

When you save it, a new prompt will ask you to grant the permissions to API Gateway to call your Lambda function aliases, as shown in the following screenshot:

Execute the following commands to allow API Gateway to invoke the Production and Staging aliases:

  • Production alias:
aws lambda add-permission --function-name "arn:aws:lambda:us-east-1:ACCOUNT_ID:function:FindAllMovies:Production" \
    --source-arn "arn:aws:execute-api:us-east-1:ACCOUNT_ID:API_ID/*/GET/movies" \
    --principal apigateway.amazonaws.com \
    --statement-id STATEMENT_ID \
    --action lambda:InvokeFunction
  • Staging alias:
aws lambda add-permission --function-name "arn:aws:lambda:us-east-1:ACCOUNT_ID:function:FindAllMovies:Staging" \
    --source-arn "arn:aws:execute-api:us-east-1:ACCOUNT_ID:API_ID/*/GET/movies" \
    --principal apigateway.amazonaws.com \
    --statement-id STATEMENT_ID \
    --action lambda:InvokeFunction

Then, create a new stage called production, as shown in next screenshot:

Next, click on the Stages Variables tab, and create a new stage variable called lambda and set FindAllMovies:Production as a value, shown as follows:

Do the same for the staging environment with the lambda variable pointing to the Lambda function’s Staging alias, shown as follows:

To test the endpoint, use the cURL command or any REST client you’re familiar with. I opt for Postman. A GET method on the API Gateway’s production stage invoked URL should return all the movies in the database, shown as follows:

Do the same for the staging environment, with a new Header key called Count=4; you should have only four movies items in return, shown as follows:

That’s how you can maintain multiple environments of your Lambda functions. You can now easily promote the 1.1.0 version into production by changing the Production pointer to point to 1.1.0 instead of 1.0.0, and roll back in case of failure to the previous working version without changing the API Gateway settings.

To summarize, we learned about how to deploy serverless applications using the AWS Lambda functions. If you’ve enjoyed reading this and want to know more about how to set up a CI/CD pipeline from scratch to automate the process of deploying Lambda functions to production with Go programming language, check out our book, Hands-On Serverless Applications with Go.

Read Next

Keep your serverless AWS applications secure [Tutorial]

Azure Functions 2.0 launches with better workload support for serverless

How Serverless computing is making AI development easier

A Data science fanatic. Loves to be updated with the tech happenings around the globe. Loves singing and composing songs. Believes in putting the art in smart.