Code repo https://github.com/PacktPublishing/Hands-on-Serverless-Architecture-with-AWS-Lambda
NOTE" original video was done using Node.js 6.10 runtime for Lambda - I'll be using the latest at the time of writing which s 8.10
Some useful 8.10 lnks
-
Lamdba Node.js 8.10 truntime uses async/await for promises https://aws.amazon.com/blogs/compute/node-js-8-10-runtime-now-available-in-aws-lambda/
-
Logging in Lambda https://docs.aws.amazon.com/lambda/latest/dg/nodejs-prog-model-logging.html
-
DynamoDB SDK get() is now getItem() https://docs.aws.amazon.com/AWSJavaScriptSDK/latest/AWS/DynamoDB.html#getItem-property
-
Adding Amazon Cognito Identity into client side code https://docs.aws.amazon.com/cognito/latest/developerguide/setting-up-the-javascript-sdk.html and https://github.com/aws-amplify/amplify-js#web-development/
- AWS account
- Create a new Lambda function
- Named customerapp
exports.handler = async (event) => { // TODO implement const response = { statusCode: 200, body: JSON.stringify('Hello from Lambda!') }; console.log("Hello Phamalicious") console.log('value1 =', event.key1); console.log('value2 =', event.key2); console.log('value3 =', event.key3); return response; };
- Create a new role using role templates
- Named customerrole
- AWSLambdaBasicExecutionRole
- Named customerrole
- Create a new table
- Name Customer
- Partition key
- Named 'ID' of type 'Number'
- Accept all defaults
- View the table
- View items
- Insert some sample data with schema as we go
{ { "ID": "1", "Name": "John" }, { "ID": "2", "Name": "Mark" }, { "ID": "3", "Name": "James" }, } }
- Edit Lambda function to read from Customer table using the SDK
- Import the sdk and use promises in Node.js 8.10
let AWS = require('aws-sdk'); exports.handler = async (event) => { let docClient = new AWS.DynamoDB(); let table = "Customer"; let id = "1"; let params = { TableName: table, Key: { "ID": { N: id } } }; console.log("0"); // return docClient.getItem(params, function(err, data) { // console.log("1"); // if (err) { // console.log("2"); // console.error("Unable to read item. Error JSON:", JSON.stringify(err, null, 2)); // } else { // console.log("3"); // console.log("GetItem succeeded:", JSON.stringify(data, null, 2)) // } // }).promise(); // return await docClient.getItem(params).promise(); let data; try { data = await docClient.getItem(params).promise(); } catch (err) { console.log(err); return err; } return data.Item; };
- Add the presmission to the Lambda's role to read from Dynamo
- In IAM, for role customerrole
- AmazonDynamoDBReadOnlyAccess
- In IAM, for role customerrole
- Testing will produce output with return value from method call
Response: { "Item": { "ID": { "N": "1" }, "Name": { "S": "John" } } } Request ID: "eaa9fdea-da5e-11e8-b001-ad153824f7d3" Function Logs: START RequestId: eaa9fdea-da5e-11e8-b001-ad153824f7d3 Version: $LATEST 2018-10-28T03:09:43.714Z eaa9fdea-da5e-11e8-b001-ad153824f7d3 0 END RequestId: eaa9fdea-da5e-11e8-b001-ad153824f7d3 REPORT RequestId: eaa9fdea-da5e-11e8-b001-ad153824f7d3 Duration: 802.15 ms Billed Duration: 900 ms Memory Size: 128 MB Max Memory Used: 30 MB
- Create a new API
- API Named customer
- Leave Endpoint Type as Regional
- Create a method
- Actions > Create Method > POST
- Once created, leave Integration Type as Lambda Function
- Lambda Function is customerapp (should auto complete)
- Leave other defults and click Save (arn:aws:lambda:ap-southeast-2:040656198761:function:customerapp)
- Test API gateway method
- Request body
{"ID":"3"}
- Edit Lambda function to get ID from incoming request body
let AWS = require('aws-sdk'); exports.handler = async (event) => { let docClient = new AWS.DynamoDB(); let table = "Customer"; let id = event["ID"]; console.log("checking id=", id); let params = { TableName: table, Key: { "ID": { N: id } } }; let data; try { data = await docClient.getItem(params).promise(); } catch (err) { console.log(err); return err; } return data.Item; };
- Deploy API
- Actions > Deploy API
- Create a new stage by selecting New Stage and call it Production
- Note the invocation URL https://XXXXXXXXXX.execute-api.ap-southeast-2.amazonaws.com/Production
- Enable CORS
- Actions > Enable CORS
- Click Enable CORS
- Deploy API again Actions > Deploy API and select previously created Production stage
- Test public POST API using sample static webpage
<!DOCTYPE html> <html> <head> <script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script> <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css" integrity="sha384-BVYiiSIFeK1dGmJRAkycuHAHRg32OmUcww7on3RYdg4Va+PmSTsz/K68vbdEjh4u" crossorigin="anonymous"> <script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script> <script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/js/bootstrap.min.js" integrity="sha384-Tc5IQib027qvyjSMfHjOMaLkfuWVxZxUPnCJA7l2mCWNIpG9mGCD8wGNIcPD7Txa" crossorigin="anonymous"></script> <script> $(document).ready(function(){ console.log("In ready!"); $("button").click(function(){ console.log("Button click!"); var jsonData = {}; jsonData['ID'] = document.getElementById("cid").value; console.log(jsonData['ID']); $.ajax({ type: "POST", url: "https://XXXXXXXX.execute-api.ap-southeast-2.amazonaws.com/Production", crossDomain: true, data: JSON.stringify({"ID": jsonData['ID']}), contentType: "application/json", dataType: "json", success: function(data, status) { console.log(data); document.getElementById("Cname").value = data.PersonName.S; } }); }); }); </script> </head> <body> <div class="jumbotron"> <div class="container text-center"> <h1>Customer Data</h1> <p>Data</p> </div> </div> <div class="form-group"> <div class="col-sm-offset-2 col-sm-10"> <div class="col-xs-2"> <label for="usr">ID:</label> <input type="text" class="form-control" id="cid"> </div> </div> <div class="col-sm-offset-2 col-sm-10"> <div class="col-xs-2"> <label for="usr">Name:</label> <input type="text" class="form-control" id="Cname"> </div> </div> <div class="col-sm-offset-2 col-sm-10"> <div class="col-xs-2"> <button type="submit" value="Submit">Get Name</button> </div> </div> </div> </body> </html>
- Create a enw bucket
- Edit public access settings for this bucket
- Leave default options
- Properties > Static website hosting
- Enable by selecting radio button 'Use this bucket to host a website'
- Index Document = same test page in previous API Gateway testing
- Note the public URL
- Click Save
- Upload html file
- Overview tab
- Click Upload and upload the file
- After uploading, you're taken back to the Overview tab
- Click into the uplaoded file in the bucket listing and click Mark public
- Edit public access settings for this bucket
WebApp --> UserPool --> Tokens --> IdentityPool --> AWS Credentials --> AWS Services
-
Define a user pool
- Create a user pool
- Named customerapp
- Stepping through the settings for this exercise
- Leave default Username
- Leave default standard attributes
- Leave default password policy
- Leave default allowing users to sign themselves up
- Leave remaining defaults (including App clients as default too as we'll do that later)
- Click Create pool
- In the confirmation screen, note t Pool iid
- Create a user pool
-
Define an App Client
- Go to user pool for customerapp > App clients
- Click on Add an app client
- Name as demoapp
- Uncheck 'Generate client secret' as this is not needed by Javascript client app
- Check 'Enable username-password (non-SRP) flow for app-based authentication (USER_PASSWORD_AUTH)'
- In the App Client confirmation screen, note the App client id
-
Using a demo page to sign up or register by consuming the Cognito API from Javascript
<!DOCTYPE html> <html> <head> <script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script> <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css" integrity="sha384-BVYiiSIFeK1dGmJRAkycuHAHRg32OmUcww7on3RYdg4Va+PmSTsz/K68vbdEjh4u" crossorigin="anonymous"> <script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script> <script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/js/bootstrap.min.js" integrity="sha384-Tc5IQib027qvyjSMfHjOMaLkfuWVxZxUPnCJA7l2mCWNIpG9mGCD8wGNIcPD7Txa" crossorigin="anonymous"></script> <script src="node_modules/amazon-cognito-identity-js/dist/amazon-cognito-identity.min.js"></script> <script> $(document).ready(function(){ console.log("In ready!"); $("button").click(function(){ console.log("Button click!"); var name = document.getElementById("username").value; var password = document.getElementById("password").value; var email = document.getElementById("email").value; var CognitoUserPool = AmazonCognitoIdentity.CognitoUserPool; var poolData = { UserPoolId: 'ap-southeast-2_od8tqWHJT', ClientId: '57ni95hiu2mvulnss24nk6kt10' }; var userPool = new AmazonCognitoIdentity.CognitoUserPool(poolData); var attributeList = []; var dataEmail = { Name: 'email', Value: email }; var dataGivenName = { Name: 'given_name', Value: name }; var attributeEmail = new AmazonCognitoIdentity.CognitoUserAttribute(dataEmail); var attributeGivenName = new AmazonCognitoIdentity.CognitoUserAttribute(dataGivenName); attributeList.push(attributeEmail); attributeList.push(attributeGivenName); userPool.signUp(name, password, attributeList, null, function(err, result) { if (err) { alert(err.message || JSON.stringify(err)); return; } cognitoUser = result.user; console.log('Username is ' + cognitoUser.getUsername()); }); }); }); </script> </head> <body> <div class="jumbotron"> <div class="container text-center"> <h1>Sample Application</h1> <p>Sign up</p> </div> </div> <div class="form-group"> <div class="col-sm-offset-2 col-sm-10"> <div class="col-xs-2"> <label for="usr">Username:</label> </div> <div class="col-xs-2"> <label for="usr">Password:</label> </div> <div class="col-xs-2"> <label for="usr">Email:</label> </div> </div> <div class="col-sm-offset-2 col-sm-10"> <div class="col-xs-2"> <input type="text" class="form-control" id="username"> </div> <div class="col-xs-2"> <input type="password" class="form-control" id="password"> </div> <div class="col-xs-2"> <input type="email" class="form-control" id="email"> </div> <div class="col-xs-2"> <button type="submit" value="Submit">Register</button> </div> </div> </div> </body> </html>
- Noting that to import the amazon-cognito-identity library, using npm in the root of the html files
// install only the auth module of aws-amplify npm install @aws-amplify/auth --save
-
Then search for
amazon-cognito-identity.min.js
should return a relative path similar tonode_modules/amazon-cognito-identity-js/dist/amazon-cognito-identity.min.js
-
Upload only the follwoing files to the root of the same S3 bucket to host statically (remembering to make public)
- Demo.html
- node_modules/amazon-cognito-identity-js/dist/amazon-cognito-identity.min.js (directories too)
-
Create an authorizer for the customerapp API with the customerapp User Pool