Lambda always returns 200 - node.js

When I provide my Lambda function with an invalid primary key for a GetItem call, it seems to search for that key until it times out, but then it still just returns a 200 (without a response body).
Is there any way to make sure the function aborts once it has gone through the table once without finding the key and returning an error message instead? Seems like a waste of function time to have it look over and over again until it times out? Furthermore, it times out after 1000ms which is not my function timeout setting which makes me think that there is something else going on here than a regular timeout.
Code:
'use strict';
const AWS = require('aws-sdk');
const docClient = new AWS.DynamoDB.DocumentClient();
exports.handler = (event, context, callback) => {
const done = (err, res) => {
const response = {
statusCode: err ? '400' : '200',
body: err ? JSON.stringify(err) : JSON.stringify(res)
}
callback(null, response);
};
const groupId = event.pathParameters.groupId;
const eventId = event.pathParameters.eventId;
docClient.get({
TableName: 'events',
Key: {
groupId,
eventId
}
},
(err, data) => {
done(err, data.Item);
});
};

In a perfect world, I would like for the function to stop once it has "realized" that I have supplied an incorrect primary key and then return some error that would tell the client that it could not find an item in the table with that primary key, yes!
This can only happen if we let the DB operation do it! SO we have to be dependent on the response from the DB.
Here's what we can do: Based on response return the error whether 200 or 400!
To check if the res isEmpty method of Lodash and return 400 using your done function.
'use strict';
const AWS = require('aws-sdk');
const docClient = new AWS.DynamoDB.DocumentClient();
const { isEmpty } = require('lodash');
exports.handler = (event, context, callback) => {
const done = (err, res) => {
const response = {
statusCode: (err || isEmpty(res)) ? '400' : '200',
body: err ? JSON.stringify(err) : JSON.stringify(res)
}
callback(null, response);
};
const groupId = event.pathParameters.groupId;
const eventId = event.pathParameters.eventId;
docClient.get({
TableName: 'events',
Key: {
groupId,
eventId
}
},
(err, data) => {
done(err, data.Item);
});
};
Hope this solves your query!

Related

Inconsistent DynamoDB writtings

We have the following code used as lambda Function in Serverless Framework triggered every 2min with cron. The issue we are facing is that the writing in DynamoDB is inconsistent , we want to have 3 writings but instead we receive 1 or 2 writings every 2 minutes.
DynamoDB has a HASH key the HOUR and SORT KEY the DATE and Billing mode: PROVISIONED. Has someone faced the same behavior from DynamoDB or the same issue to share how he sovled it. Thanks
"use strict";
const AWS = require("aws-sdk");
const axios = require("axios");
const dynamoDb = new AWS.DynamoDB.DocumentClient();
const lambda = new AWS.Lambda({
region: "us-east-1",
});
module.exports.getWeather = async (event, context, callback) => {
const openWeatherMapAPIURL = `http://api.openweathermap.org/data/2.5/weather?id=${event}&appid=XXXXXXXXXXXXXXXXXXXXXXX&units=metric`;
const currentWeather = await axios
.get(openWeatherMapAPIURL)
.then((records) => {
console.log(records);
const d = new Date(records.headers.date);
let hour = d.getHours();
const params = {
TableName: process.env.DYNAMODB_TABLE_NAME,
Item: {
hour: hour,
date: records.headers.date,
city: records.data.name,
temp: records.data.main.temp,
feelsLike: records.data.main.feels_like,
description: records.data.weather[0].description,
},
};
setTimeout(function () {
dynamoDb.put(params, (error) => {
// handle potential errors
console.log(`zapis na: ${records.data.name} ${records.headers.date}`);
if (error) {
console.log(error);
console.error(error);
return;
}
});
}, 3000);
})
.catch((error) => {
console.log(error);
return;
});
const response = {
statusCode: 200,
body: JSON.stringify({
message: `Weather from ${event} was requested!`,
}),
};
callback(null, response);
};
module.exports.cron_launcher = (event, context, callback) => {
const requestedID = ["786735", "792578", "785842"];
requestedID.forEach((requestedID) => {
const params = {
FunctionName: process.env.HANDLER_LOCATION + "-getWeather",
InvocationType: "RequestResponse",
Payload: JSON.stringify(requestedID),
};
return lambda.invoke(params, function (error, data) {
if (error) {
console.error(JSON.stringify(error));
return new Error(`Error printing messages: ${JSON.stringify(error)}`);
} else if (data) {
console.log(data);
}
});
});
};
You are not waiting for the dynamodb.put operation to finish. Additionally, you are wrapping the call in a setTimeout. Your lambda function is returning before the network operation can be made. Make sure the put operation succeeds before returning a result from your lambda.
I see no reason for you to use a setTimeout here.
You can call dynamodb.put(...).promise() to get a promise from the dynamodb SDK and await that promise.
2.a Or you can continue using a callback, but wrap the entire section of code in a new promise object, calling the resolve method after the dynamodb.put call finishes.

Listing cognito userpool users on AWS lambda

I'm trying to list all of my cognito users in my lambda function, however i get nothing in the return as if the callback not getting executed. What am I doing wrong?
The output of the code below just gives me a hello in the console.
var AWS = require("aws-sdk");
const cognitoidentityserviceprovider = new AWS.CognitoIdentityServiceProvider();
export async function main() {
console.log("hello")
var params = {
UserPoolId: "myuserpoolid",
AttributesToGet: ["username"]
};
cognitoidentityserviceprovider.listUsers(params, (err, data) => {
if (err) {
console.log(err, err.stack);
return err;
} else {
console.log(data);
return data;
}
});
}
First of all, the structure of the code is wrong. The header of Lambda function should have a certain structure, either using async function or non-async function. Since you are using non-async code in your example I will show you how to do the later.
var AWS = require("aws-sdk");
const cognitoidentityserviceprovider = new AWS.CognitoIdentityServiceProvider();
exports.handler = function(event, context, callback) {
console.log("hello")
var params = {
UserPoolId: "myuserpoolid",
AttributesToGet: ["username"]
};
cognitoidentityserviceprovider.listUsers(params, (err, data) => {
if (err) {
console.log(err, err.stack);
callback(err) // here is the error return
} else {
console.log(data);
callback(null, data) // here is the success return
}
});
}
In this case, Lambda will finish only when callback is called (or when it times out).
Similarly, you can use async function but you will need to restructure your code accordingly. Here is an example taken from official docs. Note how the promise wrapper is used.
const https = require('https')
let url = "https://docs.aws.amazon.com/lambda/latest/dg/welcome.html"
exports.handler = async function(event) {
const promise = new Promise(function(resolve, reject) {
https.get(url, (res) => {
resolve(res.statusCode)
}).on('error', (e) => {
reject(Error(e))
})
})
return promise
}
For AttributesToGet, don't use username because it is one of the fields that always gets returned. The following are members of the Attributes array, and can be used in the AttributesToGet field:
sub, email_verified, phone_number_verified, phone_number, email.
e.g.
AttributesToGet: ["email","email_verified"]

API works randomly while putting data into dynamoDB

I'm trying to put data into Dynamodb using serverless deployment. I have added the permission to write to Dynamodb.
The api is always sending {"message": "Internal server error"} but is able to put the data into db once if tried 5,6 times.
Following is the serverless.yaml config
handler: dynamoUpdate.handler
events:
- http:
path: /createdbentry
method: get
cors: true
Following is the code:
const AWS = require('aws-sdk')
AWS.config.update({ region: process.env.REGION || 'us-east-1' })
var ddb = new AWS.DynamoDB({apiVersion: '2012-08-10'});
exports.handler = async (event) => {
return await createDBEntry("i123","Working");
}
const sendRes = (status, body) => {
var response = {
statusCode: status,
headers: {
"Content-Type": "application/json"
},
body: body
};
return response;
};
const createDBEntry = async function(id,result) {
var params = {
TableName: 'emplist',
Item: {
'ID' : {S:id},
'Summary':{S: result},
}
};
ddb.putItem(params, function(err, data) {
console.log("Here comes me.")
if (err) {
console.log("Opps Error");
return sendRes(403,err);
} else {
console.log("Complete")
return sendRes(200,data);
}
});
}
How can it be resolved ?
The problem is you are not returning any promise or awaiting anything async in your function called createDBEntry. Thus your handler returns undefined which makes apigateway return 500 internal server error.
You are mixing callbacks and async/await.
Your createDBEntry function should look like this.
const createDBEntry = async function(id, result) {
var params = {
TableName: 'emplist',
Item: {
'ID' : {S:id},
'Summary':{S: result},
}
};
try {
let data = await ddb.putItem(params).promise()
return sendRes(200, JSON.stringify(data))
} catch (err) {
console.log("Oops Error");
return sendRes(403, err.toString());
}
}
When you return from an async handler in lambda the current execution environment is frozen immediately (unlike when you use a non-async handler with a callback). This is why most of the time the writes to the database does not succeed.

Query S3 json file in AWS

I have json file uploaded to s3
then I wrote the following code to Query this file
const aws = require('aws-sdk');
const s3 = new aws.S3();
const bucket = 'hotels.mserver.online';
const objectKey = 'hotelsrates.json';
exports.handler = (event,context,callback) => {
// TODO implement
const response = getS3Objects(bucket,objectKey); //s3.listObjectsV2({}).promise();
console.log(response);
};
function getS3Objects(bucket,key) {
return s3.getObject({ Bucket:bucket, Key:key, ResponseContentType:'application/json '})
.promise().then(file => { return file })
.catch(error => { return error });
}`
but the result is getting null .
I understand what you are trying to accomplish here but that is not the right way to do it.
function getS3Objects(bucket,key){
return s3.getObject({Bucket:bucket,Key:key,ResponseContentType:'application/json'})
.promise().then(file=>{return file})
.catch(error =>{return error});
}`
The part above will still return a promise object, which means that you need to handle it accordingly. Instead of const response = getS3Objects(bucket,objectKey); you want to do
getS3Objects(bucket,objectKey).then(response => console.log(response));
Inside of your handler function.
Furthermore, your usage of s3.getObject function is incorrect. Where first argument is an object - parameters, and the second argument is a callback function.
s3.getObject(params, function(err, data) {
if (err) console.log(err, err.stack); // an error occurred
else console.log(data);
Therefore in your case, you want to modify your getS3Objects function a bit. If you want to use promises, then you can do it like this.
function getS3Objects(bucket, key) {
return new Promise((resolve, reject) => {
s3.getObject(
{
Bucket: bucket,
Key: key,
ResponseContentType: 'application/json'
},
(err, data) => {
if (err) {
reject(err);
} else {
resolve(data);
}
}
);
});
}
Another way that you can do this is as follows:
const AWS = require('aws-sdk');
const s3 = new AWS.S3();
async function readFile(Bucket, Key) {
const params = {
Bucket,
Key,
ResponseContentType: 'application/json',
};
const f = await s3.getObject(params).promise();
return f.Body.toString('utf-8');
}
readFile('mybucket', 'xyz.json').then(console.log);

Read dynamodb code not being executed in exports.handler in lambda function

I have a lambda function written in node.js that returns a QRCode Image. I am also trying to read a value from the Dynamodb. However, the console logs inside it do not seem to be executed which makes me think the code is not being run.
I suspect this is due to so synchronization issues. But I am not sure what to do to fix it. The code is below:
var qrImage = require('qr-image');
const AWS = require('aws-sdk');
const docClient = new AWS.DynamoDB.DocumentClient({region:'us-west-2'});
exports.handler = async(event, context, callback) => {
var path = event.path;
var drugId = path.replace(/\//g, '');
var params = {
TableName: 'QRCodeInfo',
Key: {
"DrugId" : "1234"
}
};
docClient.get(params, function(err,data) { //does not get executed
if (err) {
console.log(err);
} else {
console.log(data);
}
});
return sendRes(200,drugId); //this works. Image is seen.
};
const sendRes = (status, body) => {
//console.log(body);
const svg_string = qrImage.imageSync(body, { type: 'svg', size: 10 });
var response = {
statusCode: status,
headers: {
"Content-Type": "image/svg+xml"
},
body: svg_string
};
return response;
};
You are probably exiting the lambda before the callback of the dynamodb call has had a chance to execute.
Try calling callback(null, data) in the callback of the dynamo call, after your console.log and similar in the err scenario e.g. callback(err)
You do not exit a lambda by calling return, you should be calling callback() (that's why it's available as the 3rd argument of the lambda) see https://docs.aws.amazon.com/lambda/latest/dg/nodejs-prog-model-handler.html#nodejs-prog-model-handler-callback

Resources