...as an alternative to the Serverless Framework.
- SAM templates result in Cloudformation stacks. This has a benefit of ensuring all the resoures are defined together and allows the management from a GUI.
- SAM requires that IAM policies to be defined at the function level rather than globally as per the Serverless Framework. According to the SAM docs, this was a design decision to reduce the risk of functions being granted more permissions than they need.
- Enabling CORS in SAM requires the specific allowed methods and headers to be provided. This is as opposed to Serverless Framework where it is just an on/off flag.
- SAM supports parameters to be passed to the template at deploy/test-time. Serverless Framework achieves this with environment variables.
- SAM does not have build plugins like the Serverless Framework.
- As SAM does not have plugins, it can't orchestrate the ES5 bundling that is done in Serverless Framework with the
serveless-bundle
plugin. Instead, this is achieved with the combination ofnode
andwebpack
rather than relying onsam build
. - As a side effect of the ES5 use, SAM requires the build to be done before being able to deploy or test code locally. This can be a cause for confusion when starting out as one may try to
deploy
orinvoke
and wonder why the code changes aren't being reflected. I got into the habit of building first withnpm run-script build && sam deploy
. - In SAM, when we set the
DefaultAuthorizer: AWS_IAM
in theGlobal
section, it will break theOPTIONS
request when the API is called from a browser. That is because the global flags sets authentication even for theOPTIONS
request which the Serverless Framework doesn't do. This workaround in SAM is to setAddDefaultAuthorizerToCorsPreflight: false
.
- The unit testing chapter is currently incompatible with the SAM setup (needs more investigation).
The first 7 chapters (Introduction to Create a Cognito test user) are relevant irregardless of a choice of the Serverless Framework or SAM.
When SAM is to be used instead of the Serverless Framework, chapter 8 (Set up the Serverless Framework) should be replaced with the steps below.
Create the node
project.
mkdir notes-app-api
cd notes-app-api
npm init -y
The steps in the existing chapter 8 related to Install Node.js packages are the same. They are copied below for convenience.
npm install
npm install aws-sdk --save-dev
npm install uuid@7.0.3 --save
Commit: f71a1d7
A package.json
A package-lock.json
When SAM is to be used instead of the Serverless Framework, chapter 9 (Add Support for ES6/ES7 JavaScript) should be replaced with the steps below.
The following webpack related steps were required as per https://github.com/graphboss/aws-sam-webpack-plugin (more info here also)
npm install webpack webpack-cli aws-sam-webpack-plugin @types/aws-lambda --save-dev
npm install source-map-support --save
Create webpack.config.js
const path = require("path");
const AwsSamPlugin = require("aws-sam-webpack-plugin");
const awsSamPlugin = new AwsSamPlugin();
module.exports = {
// Loads the entry object from the AWS::Serverless::Function resources in your
// SAM config. Setting this to a function will
entry: () => awsSamPlugin.entry(),
// Write the output to the .aws-sam/build folder
output: {
filename: (chunkData) => awsSamPlugin.filename(chunkData),
libraryTarget: "commonjs2",
path: path.resolve(".")
},
// Create source maps
devtool: "source-map",
// Resolve .js extensions
resolve: {
extensions: [".js"]
},
// Target node
target: "node",
// AWS recommends always including the aws-sdk in your Lambda package but excluding can significantly reduce
// the size of your deployment package. If you want to always include it then comment out this line. It has
// been included conditionally because the node10.x docker image used by SAM local doesn't include it.
externals: process.env.NODE_ENV === "development" ? [] : ["aws-sdk"],
// Set the webpack mode
mode: process.env.NODE_ENV || "production",
// Add the AWS SAM Webpack plugin
plugins: [awsSamPlugin]
};
Commit: b22062b
A webpack.config.js
M package.json
M package-lock.json
The next chapter (Initialize the backend repo) is relevant irregardless of the use of SAM or Serverless Framework.
The steps related to the creation of javascript files (i.e. create.js
, libs/dynamodb-lib.js
and libs/handler-lib.js
) and the mock (i.e. mocks/create-event.json
) in the original chapter are unchanged.
The key difference for SAM are:
- we create a
template.yaml
(instead of theserverless.yml
for the Serverless Framework) - we need to build before we can test (instead of the Serverless Framework doing this automatically for us)
- the test invocation command is slightly different for SAM
The template.yaml
is as follows:
AWSTemplateFormatVersion: '2010-09-09'
Transform: AWS::Serverless-2016-10-31
Description: This is an AWS SAM fork of the popular Serverless Notes API
Parameters:
NotesTableName:
Type: String
Default: "notes"
Description: Notes Table Name
Globals:
Api:
Cors:
AllowMethods: "'HEAD,OPTIONS,DELETE,GET,PUT,POST'"
AllowHeaders: "'Content-Type,X-Amz-Date,Authorization,X-Api-Key,X-Amz-Security-Token'"
AllowOrigin: "'*'"
Auth:
DefaultAuthorizer: AWS_IAM
AddDefaultAuthorizerToCorsPreflight: false
Function:
CodeUri: .
Runtime: nodejs12.x
Timeout: 10
Environment:
Variables:
tableName: !Ref NotesTableName
Resources:
Create:
Type: AWS::Serverless::Function
Properties:
Handler: create.main
Policies:
- Version: '2012-10-17'
Statement:
- Effect: Allow
Action:
- 'dynamodb:PutItem'
Resource: 'arn:aws:dynamodb:us-east-1:*:*'
Events:
HTTP:
Type: Api
Properties:
Path: /notes
Method: post
Outputs:
NotesApi:
Description: "API Gateway endpoint URL for Prod stage for Notes function"
Value: !Sub "https://${ServerlessRestApi}.execute-api.${AWS::Region}.amazonaws.com/Prod/notes/"
Unlike the Serverless Framework, we have to build our code first. Let's update our package.json
so that it has the ability to do our build
"scripts": {
"build": "webpack-cli",
"clean": "rimraf .aws-sam",
"prebuild": "rimraf .aws-sam",
"prewatch": "rimraf .aws-sam",
"watch": "webpack-cli -w"
},
Now we can build our code with npm run-script build
This will result in the javascript being built under .aws-sam/build
Now we can invoke the function with sam local invoke --parameter-overrides ParameterKey=NotesTableName,ParameterValue=notes --profile myProfile Create -e mocks/create-event.json
Commit: b22062b
A template.yaml
A create.js
A libs/dynamodb-lib.js
A libs/handler-lib.js
A mocks/create-event.json
M package.json
Requires Get
function in template.yml
Requires List
function in template.yml
Requires Update
function in template.yml
Requires Delete
function in template.yml
The Working with 3rd party APIs and Setup a Stripe account chapters don't need to change.
Requires Billing
function in template.yml
In addition to SAM's support for passing arguments at deploy-time, it can also take a JSON file with the specific environment variables when calling the invoke
command.
Now we can invoke the function with sam local invoke --parameter-overrides ParameterKey=NotesTableName,ParameterValue=notes ParameterKey=StripeSecretKey,ParameterValue=<SecretKey> --profile myProfile Billing -e mocks/billing-event.json
More investigation is needed for the chapters Unit Tests in Serverless and Handle API Gateway CORS Errors.
sam deploy --guided
The full working SAM project is available at https://github.com/m0un10/sam-stack-demo-api/