The AWS blog gave me a very pleasant surprise the other day: AWS SAM Local is in public beta. It's a golang CLI project that's distributed via NPM.
SAM Local leverages Docker to run you code in local containers, and leverages the great work done by Michael Heart on LambCI for the container.
In this post I'll highlight the minimum number of steps to get up and running with SAM Local.
SAM
SAM Local builds upon AWS SAM: The Serverless Application Model. It was introduced in late 2016 and presents a simplified model for creating and deploying serverless applications. Oh, and the mascot is a magic construction squirrel:
It's a transformation layer on top of AWS CloudFormation that gives you a declarative way to define your serverless resources.
Using SAM instead of raw CloudFormation allows for a less verbose declaration of resources such as functions (Lambda), event sources (e.g. S3, API Gateway, etc), and database (DynamoDB).
A potential point of confusion for new users of SAM is that it is an open source standard, not an open source tool. While you can suggest and submit improvements to the standard (via the GitHub repo), the actual implementation is not open (because it depends on CloudFormation, which is an AWS service).
It's also important to note that SAM will not support non-serverless services. If you want to create other resources, just use CloudFormation in your SAM template! It all gets translated to CloudFormation in the end...
Installation
In order to get started you'll need to have Docker running in the background. On Mac OSX you should install that via Homebrew:
brew cask install docker
Once installed and run, you'll see the Docker icon in your tray, and it should be running:
Now that you've got the required dependencies, install the SAM Local tool itself:
npm install --global aws-sam-local
Once that's installed, you can access the command sam
in your terminal.
Sample Project
To get started quickly we'll leverage the api-event-source
sample projects in the SAM Local repo.
Clone the repo and go to the example directory:
git clone https://github.com/awslabs/aws-sam-local.git
cd aws-sam-local/samples/api-event-source
This project creates a single function with an API (i.e. HTTP) event source, so it simulates a Lambda function and an API Gateway endpoint.
At the top of the template.yaml
file you can see the key difference with normal CloudFormation templates:
AWSTemplateFormatVersion : '2010-09-09'
Transform: AWS::Serverless-2016-10-31
The Transform
property tells CloudFormation how to handle the template, and allows you to use the SAM resource types.
All the function handler code (in index.js
) does is return a 200 response with the string payload "OK".
Run
sam local start-api
If it's the first time you've run this command, the Docker container will be downloaded before running.
In the output you'll see the required container being run, and then listening for events as a HTTP endpoint that you can open in your local browser.
On my development laptop it took a while for requests to respond, even though the actual compute time of the function (which is reported in the output) was very low:
2017/08/16 22:22:14 Invoking index.handler (nodejs6.10)
START RequestId: 0d3c5d51-c16e-19a9-2816-78fdeffb2f51 Version: $LATEST
END RequestId: 0d3c5d51-c16e-19a9-2816-78fdeffb2f51
REPORT RequestId: 0d3c5d51-c16e-19a9-2816-78fdeffb2f51 Duration: 8.15 ms Billed Duration: 100 ms Memory Size: 128 MB Max Memory Used: 29 MB
This is because of the overhead of spinning up the container (before it even touches your code) for each request. I found that giving the function more memory (the default is the lowest i.e. 124MB) sped up effective response time considerably. Hopefully that (i.e. cold starts) is something that can be improved in the future without having to throw memory at it.
If you only edit your function handler code (i.e. in index.js
) you don't need to restart your service.
Changes to the template (i.e. template.yaml
) do need a restart to take effect.
Sample Event
Before I would usually have to set up a logging function (i.e. one that just logs the event it receives to the console) with my chosen event source, and fire a few test events to it so that I had something to work with.
Now we can just run a command locally to get a sample event to work with. In this case we want an API test event, so we pass that as an argument:
sam local generate-event api
This will output the event (JSON) to your terminal. While interesting, it's not usable.
To save it so we can use it later, redirect the output to a file:
sam local generate-event api > event.json
Invoking
Now that we have a test event, we can do a one-off invocation of our function with the test event we've saved:
sam local invoke "ExampleFunction" -e event.json
This will give the function's output as a one-off.
START RequestId: 42949c86-df9a-1f85-6ed3-c399223e292f Version: $LATEST
{"statusCode":200,"body":"OK"}
END RequestId: 42949c86-df9a-1f85-6ed3-c399223e292f
REPORT RequestId: 42949c86-df9a-1f85-6ed3-c399223e292f Duration: 11.16 ms Billed Duration: 100 ms Memory Size: 128 MB Max Memory Used: 28 MB
Any errors will be output to stderr
.
Validate
Another one of the great time-saving features I was happy to see in SAM Local was the validate
command.
If your template is using SAM (i.e. not just CloudFormation) and named template.yaml
you can validate it with:
sam validate
This will give you feedback on your template's properties and formatting (e.g. YAML whitespace) without going through the package/deploy/test cycle, which is a big improvement to the development flow.
Help
All the commands take the -h
/--help
argument so that you can find out all their valid arguments and descriptions.
I'm really happy that the tooling around serverless applications is improving at such a rapid clip!