Serverless, Development & Deployment - AWS Serverless Application Model (SAM)

23.08.2023Tom Trapp
Tech Serverless Amazon Web Services Function as a Service Pay As You Go

👉 You don’t know what serverless is? Then you can find everything you need to know about serverless here.

In the second part of our series on Serverless Development & Deployment, we will take a closer look at the AWS Serverless Application Model (SAM). In the first part we already looked at the AWS Cloud Development Kit (CDK). Both tools are very similar, but there are some differences that I want to point out in this article.

AWS Serverless Application Model (SAM)

AWS SAM, or Amazon Web Services Serverless Application Model, is an open source framework developed by AWS and first released in November 2016. AWS SAM helps developers to efficiently build, test and debug Serverless applications.

AWS SAM has the following features:

  • Serverless Support: AWS SAM is specifically designed for building Serverless applications. It supports AWS services such as AWS Lambda, Amazon API Gateway and Amazon DynamoDB.
  • Built-in Best Practices: With AWS SAM, developers can easily implement best practices for serverless applications. This includes configuring event sources and linking resources.
  • Local Development and Testing: AWS SAM provides a local development environment that allows developers to build and test applications on their local system before going to production.
  • Integration with development tools: AWS SAM integrates with popular IDEs and CI/CD tools to simplify and accelerate the development process.
  • Simplified Deployment: With AWS SAM deployment, developers can easily and reliably deploy serverless applications. Additionally, Infrastructure as Code (IaC) can be used to define and manage resources.
  • Versatility: Since SAM is based on CloudFormation templates, “non-SAM-resources” can be defined in the same template.
  • Extensibility: AWS SAM supports template extensibility, allowing developers to create reusable parts of applications or stack configurations.

Let’s look at this graphically, what exactly does SAM do:

sam_explained.png

Figure: Source: https://www.dev-insider.de/grundlagen-zu-aws-sam-a-843785/ (23.07.2023)

With SAM we get to know the first declarative serverless IaC framework, i.e. the definition of the resources is made in YAML and not, as with CDK for example, in Golang. This is why we also speak of a SAM template.

Let’s take a closer look at the project structure:

sam_explained_detail.png

Figure: Source: https://www.sqlshack.com/getting-started-with-the-aws-sam-cli/ (23.07.2023)

Here we see that behind SAM (as with CDK), there is a CloudFormation stack. This stack is created with the sam deploy command.

Hands On

Target: A Rest API is to read something from a DynamoDB via Lambda Function. Serverless Land Pattern: API Gateway to Lambda to DynamoDB IaC Tool: AWS Serverless Application Model (SAM) Language: Java Aws Services: API Gateway, Lambda, DynamoDB

apigw-lambda-dynamodb-cdk-go

Setup

First we have to install the aws-sam-cli, the easiest way to do this is via brew:

1
2
brew install aws/tap/aws-sam-cli
sam --version

And we are ready! No bootstrapping necessary, as SAM works directly with CloudFormation.

Development

As in the previous example, we clone the repository (the existing one can also be used) and change to the correct folder:

1
2
3
git clone https://github.com/aws-samples/serverless-patterns/ 
cd serverless-patterns/apigw-lambda-dynamodb-sam-java
code .

We notice the following:

  • In the folder src there is our complete Java source code.
  • This is a Maven project
  • In the file template.yaml all SAM instructions are defined.

Local Development

SAM offers us the possibility to test our functions locally. To do this, we first have to build our Java function with Maven:

1
mvn clean package

Then we can start a container locally which executes our lambda function:

1
echo ''{"userId": "231deb432f3dd", "description": "I have not been listening to decodify yet."}'' | sam local invoke --event -

Unfortunately, this doesn’t work like this, because we need to send a complete HTTP Proxy API gateway event to our local lambda function. Fortunately, SAM provides us with a solution for this as well:

1
sam local generate-event apigateway http-api-proxy > event.json

With this command we generate an event, which we can then send to our function. Now we can specify our json in the event.json property body, which should look like this:

1
"body": "{\"userId\": \"231deb432f3dd\",\"description\": \"I have not been listening to decodify yet.\"}",

Then we invoke our function with the event:

1
sam local invoke -e event.json

Now we see our function was invoked correctly, because we get an error that our DynamoDB cannot be contacted.

Of course, we could now either write a mock service in Java that abstracts the DynamoDB. Or we could use Docker and start a local DynamoDB and let our function communicate with it. But both would go beyond the scope here, as we want to focus on the serverless part.

Basically, however, we have seen how we could do local development with SAM, including effective local REST calls. Here, too, the Test-Driven Development approach would be a good alternative.

Deployment

Enough local development action, now let’s deploy our little Java serverless programme! For safety’s sake, let’s build our program again:

1
mvn clean package

Then we can run our sam deploy and specify the AWS S3 bucket where our Lambda function or its built source code is located.

1
sam deploy --guided

We are now asked for different input parameters.

sam_deploy.png

Fortunately, we can store these values in an samconfig.toml so we don’t have to type them in every time.

Once we have run sam deploy --guided once we can just run sam deploy in the future and use the configs on samconfig.toml.

Afterwards we will be shown what exactly is deployed:

sam_deploy_2.png

Now our Sam template has been successfully deployed. Since we have defined in our template.yaml that our API Gateway URL should be output, we can cache it again:

1
export APIGW_REST_ENDPOINT=https://<app-id>.execute-api.eu-central-1.amazonaws.com/dev/ticket

Other useful commands:

  • sam build: Compiles the code for all functions in the project and stores them in a bucket for upload to AWS Lambda.
  • sam package: Packs the application and dependencies into a CloudFormation-compatible package that can be used for later deployment.
  • sam local invoke: Tests a function locally by triggering it with an event.
  • sam logs: Displays the logs for a given function.
  • sam validate: Checks the template for valid syntax and semantics.

Testing

Now that we have deployed our SAM stack, we want to test it via curl:

1
2
curl -X POST $APIGW_REST_ENDPOINT -H "Content-Type: application/json" -d '{"userId": "John", "description": "I have not been listening to decodify yet."}'
curl -X POST $APIGW_REST_ENDPOINT -H "Content-Type: application/json" -d '{"userId": "Maria", "description": "What is decodify?"}'

Both calls go straight through with HTTP 200 Success, very cool! 🔥

After that we can look at the content of our DynamoDB again:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
aws dynamodb scan --table-name tickets

---
{
    "Items": [
        {
            "description": {
                "S": "What is decodify?"
            },
            "userId": {
                "S": "Maria"
            },
            "ticketId": {
                "S": "3fe863f8-78d6-4447-9f44-9c3c443010df"
            }
        },
        {
            "description": {
                "S": "I have not been listening to decodify yet."
            },
            "userId": {
                "S": "John"
            },
            "ticketId": {
                "S": "4425a3ad-463e-4666-bf6b-a272308979b9"
            }
        }
    ],
    "Count": 2,
    "ScannedCount": 2,
    "ConsumedCapacity": null
}

That’s it, we have successfully deployed and tested a Rest API with Lambda and DynamoDB via SAM.

Cleanup

1
2
sam delete --stack-name ticket-stack
aws s3 rb s3://b-nova-sam-example-bucket --force

TLDR

SAM is currently the most widely used IaC tool on Serverless Land, with over 250 patterns.

Advantages

  • Declarative approach
  • Simple syntax for defining serverless resources
  • Integrates seamlessly with AWS CloudFormation
  • Provides a local development environment for faster iteration.

Cons

  • Only supports AWS as a cloud platform
  • Local mocking required, not all services can be emulated locally.

SAM vs. CDK

Both IaC tools aim to achieve the same thing, deploy AWS resources easily and quickly. SAM specialises in serverless deployment, which makes it very easy to deploy a serverless application. The declarative approach also makes SAM a bit easier and more usable than CDK. Basically, however, the deployment is use-case dependent; if it is a serverless application, I would prefer SAM.

Now we have learned about the AWS Serverless Application Model (SAM) and how we can use it to deploy a REST API with Lambda and DynamoDB. In the next TechUp in the series, we’ll venture into the open source world, move away from vendor lock-in and take a closer look at the Serverless Framework. Stay tuned! 🔥

Tom Trapp

Tom Trapp – Problemlöser, Innovator, Sportler. Am liebsten feilt Tom den ganzen Tag an der moderner Software und legt viel Wert auf objektiv sauberen, leanen Code.