AWS API Gateway + Lambda(NodeJS function) + DynamoDB to PutItem and GetItem Test Drive
I'd be really pressed to find relevant and even helpful material online, with actually working examples if you want to get started with API Gateway and Integration via Lambda, particularly when you wish to try interact with the simple databases like dynamoDB. Yes there is a lot of material, but if you're only starting out and exploring the space. And while its obviously not a non-starter, its a laborious task to get going, fine tuning the elements of the product, working out integration semantics and spending hours in the Docs, as helpful as they may be.
Having played with API deployment and Deployed API proxies with the likes of TYK in the past, This AWS offering of cloud-based API configuration, management and infinite scalability really does strikes accord with me. In particular when business logic is not only heading into Cloud and Containerisation but further, - serverless. And all with good reasons mentioned above and not forgetting the key financial incentive. It's really cost effective, not managing your own services you now.
Its been a little while since i've completed my AWS Certifications, and with that helpful knowhow I decided to take a plunge and hands-on to investigate how such offering and projects fare, not having worked on these AWS products in the past.
The Requirement
Deploy a Simple API, which enables PUT and GET requests
In Particular, You should be able to add a record to a database, dynamodb so,
name: James, dateOfBirth: 1990-01-01 would do
The Workings;
I decided to use the native proxy for put function, directly "calling" dynamoDB, its a simpler method, and does not need the coding overhead.
I also decided on the dynamoDB to be my key-pair storage for this trial
Finally, since we want some computational result from GET request, its a lambda with good old NodeJS framework.
My database
Its a fairly straight forward setup, via AWS Console
Once navigate into DynamoDB product offering, simply create a table name of choice. I created namespaces
.
Put integration
Actual PUT request had to be mapped in PUT integration as follows:
#set($inputRoot = $input.path('$'))
{
"TableName": "namespaces",
"Item": {
"name": {
"S": "$input.params('name')"
},
"dateOfBirth": {
"S": "$input.path('$.dateOfBirth')"
}
}
}
GET Requests
This was a what one would find naturally tricky especially if it's not your daily bread-n-butter there.
GET Integration had to be mapped, to ensure we can reach this in our lambda call.
{
"myname" : "$input.params('name')"
}
Once we have our name variable, we can have a function call to dynamoDB and perform GetItem on the table, the latter was predefined in lambda code.
---- example lambda code ----
var aws = require('aws-sdk');
exports.handler = function(event, context) {
var dynamo = new aws.DynamoDB({
region: 'eu-west-2',
maxRetries: 0
});
// getting my passed var over
var nameInfo = event.myname;
console.log('Will run getItem on name key : '+nameInfo);
// params for my dynamodb func
var params = {
TableName : 'namespaces',
Key : {
"name" : {
"S" : nameInfo
},
},
AttributesToGet: [
'name', 'dateOfBirth'
],
ConsistentRead: true,
}
dynamo.getItem(params,
function(err, data) {
console.log("Pre-processing - data view: ",data);
if (err) {
console.log(err, err.stack); // an error occurred
context.done(err);
}
else {
// once its all great success, lets work on dates
var date = new Date();
var getFullYear = date.getFullYear();
var originalDateOfBirth=data.Item.dateOfBirth.S
var strippeddate=originalDateOfBirth.toString().split("-");
var Original_Birthday_MD=strippeddate[1].replace(/(^|-)0+/g, "$1")+"-"+strippeddate[2].replace(/(^|-)0+/g, "$1");
var Current_Date_MD=date.getMonth()+'-'+date.getUTCDate()
console.log('Comparing Original_Birthday_MD: '+Original_Birthday_MD+' & Current_Date_MD: '+Current_Date_MD )
//So, erm, How many days until your birthday?
var oneDay = 24*60*60*1000; // hours*minutes*seconds*milliseconds
var Original_Birthday_MD_ = new Date(getFullYear+"-"+Original_Birthday_MD);
var Current_Date_MD_ = new Date(getFullYear+"-"+Current_Date_MD);
console.log('Comparing FULL DATE Original_Birthday_MD_: '+Original_Birthday_MD_+' & Current_Date_MD_: '+Current_Date_MD_ )
var daysUntilBday = Math.round(Math.abs(Current_Date_MD_.getTime() - Original_Birthday_MD_.getTime() )/(oneDay));
console.log('How many Days until my birthday? '+daysUntilBday);
// Is this your lucky day
if (daysUntilBday == 0)
{ // lets rock this overriding data response, because we can
data='Happy Birthday '+nameInfo+'!';
}
else if (daysUntilBday < 5)
{ // perhaps even warn if they have bday upcoming
data='Hello '+nameInfo+'! Your Birthday is in '+daysUntilBday+' days.';
}
console.log(data); // successful response
context.succeed(data); // must ssuccessfully terminate the function and return !!
}
});
};
[Latest and Greatest code on] (https://gist.github.com/jpantsjoha/8479ffb137ea873a169fc00e056ed77a)
Common gotchas I came across
- API Gateway + lambda working can lead to false error rates which will send you down the colossal time wasting route. Thus;
- Its all about the mapping, whether you map calls to your dynamoDB direct or mapping vars to lambda function.
- Ensure you lambda function isn't timing out too early resulting in errors which have nothing to do (necessarily) with your code, though be thorough checking it
- Ensure you literally refresh browser if you have to when you are sampling back and forth, between API-Gateway integration test route and the lambda code working
- When aforementioned point 2 works fine and you're happy with it, and you're still seeing the errors on external url - if you happen to be curl testing this also, - chances are you forgot and need to, republish your api through to relevant (or brand new) "stage". Yes this may be a new DNS name for the external access your api gateway, - just saying, seems to be learning the hard way here.
- Lambda "test" section was helpful - albeit only API Proxy data - with pre-filed random data, so if you're doing something specific, this could be misleading, - so do bear that in mind.
The Fun Part
Time To Test and Play.
Putting Several New Users, bob, kev, kal
bob, will be DOB 1980-01-01
kev, will be DOB closer to today but not today, so 1980-04-30
kal, will be DOB on today's date, to check out birthday message function, so 1980-05-02
And, Lets Curl/URL the results
(URL secrecy aside, this is like for like)
jp@jpmac~$ curl https://secret.execute-api.eu-west-2.amazonaws.com/hello/kal
"Happy Birthday kal!"
jp@jpmac~$ curl https://secret.execute-api.eu-west-2.amazonaws.com/hello/kev
"Hello kev! Your Birthday is in 2 days."
jp@jpmac~$ curl https://secret.execute-api.eu-west-2.amazonaws.com/hello/bob
{"Item":{"name":{"S":"bob"},"dateOfBirth":{"S":"1980-01-01"}}}
In summary
PUT
curl -X PUT -H "Content-Type: application/json" -d '{"dateOfBirth":"1998-11-30"}' https://secret.execute-api.eu-west-2.amazonaws.com/hello/jones
{}
GET
curl https://secret.execute-api.eu-west-2.amazonaws.com/hello/jones
{"Item":{"name":{"S":"jones"},"dateOfBirth":{"S":"1998-11-30"}}}
More on Gist
Checkout the Gist Link for the full (especially it i will update it) snipped of the lambda function
Public GitHub Gist on how to see this NodeJS Based Lambda glue is actually working is all there:
https://gist.github.com/jpantsjoha/8479ffb137ea873a169fc00e056ed77a
Clap me for this post if you found it useful.
Cheers!
Congratulations @yogibear! You received a personal award!
Click here to view your Board of Honor
Congratulations @yogibear! You received a personal award!
You can view your badges on your Steem Board and compare to others on the Steem Ranking
Vote for @Steemitboard as a witness to get one more award and increased upvotes!