AWS SAM and DynamoDB Local - node.js

I want to create a API endpoint in Node.js that lists the table names in DynamoDB. I have created a simple table locally and confirmed the table exists with the command
aws dynamodb list-tables --endpoint-url http://host.docker.internal:8000
{
"TableNames": [
"SampleTable"
]
}
but my lambda returns
{"TableNames":[]}
here's my lambda
const AWS = require('aws-sdk');
const options = {
apiVersion: '2012-08-10',
region: 'us-east-1',
};
if (process.env.AWS_SAM_LOCAL) {
options.endpoint = new AWS.Endpoint('http://host.docker.internal:8000');
}
const ddb = new AWS.DynamoDB(options);
exports.listTablesHandler = async (event) => {
if (event.httpMethod !== 'GET') {
throw new Error(`listTables only accept GET method, you tried: ${event.httpMethod}`);
}
console.info('received:', event);
const params = {};
let response = { statusCode: '500' };
try {
response = await ddb.listTables(params).promise();
} catch (err) {
console.log(err);
}
return response;
};
I expected the Lambda to list the name "SampleTable" in the API response

You should check if you are using -sharedDb ?
If you use the -sharedDb option, DynamoDB creates a single database file named shared-local-instance.db. Every program that connects to DynamoDB accesses this file. If you delete the file, you lose any data that you have stored in it.
If you omit -sharedDb, the database file is named myaccesskeyid_region.db, with the AWS access key ID and AWS Region as they appear in your application configuration. If you delete the file, you lose any data that you have stored in it.

Related

Query DynamoDB to check if a particular item exists or not

I just started out with Node.js and AWS DynamoDB and I am stuck with a very basic problem. I would like to query my DynamoDB which has a "Users" table with "Username"(PKey) and "JoinedOn" columns. Now I would like to write a Lambda function to check if a particular Username exists or not and return a response on the basis of that. Currently I am using the below code:
const AWS = require('aws-sdk');
const docClient = new AWS.DynamoDB.DocumentClient();
const params = {
TableName : 'Users',
Key: {
Username: 'user1'
}
}
async function getItem(){
try {
const data = await docClient.get(params).promise()
return data
} catch (err) {
return err
}
}
exports.handler = async (event, context) => {
try {
const data = await getItem()
return { body: JSON.stringify(data) }
} catch (err) {
return { error: err }
}
}
I am currently hardcoding the key (Username: 'user1') in my code, but I want to avoid doing that as I want to use the same code to check for different users, whether the Username exists in the "Users" table or not.
I want to use SAM. My event.json should look like below, as I want to check if user1 exists in "Users" or not:
{
"Username": "user1"
}
On running "sam local invoke TestFunction -e events/event.json", I should get a response "User exists" or "User does not exist"
I am new to AWS and development. Any help is really appreciated.
Thank you in advance!
you need to define that username in environment variable section of lambda.
then inside the lambda code you can simply call process.env.username.

Node JS queries on Dynamo DB hangs forever using IAM roles

I'm trying to connect my Node JS app to Dynamo DB from a EKS instance using IAM roles.
As you probably know, credentials should be passed automatically to the container in this case. As a prove of that, I tried to install the aws-cli within the same container and it's perfectly working.
However, my Node JS script is hanging forever when I try to execute it and no errors are returned.
I checked few things: proxy, permissions (OS user), etc.. Nothing seemed related to this problem.
const { DynamoDBClient, QueryCommand } = require("#aws-sdk/client-dynamodb");
const ddbClient = new DynamoDBClient({
region: "eu-west-1",
});
// Set the parameters
const params = {
KeyConditionExpression: "itemRef = :itemRef",
ExpressionAttributeValues: {
":itemRef": { S: "ABC" },
},
TableName: "my-sandbox",
};
const run = async () => {
try {
const data = await ddbClient.send(new QueryCommand(params));
data.Items.forEach(function (element, index, array) {
console.log(element);
return data;
});
} catch (err) {
console.error(err);
}
};
run();
Any ideas?

DynamoDB getItem returns empty item

I am using the aws-sdk within Node and trying to get an item from a DynamoDB table but it prints just Success {} and not the actual content of the item. Does anyone know how to get the actual content of the item?
The Node script I am using is the following:
// Load the AWS SDK for Node.js
var AWS = require('aws-sdk');
// Set the region
AWS.config.update({region: 'ap-southeast-2'});
// Create the DynamoDB service object
var ddb = new AWS.DynamoDB({apiVersion: '2012-08-10'});
var params = {
TableName: 'test_table',
Key: {
'TWEET_KEY' : {S: 'Test'}
},
ProjectionExpression: 'ATTRIBUTE_NAME'
};
// Call DynamoDB to read the item from the table
ddb.getItem(params, function(err, data) {
if (err) {
console.log("Error", err);
} else {
console.log("Success", data.Item);
}
});
and the actual data within the DynamoDB test_table which I'm trying to get is as follows:
Thank you very much!
Here - ProjectionExpression: 'ATTRIBUTE_NAME', you need to set the attribute names that you need to get, or just remove it to get the whole record content.

Inconsistent results making API call from AWS Lambda

Let me just apologize for this abysmal code ahead of time. I have almost zero node experience, and write all of my JS with React apps and Elixir on the back end. I am struggling to write a correct Lambda function in NodeJS, and have basically cobbled something together from Googling/SO/trial and error, etc.
What I'm doing is the following:
User wants to upload a file so they send some info to back end.
Back end generates a presigned key.
Front end sends file to S3.
S3 fires event and Lambda executes
Lambda now checks for mimetype and if it's a bad file, will delete the file from the S3 bucket and make a DELETE API call to my backend to tell it to delete the row the photo upload belongs to.
Where I'm struggling is when I make the API call to my backend inside of the s3.deleteObject call, I am getting wildly inconsistent results. A lot of time it's sending two delete requests back to back in the same Lambda execution. Sometimes it's like it never even calls the backend and just runs and shows complete without really logging anything to Cloudwatch.
My code is as follows:
const aws = require('aws-sdk');
const s3 = new aws.S3({apiVersion: '2006-03-01'});
const fileType = require('file-type');
const imageTypes = ['image/gif', 'image/jpeg', 'image/png'];
const request = require('request-promise');
exports.handler = async (event, context) => {
// Get the object from the event and show its content type
const bucket = event.Records[0].s3.bucket.name;
const key = decodeURIComponent(
event.Records[0].s3.object.key.replace(/\+/g, ' ')
);
const params = {
Bucket: bucket,
Key: key,
};
try {
const {Body} = await s3.getObject(params).promise();
const fileBuffer = new Buffer(Body, 'base64');
const fileTypeInfo = fileType(fileBuffer);
if (
typeof fileTypeInfo !== 'undefined' &&
fileTypeInfo &&
imageTypes.includes(fileTypeInfo.mime)
) {
console.log('FILE IS OKAY.');
} else {
await s3
.deleteObject(params, function(err, data) {
console.log('FILE IS NOT AN IMAGE.');
if (err) {
console.log('FAILED TO DELETE.');
} else {
console.log('DELETED ON S3. ATTEMPTING TO DELETE ON SERVER.');
const url =
`http://MYSERVERHERE:4000/api/event/${params.Key.split('.')[0]}`;
const options = {
method: 'DELETE',
uri: url,
};
request(options)
.then(function(response) {
console.log('RESPONSE: ', response);
})
.catch(function(err) {
console.log('ERROR: ', err);
});
}
})
.promise();
}
return Body;
} catch (err) {
const message = `Error getting object ${key} from bucket ${bucket}. Make sure they exist and your bucket is in the same region as this function.`;
console.log(message);
throw new Error(message);
}
};
This has been driving me mad for days. Any help is appreciated to explain why I would be getting unexpected results from a Lambda function like this.
Please check after update your else part with proper await use
Please try below code.
exports.handler = async (event, context) => {
// Get the object from the event and show its content type
const bucket = event.Records[0].s3.bucket.name;
const key = decodeURIComponent(
event.Records[0].s3.object.key.replace(/\+/g, ' ')
);
const params = {
Bucket: bucket,
Key: key,
};
try {
const {Body} = await s3.getObject(params).promise();
const fileBuffer = new Buffer(Body, 'base64');
const fileTypeInfo = fileType(fileBuffer);
if (
typeof fileTypeInfo !== 'undefined' &&
fileTypeInfo &&
imageTypes.includes(fileTypeInfo.mime)
) {
console.log('FILE IS OKAY.');
} else {
await s3.deleteObject(params).promise(); //fail then catch block execute
console.log('DELETED ON S3. ATTEMPTING TO DELETE ON SERVER.');
const url =
`http://MYSERVERHERE:4000/api/event/${params.Key.split('.')[0]}`;
const options = {
method: 'DELETE',
uri: url,
};
let response = await request(options); ////fail then catch block execute
console.log(response);
}
return Body;
} catch (err) {
console.log(err);
const message = `Error getting object ${key} from bucket ${bucket}. Make sure they exist and your bucket is in the same region as this function.`;
console.log(message);
throw new Error(message);
}
};
S3 delete operation is eventual consistent in all regions.
Hence as par AWS (captured relevant info),
A process deletes an existing object and immediately attempts to read it. Until the deletion is fully propagated, Amazon S3 might return the deleted data.
A process deletes an existing object and immediately lists keys within its bucket. Until the deletion is fully propagated, Amazon S3 might list the deleted object.
Ref: https://docs.aws.amazon.com/AmazonS3/latest/dev/Introduction.html#ConsistencyModel

Trying to use AWS lambda to send data to a dynamodb table

console.log('starting function');
var dynamodb = new AWS.DynamoDB();
var AWS = require('aws-sdk');
exports.handler = function (e, ctx, callback) {
var params = {
Item: {
"Name": {
S: "Dalton Warden"
},
"PhoneNumber": {
S: "796-353-1416",
}
},
ReturnConsumedCapacity: "TOTAL",
TableName: "CustomerInfo"
};
dynamodb.putItem(params, function (err, data) {
if (err) console.log(err, err.stack); // an error occurred
else console.log(data); // successful response
});
};
I'm pretty new to lambda and dynamoDB but i'm trying to send data to a table I have set up. I've been through amazon's documentation and looked for similar instances on this site and The formatting looks like it would return the correct JSON but I'm still having trouble. The error I'm getting is Cannot read property 'DynamoDB' of undefined".
Please change the order of the below statements as mentioned below (first AWS and then dynamodb).
var AWS = require('aws-sdk');
var dynamodb = new AWS.DynamoDB();

Resources