DEV Community

Cover image for How to build an API with AWS Lambda and the Serverless Application Model (SAM)
Sean Ziegler
Sean Ziegler

Posted on • Originally published at seanjziegler.com

How to build an API with AWS Lambda and the Serverless Application Model (SAM)

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 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 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 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.

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. sam deploy --guided starts an interactive session which guides you 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 generated events

SAM provides a method for testing your API locally prior to deployment. 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.

Configuring an API Key and usage plan

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.

Top comments (0)