LevelUp! Lab for Serverless
Project Overview and High Level Design
Let's start with the High Level Design.
The diagram below provides a visual representation of the services used in this tutorial and how they are connected. This application uses AWS Amplify, Amazon API Gateway, AWS Lambda, Amazon DynamoDB, and AWS Identity and Access Management (IAM).
As we go through the tutorial, we will discuss the services in detail and point to resources that will help you get up to speed with them.
What Do We Need?
AWS Amplify : A way to create/host a webpage
AWS API Gateway : A way to invoke the math functionality
AWS Lambda : A way to do some math
AWS DynamoDB : Somewhere to store/return the math result
AWS IAM : A way to handle permissions
SetUp
Create an AWS Amplify to host a Web App
Open your favorite text editor on your computer. Create a new file and paste the HTML code mentioned as index-Original.html in code provided below.
<!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title>To the Power of Math!</title> </head> <body> To the Power of Math! </body> </html>
Save the file as index.html.
ZIP (compress) only the HTML file.
In a new browser window, log into the Amplify console. Note: I will be using the Asia Pacific (Mumbai) ap-south-1 Region for this tutorial.
In the Get Started section, under Host your web app, choose the orange Get started button.
Select Deploy without Git provider. This is what you should see on the screen:Choose the Continue button.
Choose the Continue button.
In the App name field, enter PowerOfMath.
For Environment name, enter dev.
Select the Drag and drop method. This is what you should see on your screen:
Choose the Choose files button.
Select the ZIP file you created in Step 3.
Choose the Save and deploy button.
After a few seconds, you should see the message Deployment successfully completed.
Test Your Web App
Select Domain Management in the left navigation menu.
Copy and paste the URL displayed in the form into your browser.
Application Architecture
Here is what our architecture looks like right now:
It is pretty minimal right now because we are only using the AWS Amplify console. We now have a live web app users can interact with. Next, we will create a Lambda function.
Create Lambda Function
To create the function
Click "Create function" in AWS Lambda Console.
Select "Author from scratch". Use name PowerOfMathFunction , select Python 3.9 as Runtime. Under Permissions, select "Use an existing role".
Click "Create function".
Replace the boilerplate coding with the following code snippet and click "Save"
Example Python Code
# import the JSON utility package
import json
# import the Python math library
import math
# define the handler function that the Lambda service will use an entry point
def lambda_handler(event, context):
# extract the two numbers from the Lambda service's event object
mathResult = math.pow(int(event['base']), int(event['exponent']))
# return a properly formatted JSON object
return {
'statusCode': 200,
'body': json.dumps('Your result is ' + str(mathResult))
}
Test Lambda Function
Let's test our newly created function. We haven't created DynamoDB and the API yet, so we'll do a sample echo operation. The function should output whatever input we pass.
Click the arrow on "Select a test event" and click "Configure test events".
Paste the following JSON into the event.
{ "base": 4, "exponent": 4 }
Click "Test", and it will execute the test event. You should see the output in the console.
Application Architecture
After setting up the Lambda Function our architecture will look like this:
You will notice we added the AWS Lambda service to the diagram, but it does not yet have a connection to AWS Amplify or our users. We will build that upcoming steps.
Create API
To create the API
Go to API Gateway console.
Click Create API.
Scroll down and select "Build" for REST API.
Give the API name as "PowerOfMathAPI", keep everything as is, click "Create API".
Each API is collection of resources and methods that are integrated with backend HTTP endpoints, Lambda functions, or other AWS services. Typically, API resources are organized in a resource tree according to the application logic. At this time you only have the root resource, but let's add a resource next Click "Actions", then click "Create Resource".
Select "POST" from drop down , then click checkmark.
The integration will come up automatically with "Lambda Function" option selected. Select "PowerOfMathFunction" function that we created earlier. As you start typing the name, your function name will show up.Select and click "Save". A popup window will come up to add resource policy to the lambda to be invoked by this API. Click "Ok".
Add Permission to Lambda Function.
Enable CORS.
Our API-Lambda integration is done!
Validate API
In the left navigation pane, select Resources.
The methods for our API will now be listed on the right. Choose POST.
Choose the small blue lightning bolt.
Paste the following into the Request Body field:
{ "base" : 2, "exponent" : 3 }
Deploy the API
In this step, you deploy the API that you created to a stage called dev.
1. Click "Actions", select "Deploy API".
2. Now it is going to ask you about a stage. Select "[New Stage]" for "Deployment stage". Give "dev" as "Stage name". Click "Deploy".
3. We're all set to run our solution! To invoke our API endpoint, we need the endpoint url. In the "Stages" screen, expand the stage "dev", select "POST" method, and copy the "Invoke URL" from screen.
Application Architecture
We added API Gateway and connected it to our existing Lambda function. Now, we can trigger our function with an API call. We are still missing the ability to generate this call from our web client. We will add our data table first now.
Create DynamoDB Table
Create the DynamoDB table that the Lambda function uses.
To create a DynamoDB Table
Open the DynamoDB console.
Choose Create table.
Create a table with the following settings.
Table name – PowerOfMathDynamoDb
Primary key – id (number)
Choose Create.
In the list of tables, select the table name, PowerOfMathDynamoDb.
In the General information section, show Additional info by selecting the down arrow.
Copy the Amazon Resource Name (ARN). You will need it later in this module.
Create and add IAM Policy to Lambda Function
Now that we have a table, let's edit our Lambda function to be able to write data to it. In a new browser window, open the AWS Lambda console.
Select the function we created in module two (if you have been using our examples, it will be called PowerOfMathFunction . If you don't see it, check the Region dropdown in the upper right next to your name to ensure you're in the same Region you created the function in.
We'll be adding permissions to our function so it can use the DynamoDB service, and we'll be using AWS Identity and Access Management (IAM) to do so.
Select the Configuration tab and select Permissions from the right side menu.
In the Execution role box, under Role name, choose the link. A new browser tab will open.
In the Permissions policies box, open the Add permissions dropdown and select Create inline policy.
Select the JSON tab.
Paste the following policy in the text area, taking care to replace your table's ARN in the Resource field in line 15:
{ "Version": "2012-10-17", "Statement": [ { "Sid": "VisualEditor0", "Effect": "Allow", "Action": [ "dynamodb:PutItem", "dynamodb:DeleteItem", "dynamodb:GetItem", "dynamodb:Scan", "dynamodb:Query", "dynamodb:UpdateItem" ], "Resource": "YOUR-TABLE-ARN" } ] }
This policy will allow our Lambda function to read, edit, or delete items, but restrict it to only be able to do so in the table we created.
Choose the blue Review Policy button.
Next to Name, enter PowerOfMathPolicy.
Choose the blue Create Policy button.
You can now close this browser tab and go back to the tab for your Lambda function.
Modify Lambda to write to DynamoDB
Select the Code tab and select your function from the navigation pane on the left side of the code editor.
Replace the code for your function with the following:
# import the JSON utility package import json # import the Python math library import math # import the AWS SDK (for Python the package name is boto3) import boto3 # import two packages to help us with dates and date formatting from time import gmtime, strftime # create a DynamoDB object using the AWS SDK dynamodb = boto3.resource('dynamodb') # use the DynamoDB object to select our table table = dynamodb.Table('PowerOfMathDatabase') # store the current time in a human readable format in a variable now = strftime("%a, %d %b %Y %H:%M:%S +0000", gmtime()) # define the handler function that the Lambda service will use an entry point def lambda_handler(event, context): # extract the two numbers from the Lambda service's event object mathResult = math.pow(int(event['base']), int(event['exponent'])) # write result and time to the DynamoDB table using the object we instantiated and save response in a variable response = table.put_item( Item={ 'ID': str(mathResult), 'LatestGreetingTime':now }) # return a properly formatted JSON object return { 'statusCode': 200, 'body': json.dumps('Your result is ' + str(mathResult)) }
Choose the Deploy button at the top of the code editor.
Test The Changes
Choose the orange Test button.
You should see an Execution result: succeeded message with a green background.
In a new browser tab, open the DynamoDB console.
In the left-hand navigation pane, select Tables > Explore items.
Select PowerOfMathDynamoDb, which we created earlier in this module.
Select the Items tab on the right.
Items matching your test event appear under Items returned. If you have been using our examples, the item ID will be the number and value will be the result of the math operation performed by Lambda.
Every time your Lambda function executes, your DynamoDB table will be updated. If the same name is used, only the time stamp will change.
Application Architecture
let's look at our current architecture:
We added two services in this step: DynamoDB (for storage) and IAM (for managing permissions securely). Both are connected to our Lambda function, so that it can write to our database. The final step is to add code to our client to call the API Gateway.
Add Interactivity to Our Web App
In this module, we will update the static website we created to invoke the REST API . This will add the ability to display text based on what you input.
Update Web App with Amplify Console
Open the index.html file you created.
Replace the existing code with the following:
<!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title>To the Power of Math!</title> <!-- Styling for the client UI --> <style> h1 { color: #FFFFFF; font-family: system-ui; margin-left: 20px; } body { background-color: #222629; } label { color: #86C232; font-family: system-ui; font-size: 20px; margin-left: 20px; margin-top: 20px; } button { background-color: #86C232; border-color: #86C232; color: #FFFFFF; font-family: system-ui; font-size: 20px; font-weight: bold; margin-left: 30px; margin-top: 20px; width: 140px; } input { color: #222629; font-family: system-ui; font-size: 20px; margin-left: 10px; margin-top: 20px; width: 100px; } </style> <script> // callAPI function that takes the base and exponent numbers as parameters var callAPI = (base,exponent)=>{ // instantiate a headers object var myHeaders = new Headers(); // add content type header to object myHeaders.append("Content-Type", "application/json"); // using built in JSON utility package turn object to string and store in a variable var raw = JSON.stringify({"base":base,"exponent":exponent}); // create a JSON object with parameters for API call and store in a variable var requestOptions = { method: 'POST', headers: myHeaders, body: raw, redirect: 'follow' }; // make API call with parameters and use promises to get response fetch("YOUR API GATEWAY ENDPOINT", requestOptions) .then(response => response.text()) .then(result => alert(JSON.parse(result).body)) .catch(error => console.log('error', error)); } </script> </head> <body> <h1>TO THE POWER OF MATH!</h1> <form> <label>Base number:</label> <input type="text" id="base"> <label>...to the power of:</label> <input type="text" id="exponent"> <!-- set button onClick method to call function we defined passing input values as parameters --> <button type="button" onclick="callAPI(document.getElementById('base').value,document.getElementById('exponent').value)">CALCULATE</button> </form> </body> </html>
Make sure you add your API Invoke URL on Line 60 . Note: If you do not have your API's URL, you can get it from the API Gateway console by selecting your API and choosing stages.
Save the file.
ZIP (compress) only the HTML file.
Open the Amplify console.
Choose the web app created in module one.
Choose the white Choose files button.
Select the ZIP file you created in Step 5.
When the file is uploaded, a deployment process will automatically begin. Once you see a green bar, your deployment will be complete.
Test the Updated Web App
Choose the URL under Domain.
Your updated web app should load in your browser.
Fill in the base and exponent value and choose the Call Calculate button.
You should see a alert that will give the user the result of the math operation performed.
The resultant value will be stored in DynamoDb table we created earlier.
Conclusion
We have successfully created a serverless APP using Amplify, API Gateway, Lambda, IAM and DynamoDB!