AWS Developer Tools Blog

Creating and Deploying a Serverless Web Application with CloudFormation and Ember.js

Serverless computing enables you to build scalable and cost-effective applications that scale up or down automatically without provisioning, scaling, and managing servers. You can use AWS Lambda to execute your back-end application code, Amazon API Gateway for a fully managed service to create, publish, maintain, monitor, and secure your REST API, and Amazon S3 to host and serve your static web files.

Ember.js is a popular, long-standing front-end web application framework for developing rich HTML5 web applications. Ember.js has an intuitive command line interface. You can write dramatically less code by using its integrated Handlebars templates. Ember.js also contains an abstracted data access pattern that enables you to write application adapters that communicate with REST APIs.

In this tutorial, we build a simple Ember.js application that initializes the AWS SDK for JavaScript using native Ember.js initializers. The application communicates with API Gateway, which runs backend Lambda code that reads and writes to Amazon DynamoDB. In addition, we use the Ember.js command line interface to package and deploy the web application to Amazon S3 for hosting and for delivery through Amazon CloudFront. We also use AWS CloudFormation with the AWS Serverless Application Model (AWS SAM) to automate the creation and management of our serverless cloud architecture.

Configuring your local development environment

Requirements:

Clone or fork the aws-serverless-ember repository and install the dependencies. Based on your setup, you might need to use the sudo argument when installing these. If you get an EACCESS error, run these commands with sudo npm install:

npm install -g ember-cli
npm install -g bower
git clone https://github.com/awslabs/aws-serverless-ember

You should now have two folders in the project’s root directory, client and cloud. The cloud folder contains our CloudFormation templates and Lambda function code. The client folder contains the client-side Ember.js web application that deploys to S3. Next, install the dependencies:

cd aws-serverless-ember
cd client
npm install && bower install

When this is complete, navigate to the cloud directory in your project’s root directory:

cd ../cloud

Create and deploy the AWS Cloud environment using CloudFormation

The infrastructure for this application uses API Gateway, Lambda, S3, and DynamoDB.

Note: You will incur charges after running these templates. See the appropriate pricing page for details.

The cloud folder of the repository contains the following files:

  • api.yaml – An AWS SAM CloudFormation template for your serverless backend
  • deploy.sh – A bash helper script for deploying with CloudFormation
  • hosting.yaml – A CloudFormation template for your S3 bucket and website
  • swagger.yaml – The API definition for API Gateway
  • index.js – The Lambda function code
  • swagger.yaml – The OpenAPI definition for your serverless REST API

First, create a website bucket with static website hosting. You also create a bucket for deploying your serverless code.

Note: For this section be sure you’ve installed and configured the AWS CLI with appropriate permissions (your CLI should at least have access to read/write to CloudFormation and read/write to S3) for this tutorial.

Run the following command to create the S3 hosting portion of our back-end, wait for completion, and finally describe stacks once complete:

aws cloudformation create-stack --stack-name ember-serverless-hosting --template-body file://$PWD/hosting.yaml && \
aws cloudformation wait stack-create-complete --stack-name ember-serverless-hosting && \ 
aws cloudformation describe-stacks --stack-name ember-serverless-hosting

This creates a CloudFormation stack named ember-serverless-hosting, waits for the stack to complete, and then display the output results. While you wait, you can monitor the events or view the resource creation in the CloudFormation console, or you run the following command in another terminal:

aws cloudformation describe-stack-events –-stack-name ember-serverless-hosting

When the stack is created, the console returns JSON that includes the stack outputs specified in your template. If the JSON does not output for some reason, re-run the last portion of the command: aws cloudformation describe-stacks --stack-name ember-serverless-hosting. This output includes the bucket name you’ll use to deploy your serverless code. It also includes the bucket URL you’ll use to deploy your ember application. For example:

{
    "Stacks": [
        {
            "StackId": "arn:aws:cloudformation:<aws-region>:<account-id>:stack/ember-serverless-hosting/<unique-id>",
            "Description": "Ember Serverless Hosting",
            "Tags": [],
            "Outputs": [
                {
                    "Description": "The bucket used to deploy Ember serverless code",
                    "OutputKey": "CodeBucketName",
                    "OutputValue": "ember-serverless-codebucket-<unique-id>"
                },
                {
                    "Description": "The bucket name of our website bucket",
                    "OutputKey": "WebsiteBucketName",
                    "OutputValue": "ember-serverless-websitebucket-<unique-id>"
                },
                {
                    "Description": "Name of S3 bucket to hold website content",
                    "OutputKey": "S3BucketSecureURL",
                    "OutputValue": "https://ember-serverless-websitebucket-<unique-id>.s3.amazonaws.com"
                },
                {
                    "Description": "URL for website hosted on S3",
                    "OutputKey": "WebsiteURL",
                    "OutputValue": "http://ember-serverless-websitebucket-<unique-id>.s3-website-<aws-region>.amazonaws.com"
                }
            ],
            "CreationTime": "2017-02-22T14:40:46.797Z",
            "StackName": "ember-serverless",
            "NotificationARNs": [],
            "StackStatus": "CREATE_COMPLETE",
            "DisableRollback": false
        }
    ]
}

Next, you create your REST API with CloudFormation and AWS SAM. For this, you need the CodeBucketName output parameter from the previous stack JSON output when running the following command to package the template:

aws cloudformation package --template-file api.yaml --output-template-file api-deploy.yaml --s3-bucket <<CodeBucketName>>

This command creates a packaged template file named api-deploy.yaml. This file contains the S3 URI to your Lambda code, which was uploaded by the previous command. To deploy your serverless REST API, run the following:

aws cloudformation deploy --template-file api-deploy.yaml --stack-name ember-serverless-api --capabilities CAPABILITY_IAM

Next, run the following command to retrieve the outputs:

aws cloudformation describe-stacks --stack-name ember-serverless-api

Note the API Gateway ID OutputValue:

...
"Outputs": [
                {
                    "Description": "URL of your API endpoint", 
                    "OutputKey": "ApiUrl", 
                    "OutputValue": "https://xxxxxxxx.execute-api.us-east-1.amazonaws.com/Prod"
                }, 
                {
                    "Description": "API Gateway ID", 
                    "OutputKey": "Api", 
                    "OutputValue": "xxxxxxxx" //<- This Value is needed in the below command
                }
            ],
...

Now, run the following command to download the JavaScript SDK for your API using the ID noted above:

aws apigateway get-sdk --rest-api-id <<api-id>> --stage-name Prod --sdk-type javascript ./apiGateway-js-sdk.zip

This downloads a .zip file that contains the JavaScript interface generated by API Gateway. You use this interface to interact with API Gateway from your Ember.js application. Extract the contents of the .zip file, which produces a folder named apiGateway-js-sdk , directly into your client/vendor/ folder. You should now have the following client/vendor folder structure:

  • client
    • vendor
      • apiGateway-js-sdk
      • amazon-cognito

The additional files are libraries that the SDK generated by API Gateway uses to properly sign your API Gateway request. For details about how the libraries are used, see the Amazon API Gateway Developer Guide.

To use the SDK in your Ember.js application, you need to ensure Ember loads them properly. We do this by using app.import() statements within the ember-cli-build.js file.

Initialize the AWS SDK for JavaScript with Ember

You’ll set up and initialize the AWS SDK for JavaScript within your application by using ember initializers. Ember initializers allow you to initialize code before your application loads. By using them, you can ensure the AWS SDK and your API SDK are properly initialized before your application is presented to the user.

Open your client/config/environment.js file and add your AWS configuration to the development section. Add the region in which you are running, the Amazon Cognito identity pool ID created in the previous section, the Amazon Cognito user pool ID created in the previous section, and the user pool app client ID created in the previous section.

You can retrieve these values by running:

aws cloudformation describe-stacks --stack-name ember-serverless-api

Use the following values returned in the Output within the client/config/environment.js file:

  • ENV.AWS_POOL_ID -> CognitoIdentityPoolId
  • ENV.AWS_USER_POOL_ID -> CognitoUserPoolsId
  • ENV.AWS_CLIENT_ID -> CognitoUserPoolsClientId
// client/config/environment.js L26
if (environment === 'development') {
    ENV.AWS_REGION = ‘aws-region-1’
    ENV.AWS_POOL_ID = ‘aws-region:unique-hash-id’
    ENV.AWS_USER_POOL_ID = ‘aws-region_unique-id’
    ENV.AWS_CLIENT_ID = ‘unique-user-pool-app-id’
}

Now, run your client and ensure the SDK loads properly. From the client directory, run:

ember s

Then visit http://localhost:4200/ in your web browser, and open the developer tools. (If you’re using Chrome on a macOS machine, press cmd+option+i. Otherwise, press Ctrl+Shift+i.), you should see the following messages:

AWS SDK Initialized, Registering API Gateway Client: 
API Gateway client initialized: 
Object {docsGet: function, docsPost: function, docsDelete: function, docsOptions: function}

This confirms your generated SDK and the AWS SDK are properly loaded and ready for use.

Now try the following:

1. Log in with any user/credentials, and observe the error. (onFailure: Error: User does not exist.)
2. Register with a weak password, and observe the error. (Password did not conform with policy. Password not long enough.)
3. Register a new user, and use an email address you have access to so you receive a confirmation code.
4. Try re-sending the confirmation code.
5. Enter the confirmation code.
6. Log in with your newly created user.

The JWT access token is retrieved from Amazon Cognito user pools, it is decoded and displayed for reference after logging into the client application. The section entitled “Dynamo Items” within the application creates and deletes items within your DynamoDB table (that was previously created with CloudFormation) via API Gateway and Lambda, and authenticated with Cognito User Pools.

Deploying the web application to Amazon S3

In this command, you use the S3 WebsiteBucketName output from the ember-serverless-hosting CloudFormation stack as the sync target.

Note: you can retrieve the output values again by running:
aws cloudformation describe-stacks --stack-name ember-serverless-hosting

cd client
ember build
aws s3 sync dist/ s3://WebsiteBucketName/ --acl public-read

Your website should now be available at the WebsiteURL output value from the ember-serverless-hostingCloudFormation stack’s outputs.

Questions or comments? Reach out to us on the AWS Development Forum.

Source code and issues on GitHub: https://github.com/awslabs/aws-serverless-ember.