Full Stack Serverless with AWS SAM
Developing applications with serverless technology facilitates high scalability and allows for significant cost reduction in many use cases. In this article, we will look into setting up a full stack serverless project with AWS Serverless Application Model (AWS SAM).
AWS SAM is a framework for building serverless applications on AWS and it consists of a template specification and a CLI for deploying the application. The template specification is based on CloudFormation templates and provides useful extensions for serverless stacks.
Going through the steps in this article, we will lay the groundwork for a full stack serverless project including CI/CD.
Backend
As a prerequisite, you will need to install and configure AWS SAM. Then create the backend template in the root of your project directory with
sam init --name backend --runtime nodejs14.x \
--dependency-manager npm --app-template hello-world
As the following steps are independent of the runtime you choose for your lambda, you can replace nodejs14.x
and npm
with any other option; e.g. you can type python3.9
and pip
instead.
That’s all we need to do to set up the backend REST API. The initialization creates a CloudFormation templatebackend/template.yaml
, which deploys an implicitly defined API Gateway routing to the Lambda handler defined in backend/hello-world/app.js
.
Additional AWS Resources
For serving the frontend serverless on AWS, we need to add an S3 bucket in the resource section of backend/template.yaml
Instead of enabling direct website hosting with S3, we will add a CloudFront distribution to be able to support HTTPS, which is a requirement for almost any productive web application.
As you can see, besides the first origin FrontendBucket
with the corresponding DefaultCacheBehavior
for serving the static website from S3, we also defined a second origin Backend
with a corresponding CacheBehavior
. The latter proxies our backend REST API at the path /api
of the same domain as the static website. This way our backend will not need a CORS configuration and our frontend will not need to know the URL of the API Gateway serving the backend.
To adjust the backend for this solution, we need to change Path: /hello
to Path: /api/hello
in the predefined HelloWorldFunction
resource.
Furthermore, we need to give CloudFront read access to the S3 bucket.
Finally, we provide the domain of the CloudFront distribution in the Outputs
section of the template by adding
Frontend
The described solution works with any frontend framework that builds a static website. We will showcase the creation of a React frontend here.
With Node.js installed create the React frontend in the root of your project with
npx create-react-app frontend
To demonstrate the connection to the backend, change the content of frontend/src/App.js
to
This will fetch and display a greeting from the generated Lambda.
Manual Deployment
The file structure of the project now looks like this:
├── backend
│ ├── events
│ ├── hello-world
│ │ ├── app.js
│ │ └── ...
│ ├── README.md
│ └── template.yaml
└── frontend
├── public
├── src
│ ├── App.js
│ └── ...
├── package.json
└── README.md
And that’s it. You could deploy the application manually by first running sam build && sam deploy --guided
in the folder backend
, then npm run build
in the folder frontend
, and finally copying the content of frontend/build
into the S3 bucket. Opening the CloudFront URL in the browser will display the greeting. But here we want to go a step further and set up automated deployment with a CI/CD tool.
CI/CD
For adding CI/CD we will first create the AWS resources required for an automated deployment by running the following command in the backend
folder. Replace <R>
with an AWS region like us-east-1
.
sam pipeline bootstrap --no-interactive --stage dev --region <R>
This will create an IAM user and role for the pipeline, an IAM role for CloudFormation, and an S3 bucket for uploading the artifacts deployed by CloudFormation. You will find the access key id and the secret access key of the created pipeline user in the output of the command.
Next, we need to create a CI/CD pipeline that provides the SAM CLI and the AWS CLI configured with the pipeline user created above. We will demonstrate a solution based on GitHub Actions as an example here. If you want inspiration on how to bootstrap such a pipeline for other CI/CD tools like Jenkins or GitLab, you can generate examples by running sam pipeline init
in the backend
folder.
To setup GitHub Actions CI/CD commit the project to a newly created GitHub repository and create repository secrets named AWS_SECRET_KEY_ID
and AWS_SECRET_ACCESS_KEY
with the credentials of the pipeline user.
Now create the actual pipeline file github/workflows/pipeline.yaml
Replace <PIPELINE_EXECUTION_ROLE>
, <CLOUDFORMATION_EXECUTION_ROLE>
and <ARTIFACTS_BUCKET>
with the values you find in backend/.aws-sam/pipeline/pipelineconfig.toml
and replace <REGION>
with the same region you selected above.
Append the following lines to the pipeline file to check out the repository and to configure the SAM CLI with the pipeline role.
Then we instruct the pipeline to build and deploy the backend.
For deploying the frontend, we first store the generated name of the S3 bucket in an environment variable.
Then we copy the result of building the application into the cleared S3 bucket.
For allowing the pipeline to clear and put objects in S3, we need to adjust the bucket policy in backend/template.yaml
by adding a statement to the FrontendBucketPolicy
section.
Also add a parameter above the Resources
section of backend/template.yaml
.
If you commit and push GitHub Actions will deploy our application. In the output of GitHub Actions you will find the URL of the CloudFront distribution. Open this URL in your browser to see the greeting.
Conclusion and Next Steps
In this article, we lay the groundwork for a full stack serverless project including CI/CD with AWS SAM.
The next step could be to set up a custom domain for your application, add a custom error response to your CloudFront distribution to support error handling in single-page applications, or set up forwarding of specific headers and cookies depending on the requirements of your backend.
This blog post is published by Comsysto Reply GmbH