Dynamodb batchWrite doesn't work in Lambda with Async - node.js

batchWrite doesn't work with async in Laqmbda. The code is going to insert one record tho, it can't. However, when I remove async, It works.
const AWS = require("aws-sdk");
const documentClient = new AWS.DynamoDB.DocumentClient();
AWS.config.update({ region: "us-west-2" });
const tableName = "BlrSession-56pfbzohnvdqpac6asb627z2wu-dev";
exports.handler = async (event, context, callback) => {
try {
let games = [];
games.push({
PutRequest: {
Item: {
id: Math.random().toString(36).substring(2) + Date.now().toString(36),
},
},
});
let params = {
RequestItems: {
[tableName]: games,
},
};
documentClient.batchWrite(params, function (err, data) {
if (err) {
callback(err);
} else {
callback(null, data);
}
});
} catch (err) {
return err;
}
};
The result is below. There is no error.
Ensuring latest function changes are built...
Starting execution...
Result:
null
Finished execution.
Have you guys got the same behavior?

You can't combine the callback method with the async/await method. The easiest thing to do here is to make it all async/await (and don't forget the .promise() on the call).
const AWS = require("aws-sdk");
const documentClient = new AWS.DynamoDB.DocumentClient();
AWS.config.update({ region: "us-west-2" });
const tableName = "BlrSession-56pfbzohnvdqpac6asb627z2wu-dev";
exports.handler = async (event, context, callback) => {
try {
let games = [];
games.push({
PutRequest: {
Item: {
id: Math.random().toString(36).substring(2) + Date.now().toString(36),
},
},
});
let params = {
RequestItems: {
[tableName]: games,
},
};
return await documentClient.batchWrite(params).promise();
} catch (err) {
return err;
}
};

Related

DynamoDB Scan with Lambda not returning elements

I have a Lambda with the following code:
// Load the AWS SDK for Node.js.
var AWS = require("aws-sdk");
// Set the AWS Region.
AWS.config.update({ region: "us-east-2" });
exports.handler = function(event, context, callback) {
// Create DynamoDB service object.
var ddb = new AWS.DynamoDB({ apiVersion: "2012-08-10" });
var params = {
TableName: "Ranking",
ProjectionExpression: "#username, Score, Duration",
FilterExpression: "#username = :username",
ExpressionAttributeNames: {
"#username": "Username",
},
ExpressionAttributeValues: {
":username": {
S: 'Alberto'
},
}
};
let toReturn = [];
ddb.scan(params, function (err, data) {
if (err) {
toReturn = err
} else {
toReturn = data.Items;
}
});
let response = {
statusCode: 200,
body: JSON.stringify(toReturn)
};
callback(null, response)
};
However I always see [] as response...
My current DB has the following records:
So my question is... why I don't get back that item?
Since you are using callbacks, your code should be as below. Also Duration is reserved keyword, so it also needs to be modified as below:
// Load the AWS SDK for Node.js.
var AWS = require("aws-sdk");
// Set the AWS Region.
AWS.config.update({ region: "us-east-2" });
exports.handler = function(event, context, callback) {
// Create DynamoDB service object.
var ddb = new AWS.DynamoDB({ apiVersion: "2012-08-10" });
var params = {
TableName: "Ranking",
FilterExpression: "#username = :username",
ProjectionExpression: "#username, Score, #duration",
ExpressionAttributeNames: {
"#username": "Username",
"#duration": "Duration",
},
ExpressionAttributeValues: {
":username": {
S: 'Alberto'
},
}
};
ddb.scan(params, function (err, data) {
if (err) {
callback(null, err)
} else {
callback(null, data.Items)
}
});
};

DynamoDB always returns "Response: null" on AWS Lambda

Consider the code :
const AWS = require('aws-sdk');
const dynamoDB = new AWS.DynamoDB({
region : 'us-east-2' ,
apiVersion: '2012-08-10'
});
exports.handler = async (event , context , callback) => {
const type = event.type;
if (type === 'all') {
// Which table we want to scan
const params = {
TableName : 'compare-yourself'
};
await dynamoDB.scan(params ,(err , data) => {
if (err) {
callback(err);
}
else{
callback(null , data);
}
});
// callback(null , "All Data");
}
else if (type === 'single') {
callback(null , "Single data");
}
else{
callback(null , "Everything else...");
}
};
When I test this piece of code on AWS Lambda with the value :
The result is always "Response: null" , even through there is data in the DynamoDB table compare-yourself.
What might be the problem here ?
Promise will solve the issue. I prefer documentclient.
const AWS = require('aws-sdk');
const documentClient = new AWS.DynamoDB.DocumentClient();
exports.handler = async (event, context, callback) => {
const type = event.type;
if (type === 'all') {
const params = {
TableName: 'compare-yourself'
};
let data = await documentClient.scan(params).promise();
callback(null, data);
} else if (type === 'single') {
callback(null, "Single data");
} else {
callback(null, "Everything else...");
}
};
The data will be an object with table values in the Items,
{
Items: [],
Count:
ScannedCount:
.....
}
try use DocumentClient();
var documentClient = new AWS.DynamoDB.DocumentClient();
const params = {
TableName : 'compare-yourself'
};
documentClient.scan(params, function(err, data) {
if (err) console.log(err);
else console.log(data);
});
Mistake:
The problem is you are doing both callback and await at the same.
Fix:
So you could put the scan function in a try block, so on error
we do callback error in the catch block and stop the function without proceeding further (return) else you will send the data.
Code:
const AWS = require('aws-sdk');
const dynamoDB = new AWS.DynamoDB({
region : 'us-east-2' ,
apiVersion: '2012-08-10'
});
exports.handler = async (event , context , callback) => {
const type = event.type;
if (type === 'all') {
// Which table we want to scan
const params = {
TableName : 'compare-yourself'
};
try {
await dynamoDB.scan(params).promise();
} catch (e) {
callback(e);
return;
}
callback(null , data);
}
else if (type === 'single') {
callback(null , "Single data");
}
else{
callback(null , "Everything else...");
}
};
Note:
Just wanted to add this, when performing scan be aware of the limits, if the limit exceeds you won't get the entire entries in the table.

node.js module answer undefined [duplicate]

This question already has answers here:
How do I return the response from an asynchronous call?
(41 answers)
Closed 4 years ago.
Can someone give me a hand? I tried to figure it out but i ran out of ideas.
-------------dynamo.js....
module.exports.readUser = function (user_id) {
AWS.config = new AWS.Config();
AWS.config.update({region: "eu-west-1"});
var docClient = new AWS.DynamoDB.DocumentClient();
var table = "user";
var user_id = user_id;
var params = {
TableName: table,
Key:{
"user_id": user_id
}
};
docClient.get(params, function(err, data) {
if (err) {
return err;
} else {
//console.log(data); <-- data is filled
return data;
}
});
var dynamo = require("./dynamo.js");
console.log(dynamo.readUser(4711998));
The data you log out inside the function is only the returned value for the anonymous function given to docClient.get as the second argument. The exported readUser function has no return statement and therefore it is undefined.
Because of the async nature of javascript you have to access the result via a callback like so:
module.exports.readUser = function (user_id, cb) {
...
docClient.get(params, function(err, data) {
if (err) {
return err;
} else {
return cb(data);
}
});
}
var dynamo = require("./dynamo.js");
dynamo.readUser(4711998, function (user) {
console.log(user);
});
docClient.get is an asynchronous function. You need promisify this function or use callback.
Use callback:
module.exports.readUser = function (user_id, cb) {
AWS.config = new AWS.Config();
AWS.config.update({ region: "eu-west-1" });
var docClient = new AWS.DynamoDB.DocumentClient();
var table = "user";
var user_id = user_id;
var params = {
TableName: table,
Key: {
"user_id": user_id
}
};
docClient.get(params, cb);
}
var dynamo = require("./dynamo.js");
dynamo.readUser(4711998, function (err, user) {
if(err)
console.error(err);
else
console.log(user);
});
Use promise:
module.exports.readUser = function (user_id) {
AWS.config = new AWS.Config();
AWS.config.update({ region: "eu-west-1" });
var docClient = new AWS.DynamoDB.DocumentClient();
var table = "user";
var user_id = user_id;
var params = {
TableName: table,
Key: {
"user_id": user_id
}
};
return new Promise(function(resolve, reject) {
docClient.get(params, function(err, data) {
if (err) {
reject(err);
} else {
resolve(data);
}
});
});
}
var dynamo = require("./dynamo.js");
dynamo.readUser(4711998).then(function (user) {
console.log(user);
}).catch(function(err) {
console.error(err);
});
Read more:
How do I promisify the AWS JavaScript SDK?
https://aws.amazon.com/ru/blogs/developer/support-for-promises-in-the-sdk/

Delete all items in Dynamodb using Lambda?

Using Lambda (node.js) - how to delete all the items in the Dynamodb table?
There are 500K rows in the table
I have tried using scan method and then loop through each item and then using delete method. It only allow up to 3000 rows only.
Code
exports.handler = function(context, callback) {
getRecords().then((data) => {
data.Items.forEach(function(item) {
deleteItem(item.Id).then((data1) => {
});
});
});
};
var deleteItem = function(id) {
var params = {
TableName: "TableName",
Key: {
"Id": id
},
};
return new Promise(function(resolve, reject) {
client.delete(params, function(err, data) {
if (err) {
reject(err);
} else {
resolve();
}
});
});
}
function getRecords() {
var params = {
TableName: 'TableName',
IndexName: 'Type-index',
KeyConditionExpression: 'Type = :ty',
ExpressionAttributeValues: {
':ty': "1"
},
ProjectionExpression: "Id",
};
return new Promise(function(resolve, reject) {
client.query(params, function(err, data) {
if (err) {
reject(err);
} else {
resolve(data);
}
});
});
}
There is already one right answer, but here is another code snippet to delete all records from Dynamo DB.
const AWS = require("aws-sdk");
AWS.config.update({
region: "us-east-1",
});
const docClient = new AWS.DynamoDB.DocumentClient();
const getAllRecords = async (table) => {
let params = {
TableName: table,
};
let items = [];
let data = await docClient.scan(params).promise();
items = [...items, ...data.Items];
while (typeof data.LastEvaluatedKey != "undefined") {
params.ExclusiveStartKey = data.LastEvaluatedKey;
data = await docClient.scan(params).promise();
items = [...items, ...data.Items];
}
return items;
};
const deleteItem = (table, id) => {
var params = {
TableName: table,
Key: {
id: id,
},
};
return new Promise(function (resolve, reject) {
docClient.delete(params, function (err, data) {
if (err) {
console.log("Error Deleting ", id,err);
reject(err);
} else {
console.log("Success Deleting ", id,err);
resolve();
}
});
});
};
exports.handler = async function (event, context, callback) {
try {
const tableName = "<table>";
// scan and get all items
const allRecords = await getAllRecords(tableName);
// delete one by one
for (const item of allRecords) {
await deleteItem(tableName, item.id);
}
callback(null, {
msg: "All records are deleted.",
});
} catch (e) {
callback(null, JSON.stringify(e, null, 2));
}
};
A Scan operation consumes Read capacity. Each Read returns up to 4 kb of data. When this limit is reached, the Scan returns only what it has found until there. If you need more, you need to issue another Scan request.
This, you'll need two loops: 1) loop to delete all records returned at each Scan; 2) loop to keep scanning multiple times, until you reach the end of the table
Make sure you use consistent Reads or wait 1 or 2 second(s) before issuing another Scan, otherwise you may get repeated items in different Scans.
exports.handler = function(context, callback) {
clearRecords();
};
clearRecords = function() {
getRecords().then((data) => {
data.Items.forEach(function(item) {
deleteItem(item.Id).then((data1) => {});
});
clearRecords(); // Will call the same function over and over
});
}
Observe that Lambda has a timeout limit of 15 minutes. Since you have 500K items in your table, it's likely that your Lambda will timeout and you'll need to trigger it more than once. You could also make your Lambda call itself after 14:50, for example, just take a look at the AWS SDK documentation for triggering Lambda functions. For this matter, you might also want to check the getRemainingTimeInMillis() method from the context object.

Unable to access dynamodb from nodejs code through lambda

I have been trying to test this from lambda but unable to proceed.
var Alexa = require("alexa-sdk");
var dynamoDBConfiguration = {
"accessKeyId": "useraccess",
"secretAccessKey": "usersecretkey",
"region": "us-east-1"
};
var AWS = require("aws-sdk");
AWS.config.update(dynamoDBConfiguration);
var promisify = require("es6-promisify");
var dynClient = new AWS.DynamoDB.DocumentClient({"region":"us-east-
1"});
//convert callback style functions to promises
const dbGet = promisify(dynClient.get,dynClient);
const dbPut = promisify(dynClient.put,dynClient);
const dbDelete = promisify(dynClient.delete,dynClient);
var startStateHandlers = (GAME_STATES.STARt, {
"StartGame": function (isNewGame,context) {
const dynamoParams = {
TableName: "Userdata",
Key: {
"UserId":"test"
}
}
dbGet(dynamoParams)
.then(data => {
console.log('Get user succeeded', data);
const userId = data.UserId;
if (userId != null) {
console.log(data.UserName);
}
else {
// no match, add the user
return dbPut(dynamoParams);
}
})
.then(data => {
console.log('Add user succeeded', data);
})
.catch(err => {
console.error(err);
});
this.emit(":tell", speechOutput, speechOutput);
}
});
var handlers = {
"LaunchRequest": function () {
var speechOutput = "hello";
this.emit(":tell", speechOutput, speechOutput);
}
};
var handler = (function () {
function handler(event, context, callback) {
var alexa = Alexa.handler(event, context);
alexa.appId = "appid";
alexa.registerHandlers(handlers, startStateHandlers);
alexa.execute();
}
return handler;
})();
exports.handler = handler;
It doesnt throw any error in the lamda logs and not able to see any log in Cloudwatch. It just executes the other lines of code.
I have attached all permission policy to the user and setup the lamda function as lambda_dynamo.
Unable to understand what is the issue with it.
Appreciate your help.
var dynamoDBConfiguration = {
"accessKeyId": "useraccess",
"secretAccessKey": "usersecretkey",
"region": "us-east-1"
};
var AWS = require("aws-sdk");
// update the AWS.Config global configuration object
AWS.config.update(dynamoDBConfiguration)
// Create DynamoDB document client
var dynClient = new AWS.DynamoDB.DocumentClient()

Resources