Home Cloud How to quickly build an API with Lambda, API Gateway, and AWS SAM

How to quickly build an API with Lambda, API Gateway, and AWS SAM

by Sean Ziegler

Designing an API on AWS is hard. Designing a reliable API on AWS is even harder. The AWS Serverless Application Model (SAM) is an open source serverless framework that makes deploying serverless resources much easier. SAM extends CloudFormation, so you can use all the usual CloudFormation resources inside of a SAM template. SAM is a full-featured serverless can deploy serverless functions, databases, and APIs.

In this post I’ll be covering the basics of using the AWS SAM template language, the AWS SAM CLI, and some advanced features like usage plans and API keys.

The SAM template language and the SAM CLI

SAM is composed of two parts, the SAM template language and the SAM CLI. The SAM template language provides a method of describing serverless resources in JSON or YAML blocks. The SAM template language provides seven resource types.

  • AWS::Serverless::Api
  • AWS::Serverless::Application
  • AWS::Serverless::Function
  • AWS::Serverless::HttpApi
  • AWS::Serverless::LayerVersion
  • AWS::Serverless::SimpleTable
  • AWS::Serverless::StateMachine

For example, I can define a Lambda function using an AWS::Serverless::Function block. Here I’ve created a Lambda function using python3.6 and SAM will store the code in an S3 bucket.

Type: AWS::Serverless::Function
Properties:
  Handler: index.handler
  Runtime: python3.6
  CodeUri: s3://bucket/key

The SAM CLI provides a command-line interface for building, testing, and deploying the serverless resources described by your SAM templates. The SAM CLI has eight commands.

  • sam build – Processes your template file, prepares code and dependencies for deployment
  • sam deploy – Deploys resources described in SAM template
  • sam init – Initializes a new SAM project
  • sam local – Creates local version of serverless resources for easier testing and debugging
  • sam logs – Show your resources’s logs
  • sam package – Creates a zip and uploads an artifact to S3
  • sam validate – Validates template file is valid CloudFormation syntax
  • sam publish – Publishes an application to the Serverless Application Repository

Start a SAM project with the SAM CLI

Let’s get started building a simple API that takes two URL parameters, a and b, and returns their product.

AWS SAM has a CLI that makes creating a new project simple. Install the AWS SAM CLI and then use sam init to start a new project. I like to use the quick start templates to get a project going quickly, but you can also select 2 for a Custom Template Location and give it a filepath or URL.

sam init

Which template source would you like to use?
	1 - AWS Quick Start Templates
	2 - Custom Template Location

Choice: 1

Choose a language for your project’s runtime. I’ll be using python3.6.

Which runtime would you like to use?
	1 - nodejs12.x
	2 - python3.8
	3 - ruby2.7
	4 - go1.x
	5 - java11
	6 - dotnetcore3.1
	7 - nodejs10.x
	8 - python3.7
	9 - python3.6
	10 - python2.7
	11 - ruby2.5
	12 - java8
	13 - dotnetcore2.1
	14 - dotnetcore2.0
	15 - dotnetcore1.0
Runtime: 9

Name the project and select the Hello World template. This will generate a sample project with a Lambda function and a template for an API Gateway resource.

Project name [sam-app]: SAMdemo

Cloning app templates from https://github.com/awslabs/aws-sam-cli-app-templates.git

AWS quick start application templates:
	1 - Hello World Example
	2 - EventBridge Hello World
	3 - EventBridge App from scratch (100+ Event Schemas)
	4 - Step Functions Sample App (Stock Trader)
	5 - Elastic File System Sample App
Template selection: 1

-----------------------
Generating application:
-----------------------
Name: SAMdemo
Runtime: python3.6
Dependency Manager: pip
Application Template: hello-world
Output Directory: .

Next steps can be found in the README file at ./SAMdemo/README.md

SAM generates a bunch of boilerplate code and a few folders for you. There are four main parts to know.

  • template.yaml – Defines your SAM resources
  • hello_world/ – Directory for Lambda function code
  • tests/ – Directory for unit tests for your function, run using Pytest
  • events/ – Mocked events for testing functions

Create a Lambda function

We need to create a function that can take in two URL parameters and return a product. I will reuse the hello_world function the SAM CLI generated by renaming the hello_world folder multiply and editing the app.py file inside it.

mv hello_world/ multiply/
vim multiply/app.py

The app.py file defines the Lambda function. This function will take in two integers, a and b, as URL parameters and return their product. We can access the URL parameters through the event variable. The event variable contains data about the API Gateway event that triggered this Lambda function.

import json

def lambda_handler(event, context):
    '''<snip>'''

    a = event["queryStringParameters"]['a']
    b = event["queryStringParameters"]['b']

    product = int(a) * int(b)

    return {
        "statusCode": 200,
        "body": json.dumps({
            "product": product,
        }),
    }

Lambda functions must return a JSON response, so I’ve chosen to just dump the product result into the body of the response.

Define an API Gateway and connect to Lambda using a SAM template

SAM also generated a file called template.yaml. I will tell SAM that I want to deploy a Lambda function by including an AWS::Serverless:Function block inside the SAM template. In the same block, I’ll include a link to the API Gateway MultiplyAPI to trigger the function with API Gateway.

AWSTemplateFormatVersion: '2010-09-09'
Transform: AWS::Serverless-2016-10-31
Description: Function and API for multiplying two numbers

Resources:
  MultiplyFunction:
    Type: AWS::Serverless::Function
    Properties:
      CodeUri: multiply/
      Handler: app.lambda_handler
      Runtime: python3.6
      Events:
         Multiply:
           Type: Api
           Properties:
             Path: /multiply
             Method: POST
             RestApiId:
               Ref: MultiplyApi
Outputs:
  MultiplyApi:
    Description: "API Gateway endpoint URL for Prod stage for Multiply function"
    Value: !Sub "https://${ServerlessRestApi}.execute-api.${AWS::Region}.amazonaws.com/Prod/multiply/"
  MultiplyFunction:
    Description: "Multiply Lambda Function ARN"
    Value: !GetAtt MultiplyFunction.Arn
  MultiplyFunctionIamRole:
    Description: "Implicit IAM Role created for Multiply function"
    Value: !GetAtt MultiplyFunctionRole.Arn

This template will deploy a Lambda function backed by the code in the multiply folder. After the deployment, the Outputs section will return an API Gateway Endpoint, a Lambda function ARN, and an IAM role for the function.

Deploy the SAM template with the SAM CLI

It is time to deploy the SAM template. I’ll be using the SAM CLI’s sam deploy --guided command to start an interactive session which will guide me through the deployment process.

~/SAMdemo ❯ sam deploy --guided                                                         

Configuring SAM deploy
======================

	Looking for samconfig.toml :  Not found

	Setting default arguments for 'sam deploy'
	=========================================
	Stack Name [sam-app]: SAMDemo
	AWS Region [us-east-1]: 
	#Shows you resources changes to be deployed and require a 'Y' to initiate deploy
	Confirm changes before deploy [y/N]: N
	#SAM needs permission to be able to create roles to connect to the resources in your template
	Allow SAM CLI IAM role creation [Y/n]: Y
	MultiplyFunction may not have authorization defined, Is this okay? [y/N]: Y
	Save arguments to samconfig.toml [Y/n]: Y

After you finish the guided process, SAM will generate a change set (Just like CloudFormation) and deploy your resources. You will see the outputs from template.yaml printed onto the screen.

CloudFormation outputs from deployed stack
-------------------------------------------------------------------------------------------------
Outputs
-------------------------------------------------------------------------------------------------
Key                 MultiplyFunctionIamRole
Description         Implicit IAM Role created for Multiply function
Value               arn:aws:iam::<snip>

Key                 MultiplyFunction
Description         Multiply Lambda Function ARN
Value               arn:aws:lambda:us-east-1:<snip>
Key                 MultiplyApi
Description         API Gateway endpoint URL for Prod stage for Multiply function
Value               https://<snip>.execute-api.us-east-1.amazonaws.com/Prod/multiply/
-------------------------------------------------------------------------------------------------

Successfully created/updated stack - SAMDemo in us-east-1

Testing the API with SAM local

SAM provides a method for testing your API locally prior to deployment called sam local. We can use it to generate mock API events and use them to test locally on a development machine.

First, let’s generate a mock API Gateway event. The command sam local generate-event apigateway aws-proxy > events/multiply.json will generate a fake API event and save it to a JSON file. Change the queryStringParameters in multiply.json to some integers for a and b.

"queryStringParameters": {
    "foo": "bar"
  },
  "queryStringParameters": {
    "a": "5",
    "b": "3"
  }

Now lets use sam local invoke to invoke the Lambda function and provide it the multiply event.

sam local invoke -e multiply.json

Invoking app.lambda_handler (python3.8)

Fetching lambci/lambda:python3.8 Docker container image......
Mounting /Users/seanziegler/Coding/SAMdemo/multiply as /var/task:ro,delegated inside runtime container
START RequestId: a17fdb0c-15bc-1cb0-c8a5-836299856b0c Version: $LATEST
END RequestId: a17fdb0c-15bc-1cb0-c8a5-836299856b0c
REPORT RequestId: a17fdb0c-15bc-1cb0-c8a5-836299856b0c	Init Duration: 93.96 ms	Duration: 2.85 ms	Billed Duration: 100 ms	Memory Size: 128 MB	Max Memory Used: 25 MB

{"statusCode":200,"body":"{\"product\": 15}"}

The function returned product: 15 which is what we expected. You can use sam local generate event to generate mock events for many services including S3, APIGateway, DynamoDB, SQS, and more. Generating events and testing locally is a great pattern for ensuring your serverless functions are reliable.

Adding API Gateway Authentication with SAM

Adding API keys and a usage plan to an API is a straightforward process. It’s possible to set up both using the Auth object on AWS::Serverless::Api.

On the Multiply route I will require an API key, limit requests to 500 per day, and limit requests to 5 requests per second.

MultiplyApi:
     Type: AWS::Serverless::Api
     Properties:
       StageName: Prod
       Auth:
         ApiKeyRequired: true
         UsagePlan:
           CreateUsagePlan: PER_API
           Quota:
             Limit: 500
             Period: DAY
           Throttle:
             RateLimit: 5

Let’s make a request and see what happens.

curl -X POST https://<snip>.execute-api.us-east-1.amazonaws.com/Prod/multiply?a=3&b=1

{"message": "Missing Authentication Token"}

Okay, that didn’t work because I didn’t supply an API key.

Let’s try it again with an API key I generated for myself earlier.

curl -X POST -H "x-api-key:<API KEY>" https://<snip>.execute-api.us-east-1.amazonaws.com/Prod/multiply?a=5&b=3

{"product": 15}

Conclusion

That’s all it takes to build a small API using SAM. You can extend this API by adding more routes and functions as resource declarations in the template.yaml file. The SAM template language reference and the SAM CLI reference are your friends.

You may also like

4 comments

chetana September 21, 2020 - 10:57 AM

Reason: Unresolved resource dependencies [ServerlessRestApi] in the Outputs block of the template

Tried to follow your code but its failing with above issue ..can you help if am i missing something or could you please provide template.yaml code completely ?

Reply
Vince Fulco October 3, 2020 - 3:19 AM

You can hard code the name for the MultiplyApi in place of the ${foo}. Also the template requires creation of the API resource. Otherwise a good read.

Reply
Vince Fulco October 3, 2020 - 3:33 AM

Apologies, I was mistaken with the hard coding.

Add this in the Resources section:

MultiplyApi:
Type: AWS::Serverless::Api
Properties:
StageName: Prod
Name: MultiplyApi

Then in the outputs section:

Value: !Sub “https://${MultiplyApi}.${AWS::Region}.amazonaws.com/Prod/multiply/”

Should be good to go then. Cheers.

Reply
Mark October 26, 2020 - 12:13 AM

Thank you for this write up, I had been searching for an example with Type: AWS::Serverless::Api

Here are some issues I had, perhaps they will save others from same problems I encountered. (2020_10Oct_25)

[1] For the first deploy, the RestApiId, prevented deployment, since it is not defined until the MultipyApi resource is added for the second deploy.
RestApiId: Ref: MultiplyApi
I just removed it, then added it back for the second deploy

[2] Super minor cut and paste issue, in the multiply.json file, if using the copy button in blog, you will need to add the comma after the ‘}’ in queryStringParameters value specification.

[3] since I didn’t change directories after the ‘sam local generate-event … > events/multiply.json’, I need to change the -e on then next command to include the events subdirectory
sam local invoke -e multiply.json

[4] I needed to add ‘Name: MultiplyApi’ to MulitiplyApi resource as in Vince’s Oct 3 comment

[5] I needed to add ‘execute-api’ to the output change suggested by Vince’s Oct 3 comment
Value: !Sub "https://${MultiplyApi}.execute-api.${AWS::Region}.amazonaws.com/Prod/multiply/"

[6] I needed to add a BurstLimit to Throttle, it was defaulting to zero, and giving me limit exceeded errors
Throttle: RateLimit: 5 BurstLimit: 15

[7] on the curl command, I need to put the URL in double quotes, otherwise it was using & in URL as command line termination and since I wasn’t then passing a b parameter, I was getting an Internal Server Error

Reply

Leave a comment

This website uses cookies to improve your experience. We'll assume you're ok with this, but you can opt-out if you wish. Accept