AWS Lambda function write to DynamoDB - amazon-web-services

I'm a beginner with Amazon Web Services and NodeJS.
I wrote a Lambda function, triggered by AWS IoT, that parse a JSON.
enter code here
use strict';
console.log('Loading function');
exports.handler = (event, context, callback) => {
console.log('Received event:', JSON.stringify(event, null, 2));
console.log('Id =', event.Id);
console.log('Ut =', event.Ut);
console.log('Temp =', event.Temp);
console.log('Rh =', event.Rh);
//callback(null, event.key1); // Echo back the first key value
//callback('Something went wrong');
};
Now I want to store the json fields into a DynamoDB table.
Any suggestion?
Thanks a lot!

Preliminary Steps:-
Create an IAM Lambda role with access to dynamodb
Launch Lambda in the same region as your dynamodb region
Create the DynamoDB table with correct key attributes defined
Sample code:-
The below code snippet is a sample code to give you some idea on how to put the item. Please note that it has to be slightly altered for your requirement (with table name and key attributes). It is not fully tested code.
use strict';
console.log('Loading function');
var AWS = require('aws-sdk');
var dynamodb = new AWS.DynamoDB({apiVersion: '2012-08-10'});
exports.handler = (event, context, callback) => {
console.log(JSON.stringify(event, null, ' '));
var tableName = "yourtablename";
dynamodb.putItem({
"TableName": tableName,
"Item" : {
"Id": event.Id,
"Ut": event.Ut,
"Temp": event.Temp,
"Rh":event.Rh
}
}, function(err, data) {
if (err) {
console.log('Error putting item into dynamodb failed: '+err);
context.done('error');
}
else {
console.log('great success: '+JSON.stringify(data, null, ' '));
context.done('Done');
}
});
};
Note:-
No need to mention the data type explicitly for String and Number as long as the data type is in compliance with JavaScript String and Number. The DynamoDB will automatically interpret the data type for String and Number.

There is also a shorter version for this with async/await:
'use strict';
const AWS = require('aws-sdk');
const dynamodb = new AWS.DynamoDB({apiVersion: '2012-08-10'});
exports.handler = async (event) => {
const tableName = "yourtablename";
try {
await dynamodb.putItem({
"TableName": tableName,
"Item" : {
"Id": event.Id,
"Ut": event.Ut,
"Temp": event.Temp,
"Rh":event.Rh
}
}).promise();
} catch (error) {
throw new Error(`Error in dynamoDB: ${JSON.stringify(error)}`);
}
};

Related

DynamoDB : Query always returning null

I am quite new on DynamoDB, I just create a table where I inserted some dummy rows just to test the read / write functions.
Here is my data.
I create a Lambda to access these data, via Partition Key ok Sort Key but I ended up with a null return every time :'(
Here is the code of my lambda :
const dynamodb = new AWS.DynamoDB.DocumentClient({apiVersion: '2012-08-10', region: 'eu-west-1'});
exports.handler = async (event,context,callback) => {
var params = {
TableName : "DontMissAPlaceTable",
KeyConditionExpression: "Partition_Key = :id",
ExpressionAttributeValues: {
":id": {"S" : "media_001"}
}
};
dynamodb.query(params, function(err, data) {
if (err) {
console.log(err, err.stack);
callback(null, {
statusCode: '500',
body: err
});
} else {
console.log(data)
callback(null, {
statusCode: '200',
body: data
});
}
})
};
I first thought it was lack of access to the table but it seems I have the right permissions.
I am quite sure it's a dummy problem but i can't figure it out ...
Thanks for help :D
Your Lambda function is async but your code uses callbacks. By the time the callback is reached, your function has already been terminated because it ran asychronously.
Rather than using the outdated, confusing callback approach, just stick with async/await. The AWS SDK for Node.js already provides a .promise() method which you can call on asynchronous methods that use a callback, making it seamless to await on them.
That said, change your code to:
const dynamodb = new AWS.DynamoDB.DocumentClient({apiVersion: '2012-08-10', region: 'eu-west-1'});
exports.handler = async (event) => {
const params = {
TableName : "DontMissAPlaceTable",
KeyConditionExpression: "Partition_Key = :id",
ExpressionAttributeValues: {
":id": "media_001"
}
};
return {
statusCode: 200,
body: JSON.stringify(await dynamodb.query(params).promise())
}
};
Since you are already using the DocumentClient API, you don't need to specify the types ("S", "N", etc) when querying.
Also, make sure that Partition_Key really is your partition key. You use the name you defined in your table, no HashKey nor PartitionKey, meaning if you called your Hash Key id, then id should be used.

Failing to write to DynamoDB using Lambda function with an S3 trigger

I'm trying to write a lambda function that is triggered whenever a new image is written to an S3 bucket. The trigger is already setup using the correct S3 bucket so I know that's not the issue.
The lambda function itself has the roles s3:GetObject and dynamodb.* (which should be full access for DynamoDB writes).
The goal here is to simply write to a table that I've already created named 'art' and insert a primary key value (imageTitle) which i'm trying to obtain in var imageName. Then I want to assign to that key an attribute which is the url of that image, which I'm storing in var url.
This is just a simple exercise I'm trying to get down so that I can move on to more complex DB writes. But as of right now, I'm not getting anything written to the art table, even though I am adding new objects to the S3 bucket which sets off the trigger. Is it possible that the lambda function isn't deployed? I wrote it directly in the inline editor of the Lambda Console and saved it.
Here's the code:
const AWS = require('aws-sdk');
const docClient = new AWS.DynamoDB.DocumentClient({region: 'us-east-1'});
const s3 = new AWS.S3();
exports.handler = async (event, context, callback) => {
//var sourceBucket = event.Records[0].s3.bucket.name;
var sourceKey = event.Records[0].s3.object.key;
var imageName = sourceKey.stringify;
//generate imageURL
var url = "https://s3.amazonaws.com/myapp-20181030214040-deployment/public/" + imageName;
var params = {
TableName : 'art',
Item: {
imageTitle: imageName,
imageURL: url
}
};
docClient.put(params, function(err, data) {
if (err) console.log(err);
else console.log(data);
});
};
The problem here is that you're using an async lambda but returning nothing that is awaitable. This means that your lambda is terminating before the docClient.put operation is sent.
With an async handler you need to await and return, for example you could change this snippet to:
const data = await docClient.put(params).promise();
return data;
Or instead you could use the callback approach (note the signature of the handler does not contain async anymore):
exports.handler = (event, context, callback) => {
// ... the rest of the code as was ...
docClient.put(params, function(err, data) {
if (err) {
console.log(err);
callback(err); // return 'lambda invoke failed because of the error' - will cause s3 to retry three times.
} else {
console.log(data);
callback(null, data); // return 'nothing failed'; n.b. the s3 trigger ignores whatever you return.
}
});
};

DynamoDB query using AWS Lambda

I am trying to select an entry from my database with id "1" (primary key) I am not sure what I am doing wrong, I also tried .getItem() method however with no positive result.
'use strict';
var AWS = require('aws-sdk'),
documentClient = new AWS.DynamoDB.DocumentClient();
exports.selectStatus = function(event, context, callback){
documentClient.query({TableName : "users", Key:{"id":"1"}}, function(err, data){
if(err){
callback(err, null);
}else{
callback(null, data.Items);
}
});
}
I am trying to build serverless REST API using AWS lambda, .scan() works perfectly but I want to filter the data. I have 2 values for each user in database, id(primary key(string)) and status (string).
FIXED UPDATE:
'use strict';
var AWS = require('aws-sdk'),
documentClient = new AWS.DynamoDB.DocumentClient();
exports.selectStatus = function(event, context, callback){
var params = {
TableName : "users",
KeyConditionExpression: "id = :id",
ExpressionAttributeValues: {
":id": "1"
}
};
documentClient.query(params, function(err, data){
if(err){
callback(err, null);
}else{
callback(null, data.Items);
}
});
}

How to get the attributes of an added item in DynamoDB using Lambda function

I have an AWS Lambda function which is being triggered by AWS DynamoDB. The function checks if the operation in the DB is INSERT, and if it does, then it sends a new push message using AWS SNS:
console.log('Loading function');
var AWS = require('aws-sdk');
AWS.config.region = 'us-east-1';
exports.handler = function(event, context, callback) {
console.log("\n\nLoading handler\n\n");
var sns = new AWS.SNS();
console.log(JSON.stringify(event, null, 2));
event.Records.forEach(function(record) {
if (record.eventName == "INSERT") {
sns.publish({
Subject: 'Test',
Message: 'Test publish to SNS from Lambda',
TopicArn: 'TOPIC-ARN'
}, function(err, data) {
if (err) {
console.log(err.stack);
return;
}
console.log('push sent');
console.log(data);
context.done(null, 'Function Finished!');
});
}
});
callback(null, "message");
};
Is there a way that I could get the attributes of the newly added item?
var id = record.dynamodb.Keys.Id.N;

Amazon Lambda to SNS notifications not working

I have a use case of pushing SNS notifications from Amazon lambda. I have written the following code, with the IAM role having the permission to invoke SNS. Even with the Kenesis trigger, this Lambda function is unable to send any notification to SNS. I confirmed this by, subscribing my email id to the SNS.
[EDIT]: Just a follow up question. I now need to query DynamoDB and based on the output, need to call different end point of SNS. Now, when I query DynamoDB, the execution stops after DynamoDB query and not even progressing for SNS checks.
Following is my lambda function code.
console.log('Loading function');
var AWS = require('aws-sdk');
exports.handler = function(event, context) {
event.Records.forEach(function(record) {
//var payload = new Buffer(record.kinesis.data, 'base64').toString('ascii');
var payload = record.kinesis.data;
console.log('Decoded payload:', payload);
var dynamodb = new AWS.DynamoDB();
var dynamodb_params = {
Key: {
dataSource: {
S: payload
}
},
TableName: 'TableName',
AttributesToGet: [
'attribute' // ...
],
ConsistentRead: false,
};
var sns_endpoint = null;
dynamodb.getItem(dynamodb_params, function(err, data) {
if (err) {
console.log(err.stack);
context.done(err, 'Errors while querying dynamodb!');
} else {
console.log(data);
sns_endpoint = data.Item.sns.S;
console.log("Result= " + data);
console.log("Item= " + data.Item);
console.log("sns= " + data.Item.sns);
console.log("value= " + data.Item.sns.S);
console.log("sns_endpoint= " + sns_endpoint);
context.done(null, 'Querying dynamodb succeeded!');
}
});
if( sns_endpoint != null ) {
console.log("sns_endpoint= " + sns_endpoint);
var sns_params = {
Message: payload,
Subject: 'Event Notification From Lambda',
TopicArn: sns_endpoint
};
var sns = new AWS.SNS();
sns.publish(sns_params, function(err, data) {
if (err) {
console.log(err.stack);
context.done(err, 'Errors while putting to SNS!');
} else {
console.log(data);
context.done(null, 'Putting to SNS succeeded!');
}
});
}
});
};
You are calling an asynchronous function, sns.publish(), within a forEach loop. Then you are immediately calling context.succeed(). As soon as context.succeed, context.fail or context.done are called, your Lambda function will exit. You need to modify your code to only call one of those after all asynchronous function calls have completed.