6

I'm trying to run a specific function from an existing app via AWS Lambda, using the JS SDK to invoke Lambda from my node.js app. Since I'm overwriting the existing function, I'll have to keep its basic structure, which is this:

overwrittenFunction = function(params) {
    //get some data
    return dataArray;
}

..so I need to end up with an array that I can return, if I'm looking to keep the underlying structure of the lib I use the same. Now as far as I know, Lambda invocations are asynchronous, and it's therefore not possible to do something like this:

overwrittenFunction = function(params) {
    lambda.invoke(params, callback);
    function callback(err,data) {
        var dataArray = data;
    }
    return dataArray;
}

(I've also tried similar things with promises and async/await).

afaik I have two options now: somehow figure out how to do a synchronous Lambda invocation, or modify my library / existing app (which I would rather not do if possible).

Is there any way to do such a thing and somehow return the value I'm expecting?

(I'm using node v8.9.4)

wagshadow
  • 61
  • 1
  • 1
  • 3

2 Answers2

23

Lambda and async/await are a bit tricky, but the following is working for me (in production):

const lambdaParams = {
    FunctionName: 'my-lambda',
    // RequestResponse is important here. Without it we won't get the result Payload
    InvocationType: 'RequestResponse',
    LogType: 'Tail', // other option is 'None'
    Payload: {
        something: 'anything'
    }
};

// Lambda expects the Payload to be stringified JSON
lambdaParams.Payload = JSON.stringify(lambdaParams.Payload);

const lambdaResult = await lambda.invoke(lambdaParams).promise();

logger.debug('Lambda completed, result: ', lambdaResult.Payload);

const resultObject = JSON.parse(lambdaResult.Payload)

Wrap it all up in a try/catch and go to town.

slaughtr
  • 536
  • 5
  • 17
  • 1
    We can call `.promise()` here because most SDK operations return a `AWS.Request` object: https://docs.aws.amazon.com/sdk-for-javascript/v2/developer-guide/using-promises.html – Kenny Worden Nov 30 '20 at 16:44
  • How to handle long requests? Would node timeout? – KJ Ang Sep 30 '21 at 00:57
  • 1
    Very helpful thank you. Worth drawing even more attention to `InvocationType: 'RequestResponse',` I think - this was the piece of the puzzle missing for me – Matt Saunders Jan 24 '22 at 17:55
5

You can use async await but as the AWS SDK uses node callback pattern you'll need to wrap the function with the built-in promisify.

const promisify = require('utils').promisify 
const aws = require('aws-sdk');

const lambda = aws.Lambda();
const invoke = promisify(lambda.invoke);

async function invocation(params) {
  try {
    return await invoke(params);
  } catch (err) {
    throw new Error('Somethings up');
  }
}

const data = invocation(params);
sidhuko
  • 3,274
  • 1
  • 17
  • 22
  • but don't async functions always return a promise? My problem is that I can't deal with promises in a way that lets me return their value "out" of the function :/ – wagshadow Jan 18 '18 at 10:04
  • Did you try this? – sidhuko Jan 18 '18 at 11:44
  • 3
    I did, it throws an error: this.makeRequest is not a function. || if I understand correctly, you're trying to make the lambda invocaction return a promise in this case, right? Afaik, that does work natively in the SDK by using lambda.invoke().promise() (which I tried) – wagshadow Jan 18 '18 at 12:40
  • 1
    This is how it should be promisified to avoid "this.makeRequest is not a function" error `const invoke = promisify(lambda.invoke.bind(lambda));` Also the import needs to be `const promisify = require('util').promisify;` It is 'util' and not 'utils' – balatamilmani Dec 10 '20 at 01:53