How do I get the data back from a lambda invoked with as an event to the calling function?
Essentially the lambda function I have is:
exports.handler = function(event, context, callback) {
var data = {};
data.foo ='hello';
callback(null, data)
}
and the invoking function looks like this:
var AWS = require('aws-sdk');
var lambda = new AWS.Lambda();
var params = {
FunctionName: 'SomeFunction',
InvocationType: 'Event'
};
lambda.invoke(params, function (err, data) {
if (err) {
console.log(err, err.stack); // an error occurred
} else {
console.log(JSON.stringify(data, null, 2));
}
});
However the only thing I get back from the function is
{
"StatusCode": 202,
"Payload": ""
}
I thought the point of the callback parameter was to allow the invoking function to get the data when the function has finished. Am I using it wrong or is what I am asking not possible with Lambdas?
When you invoke the Lambda function you need to set the InvocationType to 'RequestResponse' instead of 'Event'.
When using the Event type your callback is invoked when the payload has been received by AWS's servers. When using the RequestResponse type your callback is invoked only after the Lambda function has completed and you will receive the data it provided to its callback. It is not possible to do what you want with the Event type.
As #MatthewPope commented in this answer, if you do need the results from an asynchronous Lambda execution, you should consider having the Lambda function write its results to S3 (or something to that effect) at a known location where clients of the Lambda function can periodically check for the existence of the result.
Related
I'm running a "Node.JS" lambda on AWS that sends a message to SQS.
For some reason the SQS callback function get execute only once every couple of calls. It's looks like that the thread that running the lambda finish the run (because it's not a synchronous call to SQS and also can't return a Future) and therefore the lambda doesn't "stay alive" for the callback to get executed.
How can I solve this issue and have the lambda wait for the SQS callback to get execute?
Here is my lambda code:
exports.handler = async (event, context) => {
// Set the region
AWS.config.update({region: 'us-east-1'});
// Create an SQS service object
var sqs = new AWS.SQS({apiVersion: '2012-11-05'});
const SQS_QUEUE_URL = process.env.SQS_QUEUE_URL;
var params = {
MessageGroupId: "cv",
MessageDeduplicationId: key,
MessageBody: "My Message",
QueueUrl: SQS_QUEUE_URL
};
console.log(`Sending notification via SQS: ${SQS_QUEUE_URL}.`);
sqs.sendMessage(params, function(err, data) { //<-- This function get called about one time every 4 lambda calls
if (err) {
console.log("Error", err);
context.done('error', "ERROR Put SQS");
} else {
console.log("Success", data.MessageId);
context.done(null,'');
}
});
};
You should either stick to callback based approach, or to promise based one. I recommend you to use the latter:
exports.handler = async (event, context) => {
// Set the region
AWS.config.update({region: 'us-east-1'});
// Create an SQS service object
var sqs = new AWS.SQS({apiVersion: '2012-11-05'});
const SQS_QUEUE_URL = process.env.SQS_QUEUE_URL;
var params = {
MessageGroupId: "cv",
MessageDeduplicationId: key,
MessageBody: "My Message",
QueueUrl: SQS_QUEUE_URL
};
console.log(`Sending notification via SQS: ${SQS_QUEUE_URL}.`);
try {
await sqs.sendMessage(params).promise(); // since your handler returns a promise, lambda will only resolve after sqs responded with either failure or success
} catch (err) {
// do something here
}
};
P.S. Instantiating aws classes in the handler is not a good idea in lambda environment, since it increases the cold start time. It's better to move new AWS.SQS(...) action out of handler and AWS.config.update() too, since these actions will be executed on each call of the handler, but you really need them to be executed only once.
I am testing a call to a SOAP service using node-soap library.
This works fine as a standalone node.js app and the SOAP service responds, however when I package the same code up as a serverless AWS lambda function (using serverless framework, but also executed directly in AWS lambda), it doesn’t appear to create the soap client.
Any thoughts on why this might be happening?
export async function main(event, context) {
var soap = require('soap');
var url = 'https://service.blah.co.uk/Service/Function.asmx?wsdl';
var soapOptions = {
forceSoap12Headers: true
};
var soapHeader = {
'SOAPAction': 'http://www.blah.co.uk/Services/GetToken'
};
var params = {
xmlMessage: message
};
console.log(JSON.stringify(params));
soap.createClient(url, soapOptions, function (err, client) {
//the serverless AWS lambda function never reaches this point (deployed and invoked locally)
console.log("In create client")
if (err) console.log(err);
client.addSoapHeader(soapHeader);
client.GetToken(params, function (err, data) {
console.log(data);
});
});
}
I have created a very minimal working example using async/await. Rather using the old style callback approach, just invoke soap.createClientAsync(url).
Here's the code:
'use strict';
const soap = require('soap');
module.exports.hello = async (event, context) => {
const url = 'http://www.thomas-bayer.com/axis2/services/BLZService?wsdl';
const client = await soap.createClientAsync(url)
console.log(client)
return {
statusCode: 200,
message: 'It works!'
}
}
And here are the logs (partially):
EDIT: Function's output (via AWS's Lambda Console)
EDIT 2 - The REAL problem
Check the link above around async/await. The async keyword will execute asynchronously and will return a Promise. Since it's an asynchronous function, your Lambda is being terminated before it could actually execute your callback.
Using async/await will make your code simpler and will have you stop beating your brains out for such a small thing like this.
I have 2 Lambda function. First fetch data from database and put it in DynamoDB. And Second fetch data from the DynamoDB. I want to execute both lambda function sequentially (i.e.first then second). How could I invoke 2nd Lambda function inside 1st Lambda function? How to pass data to that Lambda function? And how to get response from the 2nd Lambda function? Does anyone know possible way? Please help.... Thank u....
For nodejs you can use lambda.invoke function to execute another lambda
var aws = require("aws-sdk");
var lambda = new aws.Lambda({
region: "us-west-1",
});
var params = {
FunctionName: "MyFunction",
InvocationType: "RequestResponse",
LogType: "Tail",
Payload: "<Stringified oject to pass parameters as event>",
};
lambda.invoke(params, function (err, data) {
if (err) console.log(err, err.stack);
// an error occurred
else console.log(data); // successful response
});
for info visit this link:
https://docs.aws.amazon.com/AWSJavaScriptSDK/latest/AWS/Lambda.html#invoke-property
I'm working on an Alexa skill in Node that runs in AWS Lambda, and having trouble getting a callback to execute when I emit an event. The Alexa-skills-kit for Node.JS README demonstrates passing a callback function to an event handler, and recommends using the arrow function to preserve context:
this.emit('JustRight', () => {
this.emit(':ask', guessNum.toString() + 'is correct! Would you like to play a new game?',
'Say yes to start a new game, or no to end the game.');
});
I'm trying to do the same, but find that my callback never appears to be executed. I thought this was because AWS Lambda is limited to Node 4.3.2, and the arrow function is not available, so I tried passing this context back to the callback the old fashioned way:
In New Session handler:
if(!account_id) {
console.log('Access token:' + accessToken);
this.emit('getAccount', accessToken, function (retrieved_id) {
console.log('account id in callback: ' + retrieved_id);
this.emit('welcome');
});
}
In event handler:
accountHandler = {
'getAccount': function (accessToken, cb) {
console.log('fetching account id');
var client = thirdparty.getClient(accessToken);
var r = client.getAccountForToken(client);
r.then(function (data) {
console.log('got it:' + data);
this.attributes['account_id'] = data;
cb.call(this, data);
}).catch(function (err) {
this.emit('handleApiError', err);
});
},
}
I can see that I'm successfully retrieving the account ID, in the logs, but Lambda is executing without error and without invoking my callback function. I'm trying to figure out if there is a problem with invoking the callback within the Promise 'then' function, or if something else is going on.
The exact problem was the lack of context within the promise then function. I fixed this by using the arrow function inside the getAccount handler:
r.then(data => {
console.log('got it:' + data);
this.attributes['account_id'] = data;
this.emit('welcome');
})
Of course this also shows that Lambda Node.JS functions support the arrow function just fine.
I am using the test function from AWS console:
console.log('Loading event');
exports.handler = function(event, context) {
console.log('value1 = ' + event.key1);
console.log('value2 = ' + event.key2);
console.log('value3 = ' + event.key3);
context.done(null, 'Hello World'); // SUCCESS with message
};
And calling it in nodejs as follows:
var params = {
FunctionName: 'MY_FUNCTION_NAME', /* required */
InvokeArgs: JSON.stringify({
"key1": "value1",
"key2": "value2",
"key3": "value3"
})
};
lambda.invokeAsync(params, function(err, data) {
if (err) {
// an error occurred
console.log(err, err.stack);
return cb(err);
}
// successful response
console.log(data);
});
and everything works fine:
//Console Output
{ Status: 202 }
But I was expecting to receive the message from context.done(null, 'Message') as well...
Any idea how to get the message?
As Eric mentioned, currently Lambda doesn't offer a REST endpoint to run the function and return its result, but may in the future.
Right now, your best bet would be to use a library like lambdaws, which wraps the function deployment and execution for you and handles returning results via an SQS queue. If you'd like more control by rolling your own solution, the process is straightforward:
Create an SQS queue
Have your Lambda function write its result to this queue
In your client, poll the queue for a result
You are calling invokeAsync, so your Lambda function is run asynchronously. This mean you get the success means back at the point your function is successfully started, and not after it completes.
As of this writing, AWS Lambda does not yet offer a way to invoke a function synchronously, returning information from the function directly back to the caller. However, this appears to be a common request and Amazon has publicly stated they are considering the feature.