I am new on node js dynamo db I wrote a node js sdk to fetch one row from a table ona dynamodb. It is fetching data correctly but not immediately for this I got error
My code is below a simple code
var AWS = require("aws-sdk");
var config = function(){
AWS.config.update({region: 'us-east-1'});
// Create the DynamoDB service object
var ddb = new AWS.DynamoDB({apiVersion: '2012-08-10'});
var params = {
TableName: 'tblConfigs',
// Key: {
// "id" : {S: "1"},
// }
ExpressionAttributeValues: {
":v1": {
S: "1"
}
},
FilterExpression: "id = :v1",
};
var v;
var json = ddb.scan(params, function(err, data) {
if (err) {
console.log("Error", err);
} else {
v = data;
// console.log(JSON.stringify(data.Item));
// return JSON.stringify(data.Item);
}
});
// if(v=="u")
// for(var i=0;)
v = v.Items[0];
// for()
var con = {
"host": v.endpoint.S,
"user": v.endpoint.username.S,
"password": v.endpoint.password.S,
"database": v.endpoint.database_name.S
};
return con;
}
And I got the below error
> config()
TypeError: Cannot read property 'Items' of undefined
at config (repl:31:7)
as v is undefined so it is giving the error but v is not undefined when I execute the code in node console it first time gave undefined next time it gave value
like below
> v
{ Items:
[ { password: [Object],
stage: [Object],
username: [Object],
id: [Object],
endpoint: [Object],
database_name: [Object] } ],
Count: 1,
ScannedCount: 1 }
how can I fetch the row immediately not after some time? IS there any good way in dynamodb I tried, get, getItem, scan, query all are giving data correctly but not immediately...Please suggest
You are missing one important thing: Javascript execution is asynchronous. As long as you are not using async/await syntax you have to "play" with callbacks like this:
var ddb = new AWS.DynamoDB({ apiVersion: '2012-08-10' });
function loadConfig(callback) {
var params = {
TableName: 'tblConfigs',
ExpressionAttributeValues: {
':v1': {
S: '1'
}
},
FilterExpression: 'id = :v1'
};
ddb.scan(params, function (error, data) {
if (error) {
callback(error);
} else {
var item = data.Items[0];
callback(null, {
'host': item.endpoint.S,
'user': item.endpoint.username.S,
'password': item.endpoint.password.S,
'database': item.endpoint.database_name.S
});
}
});
}
loadConfig(function (error, configuration) {
if (error) {
console.log(error);
} else {
// Your connection logic (JUST AN EXAMPLE!)
var connection = mysql.connect({
host: configuration.host,
user: configuration.user,
password: configuration.password,
database: configuration.database
})
}
});
Btw. storing database configurations in DynamoDB isn't a good solution, i would recommend to check AWS Systems Manager Parameter Store.
Edit
To give you a short example how the async/await syntax looks like
var ddb = new AWS.DynamoDB({ apiVersion: '2012-08-10' });
const loadConfig = async () => {
const { Items } = await ddb.scan({
TableName: 'tblConfigs',
ExpressionAttributeValues: {
':v1': {
S: '1'
}
},
FilterExpression: 'id = :v1'
}).promise();
const item = Items[0];
return {
'host': item.endpoint.S,
'user': item.endpoint.username.S,
'password': item.endpoint.password.S,
'database': item.endpoint.database_name.S
};
};
Related
let AWS = require('aws-sdk');
const getESClient = async () => {
const esEndpoint = 'https://search.<region>.es.amazonaws.com';
let options = {
hosts: [esEndpoint],
connectionClass: require('http-aws-es'),
awsConfig: new AWS.Config({
region: "",
accessKeyId: "",
secretAccessKey: ""
}),
};
let es = require('elasticsearch').Client(options);
es.indices.create({ index: 'kk' }, function (err, resp) {
if (err)
console.log('failed to create ElasticSearch index, ' + err.message);
else
console.log(resp + 'successfully created ElasticSearch index');
});
await es.index({
index: 'kk',
type: 'gsgsgg',
refresh: true,
document: {
character: 'Ned Stark',
quote: 'Winter is coming.'
}
})
return es;
};
getESClient()
I am trying the above code snippet, index is creating successfully but adding documents with the index getting errors below
body: { error: 'no handler found for uri [/kk/gsgsgg] and method
[POST]' },
I have written this function to do update in dynamo table
const updateTask = async (req, res) => {
try {
const { existingTaskText,updatedTaskText } = req.body;
console.log(existingTaskText,updatedTaskText );
UPDATE({
TableName: "todos",
Key:{ task: existingTaskText},
UpdateExpression:"set task = :task",
ExpressionAttributeValues: {":task": updatedTaskText},
});
res.status(200).json({ data: "this is controller" });
} catch (error) {
res.status(400).json({ message: error.message });
}
};
this is calling UPDATE
const UPDATE = async (payload) => {
try {
console.log(payload);
const updateDoc = await dbClient
.update({
TableName: payload.TableName,
Key: payload.Key,
UpdateExpression: payload.UpdateExpression,
ExpressionAttributeNames:payload.ExpressionAttributeNames,
ReturnValues: "UPDATED_NEW",
})
.promise();
console.log(updateDoc);
} catch (error) {
console.log(error);
}
};
When I am testing this in postman, I am getting this error
ValidationException: Invalid UpdateExpression: An expression attribute value used in expression is not defined; attribute value: :task
this is payload log getting passed
{
TableName: 'todos',
Key: { task: 'see its done' },
UpdateExpression: 'set task = :task',
ExpressionAttributeValues: { ':task': 'edited' }
}
I made below common functions for the update, get, and create a table.use the same.
const AWS = require('aws-sdk');
AWS.config.update({ region: "us-east-1",accessKeyId : process.env.AWS_ACCESS_KEY_ID, secretAccessKey: process.env.AWS_SECRET_ACCESS_KEY });
const dynamoDB = new AWS.DynamoDB()
const documentClient = new AWS.DynamoDB.DocumentClient();
const Dynamo = {
async get(id, TableName) {
const params = {
TableName,
Key: {
id,
},
};
const data = await documentClient.get(params).promise();
if (!data || !data.Item) {
throw Error(`There was an error fetching the data for ID of ${id} from ${TableName}`);
}
console.log(data);
return data.Item;
},
async getall(TableName) {
const params = {
TableName: TableName,
};
const data = await documentClient.scan(params).promise();
if (!data || !data.Item) {
throw Error(`There was an error fetching the data for ID of ${ID} from ${TableName}`);
}
console.log(data);
return data.Items;
},
async getMany(params) {
const data = await documentClient.scan(params).promise();
console.log(data);
if (!data || !data.Items) {
throw Error(`There was an error fetching the data`);
}
return data.Items;
},
async write(data, TableName) {
console.log('write dynamo',data, TableName);
if (!data.id) {
throw Error('no ID on the data');
}
const params = {
TableName,
Item: data,
};
const res = await documentClient.put(params).promise();
if (!res) {
throw Error(`There was an error inserting ID of ${data.id} in table ${TableName}`);
}
console.log('res of write dynamo ',res);
return data;
},
async createTable(TableName) {
documentClient
.scan({
TableName: TableName,
})
.promise()
.catch(error => {
return new Promise(resolve => {
dynamoDB
.createTable({
AttributeDefinitions: [
{
AttributeName: "id",
AttributeType: "S",
},
],
KeySchema: [
{
AttributeName: "id",
KeyType: "HASH",
},
],
BillingMode: "PAY_PER_REQUEST",
TableName: TableName,
})
.promise()
.then(data => console.log("Success!", data))
.catch(console.error)
})
});
},
};
module.exports = Dynamo;
When you call the dbClient.update method, you are declaring the parameter ExpressionAttributeNames. It should be ExpressionAttributeValues. This is why the error message indicates that expression attribute value used in expression is not defined.
So you can try it changing the dbClient.update call in this way:
const updateDoc = await dbClient
.update({
TableName: payload.TableName,
Key: payload.Key,
UpdateExpression: payload.UpdateExpression,
ExpressionAttributeValues:payload.ExpressionAttributeValues,
ReturnValues: "UPDATED_NEW",
})
.promise();
Here as you are setting 'ExpressionAttributeNames:', you have to set 'ExpressionAttributeValues' as well.
I'm trying to wrap my head around AWS DynamoDB and having trouble getting records.
If my db item looks like this:
{
"id": string (primary)
"identifier": string
"project": string
}
I'm trying to get a record using both identifier and project:
Right now I'm doing that like this:
const params = {
TableName: 'MY_TABLE',
ExpressionAttributeValues: {
':identifier': { S : 'Dave' },
':project': { S : 'red_squad' },
},
KeyConditionExpression: 'identifier = :identifier and project = :project'
}
docClient
.query(
params,
(err, data) => console.log(err || data)
)
However it's telling me that it needs the primary key, however I that's not suitable for my use case.
Use a scan, not a query
https://docs.aws.amazon.com/AWSJavaScriptSDK/latest/AWS/DynamoDB/DocumentClient.html#scan-property
const params = {
TableName: 'MY_TABLE',
ExpressionAttributeValues: {
':identifier': { S : 'Dave' },
':project': { S : 'red_squad' },
},
FilterExpression: 'identifier = :identifier and project = :project'
}
docClient
.scan(
params,
(err, data) => console.log(err || data)
)
I'm trying to get an item from my DynamoDB but get the following error
ValidationException: The provided key element does not match the
schema
The create item piece of the code works. But no the Get item.
Table Info:
Table Name: movieTable
Primary Partition Key: itemID
Primary Sort Key: sortKey
Here's the code for the create and update:
var fbUserId;
var params;
var keyText;
var attText;
var valText;
var dynamodb = null;
var docClient = null;
var appId = '405140756489952'; //from facebook
var roleArn = 'arn:aws:iam::042765862882:role/Verzosa'; //from AWS IAM
var resultData = null;
document.getElementById('putThis').onclick = function () {
dynamodb = new AWS.DynamoDB({ region: 'us-west-2' });
docClient = new AWS.DynamoDB.DocumentClient({ service: dynamodb });
keyText = document.getElementById("keyValue").value;
attText = document.getElementById("attributeText").value;
valText = document.getElementById("valueText").value;
console.log("Key Value: ", keyText);
console.log("Attribute: ", attText);
console.log("Value: ", valText);
params = {
TableName: 'movieTable',
Item: {
itemID: keyText,
sortKey: valText
}
};
docClient.put(params, function(err, data){
if (err) console.log(err);
else
{
resultData = data;
console.log(resultData);
}
})
};
document.getElementById('getThis').onclick = function () {
dynamodb = new AWS.DynamoDB({ region: 'us-west-2' });
docClient = new AWS.DynamoDB.DocumentClient({ service: dynamodb });
keyText = document.getElementById("keyValue").value;
attText = document.getElementById("attributeText").value;
console.log("Key Value: ", keyText);
console.log("Attribute: ", attText);
params = {
TableName: 'movieTable',
Key: {
itemID: keyText,
},
ProjectionExpression: "#a",
ExpressionAttributeNames: {
'#a': attText
}
};
docClient.get(params, function (err, data)
{
if (err)
{
console.log(err, err.stack);
}
else
{
console.log("success, logging data: ");
console.log(data);//shows keys
console.log("attribute 1 is " + data.Item.sortKey)
//var output = data.Item.attribute1;
l = document.getElementById("output");
l.innerHTML = data.Item.sortKey;
}
})
};
Any help would be appreciated.
You are getting this error because when using AWS.DynamoDB.DocumentClient.get method, you must specify both hash and sort key of an item. But you have only hash key specified (itemId), and sort key is missing.
Here is how your get params should look like:
...
params = {
TableName: 'movieTable',
Key: {
itemID: keyText,
sortKey: valText // <--- sort key added
},
ProjectionExpression: "#a",
ExpressionAttributeNames: {
'#a': attText
}
};
docClient.get(params, function (err, data) {
...
If you'd like to get a record with a hash key only, without specifying its sort key, you should use query method instead of get:
...
params = {
TableName: 'movieTable',
KeyConditionExpression: '#itemID = :itemID',
ProjectionExpression: "#a",
ExpressionAttributeNames: {
'#a': attText,
'#itemID': 'itemID'
},
ExpressionAttributeValues: {
':itemID': keyText
}
};
dynamodbDoc.query(params, function(err, data) {
...
Be aware that while get method always returns 1 or no records, query can possibly return multiple records, so you would have to revisit your current implementation of get callback (e.g. instead of accessing data.Item you should use data.Items array, see query method docs)
You need to pass both primary key and sort key in the params.
You can find these keys from the table UI.
and you should pass those as parameters when making the request
this.ProjectsModel.delete({pk1:"project#giri-test#appsc", sk1:"metadata#giri-test#appsc"}, (error) => {
if (error) {
console.error(error);
} else {
console.log("Successfully deleted item");
}
});
const AWS = require('aws-sdk')
const docClient = new AWS.DynamoDB.DocumentClient({ region: 'eu-central-1' })
const createDocument = (text, callback) => {
const createParams = {
Item: {
text: text
},
TableName: 'ToDoItems'
}
docClient.put(createParams, (err, data) => {
if(err) {
callback(err, null)
} else {
callback(null, data)
}
})
}
exports.handle = (event, context, callback) => {
createDocument(event.text, (err, data) => {
if(err) {
callback(err, null)
} else {
callback(null, data)
}
})
}
That's my AWS Lambda function, the issue is that when I get a callback, data object is empty, even though document is inserted into DynamoDB. What could the issue be here?
You can't. You have to separately query. On put, if you set ReturnValues: 'ALL_NEW', then you'll get "ReturnValues can only be ALL_OLD or NONE"
Note the 'possible' in AWS's documentation:
http://docs.aws.amazon.com/AWSJavaScriptSDK/latest/AWS/DynamoDB/DocumentClient.html#put-property
ReturnValues — (String) Possible values include:
"NONE"
"ALL_OLD"
"UPDATED_OLD"
"ALL_NEW"
"UPDATED_NEW"
Also, instead of separately querying, you can also just use the params value. If it was saved, then what you have in createParams.Item is basically what's returned if you were to separately query.
There is a workaround - You can use update method of DynamoDB.DocumentClient.
TableName: "table",
Key: {
id: randomId
},
AttributeUpdates: {
authorId: {Action: "PUT", Value: event.authorId},
date: {Action: "PUT", Value: event.date},
description: {Action: "PUT", Value: event.description},
title: {Action: "PUT", Value: event.title}
},
ReturnValues: "ALL_NEW"
This method will create new item and return all what you need
You have to request the return values, like this:
const createParams = {
Item: {
text: text
},
TableName: 'ToDoItems',
ReturnValues: 'ALL_NEW'
}
This is documented here.
I did have to implementing that the return in .then() was params.Item, like this:
var params = {
TableName:table,
Item:{
"name": value,
"email": value2,
}
};
console.info("Adding a new item...");
await docClient.put(params)
.promise()
.then(data => {
return params.Item;
}).catch(error => {
console.error(error)
throw new Error(error)
})