Sub callback in NodeJS for AWS Lambda - node.js

How can my AWS Lambda make an API call to some external service and return the output to the user who invoked the function please?
My piece of code looks like this:
module.exports.oauth_callback = (event, context, callback) => {
oauth2.authorizationCode.getToken({
redirect_uri: 'https://' + event.headers.Host + event.requestContext.path
}).then(results => {
callback(null, {statusCode: 200, body: JSON.stringify(results)});
});
};
With this, I receive a 500 Internal Error because I believe the lambda function is exiting before returning the result.
EDIT Case number 2:
Actually in the case where I add another callback like this:
return oauth2.authorizationCode.getToken({
code: code,
}).then(results => {
createFirebaseAccount(results).then(token => {
const template = signInFirebaseTemplate(token);
return callback(null, { statusCode: 200, body: template.toString() });
});
});
};
My variable template has its value but the next callback never returns and my function times out

I think you might need to return your promise from getToken(), like this:
module.exports.oauth_callback = (event, context, callback) => {
return oauth2.authorizationCode.getToken({
redirect_uri: 'https://' + event.headers.Host + event.requestContext.path
}).then(results => {
callback(null, {statusCode: 200, body: JSON.stringify(results)});
});
};
If that doesn't work, check your CloudWatch log under the monitoring tab of your Lambda.

Related

Invoke lambda instances from lambda and wait for the longest to comple

I have a lambda and I want to invoke multiple lambdas instance and wait for the longest to complete.
I already checked online and found very interesting threads but none of them helped me achieve what I want.
Here's the code:
Lambda as promise
invokeVideoRenderer = (params) => {
const param = {
FunctionName: 'myFunction',
InvocationType: 'Event',
Payload: JSON.stringify({ body: params })
};
return new Promise(async (resolve, reject) => {
this.lambda.invoke(param, (error, data) => {
if (error) {
reject(error);
}
resolve(data); <--- it always returns { StatusCode: 202, Payload: '' }
});
});
};
the loop where I invoke the different lambdas
const promises = [...Array(totalNumberOfLambdaToInvoke).keys()].map(
async (part: number) => {
const body = {
// body
};
const request = await invokeVideoRenderer(body);
return request;
}
);
const results = await Promise.all(promises); <--- here I want to wait for the longest to complete
The lambda I am invoking:
const myFunction = async (event, context, callback) => {
try {
//...code
callback(null, {
status: 200,
body: JSON.stringify({
status: "SUCCESS",
}),
});
return;
} catch (error) {
callback(null, {
status: 500,
body: JSON.stringify({
status: "FAILED",
errorMessage: error,
}),
});
return;
}
};
What am I doing wrong?
An InvocationType of Event tells Lambda to invoke the child Lambda asynchronously. Hence the Lambda service responds with 202 (Accepted) response immediately. Meanwhile the child Lambda function executes.
If you want it to execute synchronously, then don't provide an InvocationType of Event. Either remove it entirely to get the default, or explicitly set it to RequestResponse (which is the default).
Note: it's typically not ideal to invoke Lambda functions synchronously if you can avoid it, because you are paying for both Lambdas concurrently.

How to make Lambda function to wait till async operation finished?

I am new to NodeJS as well as AWS Lambda. I am creating a lambda function to get pre-signed URL for s3 put object. The problem I am facing here is that, call s3.getSignedUrl is async process which respond signed URL in callback but before this call responds, lambda function controls is reaching to the next line after this call and empty response is being received at client side. here is my code snippet of lambda function.
exports.handler = async (event) => {
// Some stuff like reading inputs from request, initialising s3 object with AWS_ACCESS_KEY, and AWS_SECRET_KEY
var response="default response";
response = s3.getSignedUrl('putObject', {
"Bucket": myBucket,
"Key": fileName,
"ContentType": fileType
}, function (err, url) {
if (err) {
console.log("error")
return {
statusCode: 200,
body: JSON.stringify("Error in creating signed url"),
};
} else {
console.log("generated "+url)
return {
statusCode: 200,
body: JSON.stringify(url),
};
}
});
console.log("at end");
return response;
};
If say in terms of log statements then before statement error or generated url at end log statement is printed and default response is returning. Can some body correct this code to responde the response being prepared inside callbacks of s3.getSignedUrl.
If you are just looking to create presigned url, you can use getSignedUrlPromise method that returns promise
exports.handler = async (event) => {
return s3.getSignedUrlPromise("putObject", {
Bucket: "my bucket",
Key: "my file name",
ContentType: "some file type",
});
};
If you need customize the response
exports.handler = async (event) => {
return new Promise((resolve, reject) => {
s3.getSignedUrl(
"putObject",
{
Bucket: "my bucket",
Key: "my file name",
ContentType: "some file type",
},
(err, url) => {
if (err) reject(err);
if (url)
resolve({
statusCode: 200,
body: url,
});
});
});
};
Easiest by far is to use the Promise API for aws-sdk, and just await getSignedUrl.

How to use Nodejs "request" work aysn in AWS Lambda?

When I'm trying to use "request" to access external API and get back the response in AWS Lambda, I don't know how to properly put my "return" code to return the response.
NodeJs 8.10
var request = require('request');
module.exports.sendcode = async (event) => {
let options = {
url: 'https://api.netease.im/sms/sendcode.action?' + content,
method: 'POST'
};
return await request(options, function (error, response, body) {
console.log(body);
return {
statusCode: 200,
body: JSON.stringify({
message: body,
input: event,
}),
};
});
};
When I run this code in serverless framework, I got a null response, there is nothing in the body, actually it should have at least the "input" attribute.
But console.log already logs the actual response from API.
It looks like my "return" code is not executed at all.
(If I remove async and await, then the program hangs until timeout)
Can anyone help how to modify this code to make it work?
Request does not use promises, so your await keyword is doing nothing. If you want to use promises with request, you need to find a library that supports promises, as noted here: https://www.npmjs.com/package/request#promises--asyncawait A popular option is https://github.com/request/request-promise-native
However, it is a simple matter to simply wrap a request in a promise, so that you don't need to use another dependency
var request = require('request');
const requestHandler = (options) => new Promise((resolve, reject) => {
request(options, (error, response, body) => {
if (error) {
console.error(error);
reject(error);
} else {
console.log(response, body);
resolve({ response, body });
}
});
});
module.exports.sendcode = async (event) => {
let options = {
url: 'https://api.netease.im/sms/sendcode.action?' + content,
method: 'POST'
};
const { response, body } = await requestHandler(options);
return {
statusCode: 200,
body: JSON.stringify({
message: body,
input: event,
})
}
};
Instead of writing your own promise wrapper, maybe request-promise pacakge with promise wrapper on top of request by the same authors, would be of interest to you.
const rp = require('request-promise')
module.exports.sendcode = async (event) => {
let options = {
url: 'https://api.netease.im/sms/sendcode.action?' + content,
method: 'POST'
};
const body = await rp(options)
console.log(body);
return {
statusCode: 200,
body: JSON.stringify({
message: body,
input: event,
}),
};
};
If you want full response you've to simply set resolveWithFullResponse to true
let options = {
url: 'https://api.netease.im/sms/sendcode.action?' + content,
method: 'POST',
resolveWithFullResponse: true
};
// now response contains full respose
const response = await rp(options)
// response.body
// response.statusCode
Use Async/Await with promises. You cannot use Async-Await syntax with callback. Either you must write a promise wrapper for your 'request'callback or you can use request-promise module which is already available.
I have already provided answer for similar problem which you can refer here.
Undefined value after returning an array of values from a MySQL query in a different file

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

Node.js serverless offline hangs after 1st request

first time when i make a request its working fine, but when i make the request again without restarting the server, its going time out and returning some promise error
following is the code and error
module.exports.getOne = (event, context, callback) => {
context.callbackWaitsForEmptyEventLoop = false;
db.once('open', () => {
Client.findOne({name:{ $regex : new RegExp(event.pathParameters.name, "i") }})
.then(client => callback(null, {
statusCode: 200,
body: JSON.stringify(client)
}))
.catch(err => callback(null, {
statusCode: err.statusCode || 500,
headers: { 'Content-Type': 'text/plain' },
body: 'Could not fetch the note.'
}))
.finally(() => {
// Close db connection or node event loop won't exit , and lambda will timeout
db.close();
});
});
};
Error:
(node:3273) UnhandledPromiseRejectionWarning: Error: Cannot stop server while in stopping state
The issue is that you aren't completing the call by calling callback so it thinks that your getOne() function isn't finished.
Take a look at the sample code that serverless provides when you setup a new serverless app with their template
module.exports.hello = (event, context, callback) => {
const response = {
statusCode: 200,
body: JSON.stringify({
message: 'Go Serverless v1.0! Your function executed successfully!',
input: event,
}),
};
callback(null, response);
};

Resources