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)