Building a Serverless Web Application With AWS - Act 2
Tags: amazondynamodb, awslambda, restfulapi, awsapigateway
Published: 2022-11-22 13:20:31

So far, in the previous post, we configured and deployed our WebApp on AWS Amplify and used used user pools form AWS Cognito to create a fully-managed user management system that allows us to authenticate our users and manage their user information. 

We updated our website to use the user pool, and used it to provide a signin form on the site. We used Git and GitHub and the functionalities of AWS Amplify for continuous integration and continuous deployment (CI/CD). 

## BUILD A SERVERLESS BACKEND

Here we will use AWS Lambda and Amazon DynamoDB to build a backend process for handling requests for our web application. 

To deliver on our deployed web app's promise which is to allow users request a unicorn be sent to a location of their choice the JavaScript running in the browser will need to invoke a service running in the cloud to fulfill those requests.

## Implementation

A Lambda function will be invoked each time a user requests a unicorn. The function selects a unicorn from the fleet, records the request in a DynamoDB table, and responds to the front-end application with details about the dispatched unicorn.

The function is invoked from the browser using Amazon API Gateway. We will implement that connection soon. Let's test our function in isolation first.

⚠️ Before going along with this walk through, ensure you have completed all the steps and processes in the [ACT 1](https://dev.to/securityminded/building-a-serverless-web-application-with-aws-act-1-d6) of this series. If you have terminated you application, redeploy it before you start to follow along.

## Create an Amazon DynamoDB Table

We will create a DynamoDB table and give it a partition. 

- a) From AWS Management Console, choose Services at the top left hand side of your screen, click on Database and select DynamoDB from the services on the right.

![Amazon List of services](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/xbg6t0ropz2cysla29tx.png)

- b) Choose Create table.
- c) Enter Rides for the Table name. This field is case sensitive.
- d) Enter RideId for the Partition key and select String for the key type. This field is case sensitive.
- e) Check the Use default settings box and choose Create Table (at the bottom of the page).


![Amazon DynamoDB](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/wgjnpzem57zlvmvnoooz.png)

![Amazon DynamoDB](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/bd3vwjk3o77bhktygk1v.png)

- f) Click on the name of the table, in the Overview tab, click on 'Additional info' under general information then scroll to the bottom that section of your new table and note the ARN. We will use this in the next configuration.

## Create an IAM Role for Your Lambda Function

Every Lambda function has an IAM role associated with it. This role defines what other AWS services the function is allowed to interact with. We will create an IAM role that grants our Lambda function permission to write logs to Amazon CloudWatch Logs and access to write items to your DynamoDB table.


- a) From the AWS Management Console, click on Services and then select IAM in the Security, Identity & Compliance section.

- b) Select Roles in the left navigation pane and then choose Create Role.

- c) Select Lambda for the role type from the AWS service group, then click Next: Permissions.


![Amazon DynamoDB](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/t7b00dimmgn0fu3h7rbq.png)

Note: Selecting a role type automatically creates a trust policy for your role that allows AWS services to assume this role on your behalf. If you were creating this role using the CLI, AWS CloudFormation or another mechanism, you would specify a trust policy directly.

- d) Begin typing AWSLambdaBasicExecutionRole in the Filter text box and check the box next to that role.

- e) Choose Next Step.

![Amazon IAM Role](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/gwn04aoahv88ggja1onx.png)

- f) Enter WildRydesLambda for the Role Name. Keep other parameters as default.

- g) Choose Create Role at the bottom of the page.

- h) Type WildRydesLambda into the filter box on the Roles page and choose the role you just created.


![IAM Role](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/0qedkvtvat4mujqhcdpr.PNG)

- i) On the Permissions tab, on the left under Add permissions, choose Create Inline Policy.


![Amazon IAM Role](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/tlxpy3b3bf70961uyp34.png)

- j) Select Choose a service.

- k) Begin typing DynamoDB into the search box labeled Find a service and select DynamoDB when it appears..

- l) Choose Select actions.

- m) Begin typing PutItem into the search box labeled Filter actions and check the box next to PutItem when it appears.

- n) Select the Resources section.

- o) With the Specific option selected, choose the Add ARN link in the table section.

- p) Paste the ARN of the table you created in the previous section in the Specify ARN for table field, and choose Add.

- q) Choose Review Policy.


![AWS Lambda](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/b4rmchlselukgwurt1iv.PNG)
- r) Enter DynamoDBWriteAccess for the policy name and choose Create policy.


![AWS IAM Policy](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/vj4zwspmnykfe73ytcas.PNG)

## Create a Lambda Function for Handling Requests

AWS Lambda will run our code in response to events such as an HTTP request. In this step we'll build the core function that will process API requests from the web application to dispatch a unicorn.

- a) Choose Services then select Lambda in the Compute section.

- b) Click Create function.

- c) Keep the default Author from scratch card selected.

- d) Enter RequestUnicorn in the Name field.

- e) Select Node.js 16.x for the Runtime.

- f) Ensure _'Use an existing'_ role is selected from the Role dropdown.

- g) Select WildRydesLambda from the Existing Role dropdown.

- h) Click on Create function.


![AWS Lambda](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/lsd3jac1qn8hkpsbhr0p.PNG)

- i) Scroll down to the Function code section and replace the existing code in the index.js code editor with the contents of [requestUnicorn.js](https://webapp.serverlessworkshops.io/serverlessbackend/lambda/requestUnicorn.js).

- j) Choose Deploy.


![AWS Lambda Function](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ceityi4d2u9gqhrpfzkv.PNG)

## Implementation Validation

Let's test the function that we just built using the AWS Lambda console. 

- a) From the main edit screen for your function, select Test and choose Configure test event from the dropdown.
- b) Keep Create new event selected.
- c) Enter TestRequestEvent in the Event name field
- d) Copy and paste the following test event into the editor:

```JSON
{
    "path": "/ride",
    "httpMethod": "POST",
    "headers": {
        "Accept": "*/*",
        "Authorization": "eyJraWQiOiJLTzRVMWZs",
        "content-type": "application/json; charset=UTF-8"
    },
    "queryStringParameters": null,
    "pathParameters": null,
    "requestContext": {
        "authorizer": {
            "claims": {
                "cognito:username": "the_username"
            }
        }
    },
    "body": "{\"PickupLocation\":{\"Latitude\":47.6174755835663,\"Longitude\":-122.28837066650185}}"
}
```
- e) Choose save.


![Code to Test AWS Lambda Function](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/5to7tc4roewyagkpgs6b.PNG)

- f) On the main function edit screen click Test with TestRequestEvent selected in the dropdown.
- g) Scroll to the top of the page and expand the Details section of the Execution result section.
- h) Verify that the execution succeeded and that the function result looks like the following:


![Result to Test on AWS Lambda Function](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/776zr1y7jbt3kdf0w894.PNG)

## Deploy a RESTful API

It's all beginning to come together now, In these next couple of steps, we will use API Gateway to expose the Lambda function we just built as a RESTful API. 

This API will be accessible on the public Internet. It will be secured using the Amazon Cognito user pool we created in the under User Management. Using this configuration we will then turn our statically hosted website into a dynamic web application by adding client-side JavaScript that makes AJAX calls to the exposed APIs.

### Create a New REST API

We will use the Amazon API Gateway console to create a new API named WildRydes.

- a) In the AWS Management Console, click Services then select API Gateway under Networking and Content Delivery.
- b) Select Build under REST API.
- c) Choose **'New API'**  under Create API and enter WildRydes for the API Name.
- d) Choose Edge optimized in the Endpoint Type dropdown. Note: Edge optimized are best for public services being accessed from the Internet. Regional endpoints are typically used for APIs that are accessed primarily from within the same AWS Region.
- e) Choose Create API


![Setting up RESTful API on Amazon API Gateway](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/u40m7bc3jimcw8jv73ki.PNG)

### Create a Cognito User Pools Authorizer

Amazon API Gateway can use the JWT tokens returned by Cognito User Pools to authenticate API calls. And so in this step we'll configure an authorizer for our API to use the user pool we created earlier.

- a) Under your newly created API, choose Authorizers.

- b) Chose Create New Authorizer.

- c) Enter WildRydes for the Authorizer name.

- d) Select Cognito for the type.

- e) Enter WildRydes (or the name you gave your user pool) in the Cognito User Pool input.

- f) Enter Authorization for the Token Source.

- g) Choose Create.


![Creating a User Pool Authorizer on the Rest API](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/6h521vu4jkry178o3e9j.png)

**Let's verify our authorizer configuration**

- h) Open a new browser tab and visit /ride.html under your website's domain.

- i) If you are redirected to the sign-in page, sign in with the user you created in the last module. You will be redirected back to /ride.html.

- j) Copy the auth token from the notification on the /ride.html,

- k) Go back to previous tab where you have just finished creating the Authorizer

- l) Click Test at the bottom of the card for the authorizer.

- m) Paste the auth token into the Authorization Token field in the popup dialog.

- n) Click Test button and verify that the response code is 200 and that you see the claims for your user displayed.


![Verifying that the User Pool Authorizer works](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/8hw6jz9s38v9ll6popi7.PNG)

### Create a new resource and method

We will create a new resource called /ride within our API. Then create a POST method for the resource and configure it to use a Lambda proxy integration backed by the RequestUnicorn function we created previously.

- a) Still in the API we just created, by the left nav, click on Resources.

- b) From the Actions dropdown select Create Resource.


![Configuring the Rest API POST Method on Amazon API Gateway ](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/t8nccyzktvm7eseinzro.png)

- c) Enter ride as the Resource Name.

- d) Ensure the Resource Path is set to ride.

- e) Select Enable API Gateway CORS for the resource.

- f) Click Create Resource.


![New Child resource on Amazon API Gateway](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/obdhzzwps6gzawgcltef.png)

- g) With the newly created /ride resource selected, from the Action dropdown select Create Method.

- h) Select POST from the new dropdown that appears, then click the checkmark.


![Configuring the Rest API POST Method on Amazon API Gateway](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/d5tb0n3hxwox0w9rpyq0.PNG)

- i) Select Lambda Function for the integration type.

- j) Check the box for **'Use Lambda Proxy integration'**.

- k) Select the Region you are using for Lambda Region.

- l) Enter the name of the function you created in the previous module, RequestUnicorn, for Lambda Function.

- m) Choose Save.


![AWS Lambda Function](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/uxpt24jel229esxpmju3.PNG)

⚠️ Please note, if you get an error that you function does not exist, check that the region you selected matches the one you used in the previous module.

- n) When prompted to give Amazon API Gateway permission to invoke your function, choose OK.

- o) Select the Method Request card.

- p) Click the pencil icon next to Authorization.

- q) Select the WildRydes Cognito user pool authorizer from the drop-down list, and click the checkmark icon.


![Configuring the Rest API POST Method on Amazon API Gateway](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/od8dw9ot1cvujvzcn6ug.PNG)

### Deploy Your API

- a) From the Amazon API Gateway console, choose the Actions drop-down list select Deploy API.
- b) Select [New Stage] in the Deployment stage drop-down list.
- c) Enter prod for the Stage Name.
- d) Choose Deploy.
- e) Note the Invoke URL. We will use it in the next configuration.

### Update the Website Config

Update the /js/config.js file in your website deployment to include the invoke URL of the stage we just created. You should copy the invoke URL directly from the top of the stage editor page on the Amazon API Gateway console and paste it into the _config.api.invokeUrl key of your sites /js/config.js file. 

Make sure when you update the config file it still contains the updates you made in the previous module for your Cognito user pool.

- a) Open the config.js file in a text editor.
- b) Update the invokeUrl setting under the api key in the config.js file. Set the value to the Invoke URL for the deployment stage your created in the previous section.

An example of a complete config.js file is included below. 
⚠️ Note, the actual values in your file will be different.


![Project Source code edited](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/1rajej3rnqcnpx70egwn.PNG)

- c) Save the modified file and push it to your Git repository to have it automatically deploy to Amplify Console.

```hcl
git add .
git commit -m 'RESTful API configuration update'
git push
```

### Validate your Implementation

- a) Visit /ride.html under your website domain.
- b) If you are redirected to the ArcGIS sign-in page, sign in with the user credentials you created previously in the Introduction section as a prerequisite of this tutorial.
- c) After the map has loaded, click anywhere on the map to set a pickup location.
- d) Choose Request Unicorn. You should see a notification in the right sidebar that a unicorn is on its way and then see a unicorn icon fly to your pickup location.

⭐&#9734;&#9733; Congratulations, you have completed the Wild Rydes Web Application Workshop! Now we Clean up.

The Final Act in this series will show you how to terminate all the resources used for this project.