Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

AWS Lambda: Async Calls outside handler (initialization section, invoke lambda)

I would like to call an asynchronous function outside the lambda handler with by the following code:

var client;
(async () => {
    var result =  await initSecrets("MyWebApi");
    var secret = JSON.parse(result.Payload);
    client= new MyWebApiClient(secret.API_KEY, secret.API_SECRET); 
});

async function initSecrets(secretName) {
    var input = {
    "secretName" : secretName
    };
    var result = await lambda.invoke({
       FunctionName: 'getSecrets',
       InvocationType: "RequestResponse",
       Payload: JSON.stringify(input)
    }).promise();
    return result;
}

exports.handler = async function (event, context) {

    var myReq = await client('Request');
    console.log(myReq);
};

The 'client' does not get initialized. The same code works perfectly if executed within the handler. initSecrets contains a lambda invocation of getSecrets() which calls the AWS SecretsManager Has anyone an idea how asynchronous functions can be properly called for initialization purpose outside the handler?

Thank you very much for your support.

like image 575
Homer Avatar asked Mar 18 '19 18:03

Homer


People also ask

Is Lambda invoke asynchronous?

Lambda functions can be invoked either synchronously or asynchronously, depending upon the trigger. In synchronous invocations, the caller waits for the function to complete execution and the function can return a value.

Which service invoke Lambda function asynchronously?

AWS services such as AWS S3 or SNS invoke Lambda functions asynchronously by passing events. With Lambda Destinations, a new feature launched by AWS, it can send invocation records to downstream services to chain together. The Lambda service places an event in a queue and returns a “success” response.

Does EventBridge invoke Lambda asynchronously?

SNS, S3 & EventBridge, StepFunctions— Are examples of Asynchronous Lambda Invocations. The events coming from those services will be queued up and will be pending to be processed by lambda. If your function returns an error, AWS will automatically retry the invoke twice, for a total of three invocations.

What is the difference between synchronous and asynchronous Lambda invocations?

When you invoke a function, you can choose to invoke it synchronously or asynchronously. With synchronous invocation, you wait for the function to process the event and return a response. With asynchronous invocation, Lambda queues the event for processing and returns a response immediately.


3 Answers

This can be also be solved with async/await give Node v8+

You can load your configuration in a module like so...

const fetch = require('node-fetch');

module.exports = async () => {

  const config = await fetch('https://cdn.jsdelivr.net/gh/GEOLYTIX/public/z2.json');

  return await config.json();

}

Then declare a _config outside the handler by require / executing the config module. Your handler must be an async function. _config will be a promise at first which you must await to resolve into the configuration object.

const _config = require('./config')();

module.exports = async (req, res) => {

  const config = await _config;

  res.send(config);

}

like image 155
Dennis Bauszus Avatar answered Oct 19 '22 10:10

Dennis Bauszus


I ran into a similar issue trying to get next-js to work with aws-serverless-express.

I fixed it by doing the below (using typescript so just ignore the :any type bits)

const appModule = require('./App');
let server: any = undefined;

appModule.then((expressApp: any) => {
  server = createServer(expressApp, null, binaryMimeTypes);
});

function waitForServer(event: any, context: any){
  setImmediate(() => {
    if(!server){
      waitForServer(event, context);
    }else{
      proxy(server, event, context);
    }
  });
}

exports.handler = (event: any, context: any) => {
  if(server){
    proxy(server, event, context);
  }else{
    waitForServer(event, context);
  }
}

So for your code maybe something like

var client = undefined;

initSecrets("MyWebApi").then(result => {
    var secret = JSON.parse(result.Payload);
    client= new MyWebApiClient(secret.API_KEY, secret.API_SECRET)
})

function waitForClient(){
  setImmediate(() => {
    if(!client ){
      waitForClient();
    }else{
      client('Request')
    }
  });
}

exports.handler = async function (event, context) {
  if(client){
    client('Request')
  }else{
    waitForClient(event, context);
  }
};

like image 38
Will Avatar answered Oct 19 '22 09:10

Will


client is being called before it has initialised; the client var is being "exported" (and called) before the async function would have completed. When you are calling await client() the client would still be undefined.

edit, try something like this

var client = async which => {
    var result =  await initSecrets("MyWebApi");
    var secret = JSON.parse(result.Payload);
    let api = new MyWebApiClient(secret.API_KEY, secret.API_SECRET); 
    return api(which) // assuming api class is returning a promise
}

async function initSecrets(secretName) {
    var input = {
    "secretName" : secretName
    };
    var result = await lambda.invoke({
       FunctionName: 'getSecrets',
       InvocationType: "RequestResponse",
       Payload: JSON.stringify(input)
    }).promise();
    return result;
}

exports.handler = async function (event, context) {

    var myReq = await client('Request');
    console.log(myReq);
};
like image 1
Tobin Avatar answered Oct 19 '22 11:10

Tobin