In this lab we will create a full microservice, by using a database (Amazon DynamoDB), a computing service (AWS Lambda), and an API service to expose the service to users and other microservices.
You will build, deploy, and test the microservice.
You need:
<prefix>
on the name of your resources to avoid name collisions. Use your initials, or something else unique for the pair {account, region}.
<prefix>
on the name of the resources.In this lab we are going to create a simple microservice that computes statistics for the top 10 players of a game.
The architecture is shown in the diagram below
Tables
menu option, and then click on Create table
.Table name*
, input <prefix>GameTopX
.Primary key*
, input SessionId
.Create
.Wait until the table is created. We will use this task to insert some data into this table.
Items
tab.Create item
. DynamoDB will open a window so you can input data on it.Tree
, and select Text
.{
"SessionId": "TheTestSession",
"TopX": [
{
"Level": 5,
"Lives": 3,
"Nickname": "alanis",
"Score": 1500,
"Shots": 372,
"Timestamp": "2019-06-21T00:07:01.328Z"
},
{
"Level": 2,
"Lives": 0,
"Nickname": "blanka",
"Score": 300,
"Shots": 89,
"Timestamp": "2019-06-26T08:28:52.264Z"
},
{
"Level": 1,
"Lives": 0,
"Nickname": "bugsbunny",
"Score": 200,
"Shots": 88,
"Timestamp": "2019-06-26T08:49:19.049Z"
},
{
"Level": 1,
"Lives": 0,
"Nickname": "billythekid",
"Score": 195,
"Shots": 46,
"Timestamp": "2019-06-25T22:43:32.050Z"
},
{
"Level": 1,
"Lives": 0,
"Nickname": "nobodyknows",
"Score": 65,
"Shots": 17,
"Timestamp": "2019-06-26T08:33:47.951Z"
},
{
"Level": 1,
"Lives": 0,
"Nickname": "mordorlord",
"Score": 5,
"Shots": 1,
"Timestamp": "2019-06-26T08:29:29.639Z"
},
{
"Level": 1,
"Lives": 3,
"Nickname": "naruto",
"Score": 0,
"Shots": 0,
"Timestamp": "2019-06-26T14:24:17.748Z"
},
{
"Level": 1,
"Lives": 3,
"Nickname": "ramon",
"Score": 0,
"Shots": 0,
"Timestamp": "2019-06-24T17:36:38.646Z"
},
{
"Level": 1,
"Lives": 3,
"Nickname": "bruceb",
"Score": 0,
"Shots": 0,
"Timestamp": "2019-06-24T12:24:12.238Z"
},
{
"Level": 1,
"Lives": 3,
"Nickname": "hackz",
"Score": 1,
"Shots": 0,
"Timestamp": "2019-06-24T17:36:38.646Z"
}
]
}
Save
button. Check that the record was properly created.Overview
tab. Scroll down on that page, then find and copy the Amazon Resource Name (ARN)
for the table to an auxiliary text file.Visit the AWS Lambda page on the AWS Console
Click on the button Create a function
. If the button is not visible, check on the console for a menu on the left, with the label Functions, and then hit on the Create function
button
Select Author from scratch
.
Under the section Basic information
:
Function Name
, input <prefix>GameTopXStats
Runtime
, select the latest supported version for Node.jsPermissions
, we need to give permissions to our Lambda function to get data from the DynamoDB table that we have just created. Expand the section, and select Create a new role with basic Lambda permissions
.Create function
button. You are going to get transported to the Designer page for the function.Function code
Deploy
.Scroll up on the page of your Lambda function, and find the tab Permissions
Check that there is a link for the role that was created for you (View the <prefix>GameTopXStats-role-<some suffix>
). Click on it. It will open another tab on your browser, showing to you the role configuration. See that AWS Lambda has automatically added permissions so your function can provide log records to CloudWatch Logs.
Click on Add inline policy
.
On the Create policy page, on the tab Visual editor
Service
section, select DynamoDB
Actions
section, on the Filter actions
input box, write GetItem
, and mark the method GetItem
.Resources
section, select Specific
, and then click on the label Add ARN
. On the top of the window, for the field Specify ARN for table
, copy the DynamoDB table ARN that you have previously saved.
If you are not sure on how to to get the ARN, just go to DynamoDB, select the <prefix>SessionTopX
table, and on the tab Overview you are going to find its ARN at the end of the page.On the bottom right, click on the button Review Policy. You are going to be taken to the Review policy
page
On that page, for the field Name*
, input DynamoDBPermissions
On the bottom of the page, click on Create policy
You can close this tab and get back to the Lambda Management Console page
Visit the code of your Lambda function, and check that the function readTopxDataFromDatabase (it is at the top) specifies the table to query by reading the variable process.env.TABLENAME. Let’s configure it with the name of the table that we have created:
Configuration
Environment variables
.Edit
.Add environment variable
. You will see two fields, one for Key, and another one for Value.Key
, input TABLENAME
.Value
, input the name that you used to define your DynamoDB table (<prefix>GameTopX
).Save
.Before moving forward, let’s be sure that our lambda function is working properly. As we are intending to integrate the Lambda Function with an (to be created) API Gateway component, let’s create some events that will help us to check if our code is correctly implemented.
Select a test event
, and then click on Configure test events
. A new window will be presented, so you can configure events.Create new test event
choice-button is selected.Event name
, input and appropriate name (for the first one, the suggestion is DirectIntegration).{
"params": {
"path": {},
"querystring": {
"sessionId": "TheTestSession"
}
}
}
Scroll down and click on the button Create, on the lower right of the window.
Click on the Test button. Scroll up to check the results on the screen (inside the div entitled Execution result: succeeded(logs)
).
- Expand the Details section. You should to get the response below:
{
"isBase64Encoded": false,
"statusCode": 200,
"body": "[{\"Nickname\":\"alanis\",\"Position\":1,\"Performance\":4.032258064516129},{\"Nickname\":\"blanka\",\"Position\":2,\"Performance\":3.3707865168539324},{\"Nickname\":\"bugsbunny\",\"Position\":3,\"Performance\":2.272727272727273},{\"Nickname\":\"billythekid\",\"Position\":4,\"Performance\":4.239130434782608},{\"Nickname\":\"nobodyknows\",\"Position\":5,\"Performance\":3.823529411764706},{\"Nickname\":\"mordorlord\",\"Position\":6,\"Performance\":5},{\"Nickname\":\"naruto\",\"Position\":7,\"Performance\":0},{\"Nickname\":\"ramon\",\"Position\":8,\"Performance\":0},{\"Nickname\":\"bruceb\",\"Position\":9,\"Performance\":0},{\"Nickname\":\"hackz\",\"Position\":10,\"Performance\":-1}]",
"headers": {
"Content-Type": "application/json"
}
}
Repeat the steps 1 to 6 above, for the following test event configurations:
Test event name | body of the event |
---|---|
DirectMissingSession | { "params": { "path": {}, "querystring": {} } } |
DirectWrongSession | {"params": {"path": {}, "querystring": {"sessionId": "WRONG"} } } |
ProxyIntegration | { "queryStringParameters": { "sessionId": "TheTestSession } } |
ProxyWrongSession | { "queryStringParameters": { "sessionId": "WRONG" } } |
ProxyMissingSession | { "queryStringParameters": {} } |
If at this point everything went well, then we have our Lambda function working properly. Now, let’s expose it via an API.
You can execute this task by creating a new API, or by adding a resource to an existing API. If the latter is the case, skip to the step 2 of this task.
Visit the API Gateway console page.
Depending on the state of your account, you can find a Create API
or a Get Started
button. Click on the one that has appeared to you. You are going to be taken to a Create API page.
Create API
.Choose and API type
, select REST API
, and click on Build
Create new API
section, select New API
Settings
, input the following:
API name*
, input <prefix>GameAPI
Description
, input API for the Game environment
Endpoint Type
, select Edge optimized
Create API
You are going to be taken to a page where you can configure the resources of your API. See that this page has a drop-down button labeled Actions
.
On the Resources page, click on the drop-down button Actions
, and then click on Create Resource
A section entitled New Child Resource will be shown
On the section New Child Resource
Resource Name*
, input topxstatistics
. The field Resource Path will be automatically updated with this same value. Leave it as it is.Enable API Gateway CORS
.Create Resource
. You will see that the Resources section of the page will be updated with this new resourceSelect the resource topxstatistics, then click on the button Actions
, and then on Create Method
. A drop-down list will appear.
On the drop-down list, select GET
.
Click on the check button at the right of the GET method, to confirm its creation. A section entitled /topxstatistics - GET - Setup
will appear, for you to configure the method
On the section /topxstatistics - GET - Setup:
Integration type
, make sure that Lambda function
is selected.Lambda Function
, begin typing the name of the Lambda function that you have created (GameTopXStats). A selection pop-up will appear, so you can select the name of the function. Select itSave
.
A pop-up window will appear, asking you to give permissions to your API Gateway to access invoke the lambda function. Click on the OK
button.Click on Method Request
Find the section URL Query String Parameters
and expand it:
Add query string
.Name
, input sessionId
.Required
checkbox.On the top, click on the label <- Method Execution
to get back to the configuration page
Click on Integration Request
Find the section URL Query String Parameters
, and expand it:
Add query string
.sessionId
.method.request.querystring.sessionId
Scroll down to the section Mapping Templates
, and expand it:
Request body passthrough
, select When there are no templates defined (recommended)
.Add mapping template
. A field will be make available under the section Content-Type
.application/json
(you need to write it)Click on the check button
to confirm the definition of the content-type.
Scroll down a bit. A window will be made available for you to input with the mapping template. Mapping templates are a powerful resource for you to configure integration in API Gateway without needing to write code.Generate template
, select Method Request passthrough
.
API Gateway will add, automatically, a mapping template that forwards the request to the Lambda function in a structured way.Save
.Scroll to the top, and click on the label <- Method Execution
to get back to the configuration page
Here we are going to test the API that we just defined. We are going to execute 3 tests.
TEST
(you must click exactly on the label). You are going to be taken to the testing page for that resource/method{topxstatistics}
under the Query Strings
sectionTest
button with the lightning symbol, down on the page. As we have provided no sessionId yet, your Lambda function must respond accordingly, with a 400 statusCode. On the right of the screen you will be able to check the response body, response headers, and the logs, in a screen similar to the one below
{topxstatistics}
sessionId=WRONG
Test
.{
"isBase64Encoded": false,
"statusCode": 404,
"body": "Inexistent session",
"headers": {
"Content-Type": "text/plain"
}
}
Still at the testing page, on the Query Strings
section, input the following value to the field {topxstatistics}
sessionId=TheTestSession
This is the same value that we have used to insert data on DynamoDB. Check on the previous Step.
Click on Test
.
Now you should receive a response similar to the following one:
{
"isBase64Encoded": false,
"statusCode": 200,
"body": "[{\"Nickname\":\"alanis\",\"Position\":1,\"Performance\":4.032258064516129},{\"Nickname\":\"blanka\",\"Position\":2,\"Performance\":3.3707865168539324},{\"Nickname\":\"bugsbunny\",\"Position\":3,\"Performance\":2.272727272727273},{\"Nickname\":\"billythekid\",\"Position\":4,\"Performance\":4.239130434782608},{\"Nickname\":\"nobodyknows\",\"Position\":5,\"Performance\":3.823529411764706},{\"Nickname\":\"mordorlord\",\"Position\":6,\"Performance\":5},{\"Nickname\":\"naruto\",\"Position\":7,\"Performance\":0},{\"Nickname\":\"ramon\",\"Position\":8,\"Performance\":0},{\"Nickname\":\"bruceb\",\"Position\":9,\"Performance\":0},{\"Nickname\":\"hackz\",\"Position\":10,\"Performance\":-1}]",
"headers": {
"Content-Type": "application/json"
}
}
Check that the status code is 200, and that the body contains the expected result, but not in the expected format. We would like to have it as a JSON. Let’s fix this.
On the top of the page, click on <- Method Execution
to get back to the configuration page
Integration Response
. The page will show a line with a configuration for the Method response status 200.Mapping Templates
, and expand it
We are going to configure a Mapping Template that converts the response from String to a JSON.application/json
under the section Content-Type
, click on it.Add mapping template
.Content-Type
, input application/json
.#set($inputRoot = $input.path('$'))
## The next line changes the HTTP response code with the one provided by the Lambda Function
#set($context.responseOverride.status = $inputRoot.statusCode)
## Decoding base64 (this could have been left to the application)
#if( $inputRoot.isBase64Encoded == true )
$util.base64Decode($inputRoot.body)
#else
$inputRoot.body
#end
Save
button below the mapping template field (see that are two Save buttons on that page. Be sure of clicking on the one that we mentioned).<- Method Execution
to get back to the configuration pageYour need to deploy the API so you can have access to it, externally.
Visit the home page for API Gateway.
Be sure that you have your <prefix>GameAPI
selected (just for the case of you to have other APIs on your account).
Click on the drop-down Actions
, and then click on Deploy API
. The Deploy API pop-up window will be shown to you.
On the Deploy API window:
[New Stage]
.Stage name*
, input the value prod
.Stage description
, input Production environment for the API
.Deployment description
, input first deployment
.Deploy
You are going to be forwarded to the Stage page of the console, where the your deployment URL is at the top. It will be something similar to the following:
Invoke URL: https://<API-id>.execute-api.<region>.amazonaws.com/prod
At the left, you will see the section Stages, and prod under it. Click on prod
to expand the section. You will see the topxstatistics
resource, with GET and OPTIONS under it.
GET
method under the topxstatistics
resource. Copy the Invoke URL for the resourcehttps://xyz.execute-api.region1.amazonaws.com/prod/topxstatistics
), run each one of these requests on the browser tab or window. IMPORTANT: Depending if you are creating a new API or just creating a new resource in an existing API, you may need to fix the path to the resource. Visit you API deployment on the API Gateway console, and check the path to the resources topxstatistics.Check the results of the Network log of your browser. Check that the HTTP return codes area appropriate.
One problem remains to your API: it is publicly exposed. There are many options to implement security for your API. In this lab, we are going to implement access control via API KEY. This kind of access is recommended for Business-to-Business scenarios, when you have a key shared exclusively with the business who wants to consume your API.
Enabling the security via API Key
Resources
.GET
method for /topxstatistics.Method Request
.API Key Required = true
( don’t forget of confirming it ). Your page will be similar to the one below:
Creating an API Key
API Keys
on the menu on the left hands side of the console (this is NOT under your API. It is a “standalone” menu option).Actions
, and then on Create API key
.
Name*
, input your own name.API key*
, select Auto Generate
.Description
field, input This is the key that I've created for myself
.Save
. You are going to be presented to a page where you have the details about the key.Show
label which is at the right hand side of API key
Creating the Usage Plan to be used by the API. Think of a Usage Plan as a mechanism to control the way a certain authorized entity (represented by the API Key) can access certain resources of your API.
On the menu on the left, click on Usage Plans
(it is right above the API Keys option). You are going to be taken to the Create Usage Plan
page
Name*
, input Usage plan for <your name> usage plan
. (replace <your name>
with…. your name!)Description
, input This usage plan was created to learn about API Keys and usage plans
.Enable throttling
is unchecked (we are not testing throttling on this lab).Enable quota
is unchecked (we are not testing quota validation on this lab).Next
button.Now you must be on the page Associate API Stages
:
Add API Stage
.API
, select your API.Stage
, select prod
.Next
.Now you must be on the Usage Plan API Keys
:
Add API Key to Usage Plan
.Name
, begin typing the name that you used to define your API Key (the field is case-sensitive). Select your key.Done
.Now your API Key is created, and associated to a Usage Plan, what makes it available to be used. Let’s test it.
Testing your API
To test the API in production, publicly exposed, we are going to use CURL. CURL is available on Cloud9 if you are using it. If using your own machine and you don’t have CURL, you can download and install it from here.
After installing it, run the following commands on your terminal (or console prompt, depending on how you name it).
While running the tests below, be sure of:
curl --verbose --header "x-api-key: <put your API key here>" https://<API URL>
curl --verbose --header "x-api-key: <put your API key here>" https://<API URL>?sessionId=
curl --verbose --header "x-api-key: <put your API key here>" https://<API URL>?sessionId=NONEXISTING
curl --verbose --header x-api-key: <put your API key here>" https://<API URL>?sessionId=TheTestSession
You have finished the lab.
Clear your account by: