0
votes

In AWS- Lambda, I am making a call to retrieve the data from DynamoDB table and use the data to make a post request to API Gateway. I used Async / await to retrieve the data from DynamoDB. However, while making a post request to API Gateway Https.request is not getting called.

I am newbie to NodeJs and Lambda, appreciate your help to get a solution.

I tried implementing Promise without any luck. If I remove Async / await then Https.request call is working without any issues. But data is not available for https.request to make a post request (due to async calls).

// Dynamo DB Params
var {promisify} = require('util');
var AWS = require('aws-sdk');
var dynamoDB  = new AWS.DynamoDB.DocumentClient();
var dynamoDBGetAsync = promisify(dynamoDB.get).bind(dynamoDB );
var https = require('https');

exports.handler = async function(event,context) {
    let probID = JSON.stringify(event.ID);
    probID = probID.replace(/"/g, '');      

    let params = {
        TableName : '<dummy_table>',
        Key:{
            'Server':<serverid>,
            'Service':'process2'
        }
    };

    //fetching the details from Dynamo DB 
    var dataResult= await dynamoDBGetAsync(params);   

    var obj;   
    var message = 'Sample Message';
    functionCall(dataResult,callback => {
        obj = JSON.parse(callback);
    });
}

function functionCall(data,result) {
// Options and headers for the HTTP request
    var options = {
        host: 'dummy.execute-api.us-east-1.amazonaws.com',
        port: 443,
        path: '/dev/test',
        method: 'POST',
        headers: {
            'Accept':'*/*',
            'cache-control':'no-cache',
            'Content-Type': 'application/json'
        }
    };
    const body= "{\"msg\": "+ data + "\"}";
    console.log('BODY.....:'+body);      //able to see this comment in console

    let req = https.request(options, (res) => {     // This is not getting invoked and cannot see below comment in console
        console.log('IN HTTPS REQUEST.....');
        var responseString = '';
        console.log("statusCode:" + res.statusCode);

        res.setEncoding('UTF-8');
        // Collect response data as it comes back.
        res.on('data', function(data) {
            responseString += data;

        });

        res.on('end', function() {
            result(responseString);
        });
    });

    // Handler for HTTP request errors.
    req.on('error', function(e) {
        console.error('HTTP error: ' + e.message);
        result('Request completed with error(s).');
    });

    req.write(body);
    req.end();
}
1

1 Answers

1
votes

There are a few possible problems but the most striking to me is the fact that you're incorrectly mixing programming styles.

You have declared the handler as an async function which is fine. But inside the async function you're mixing an awaitable call with a classic continuation-style function call that you're not properly awaiting.

What is happening is that your Lambda executes the first part (call to dynamo) and then the runtime ends execution before actually completing your second continuation-style function call.

One solution is to wrap your https request in a promise and await on that in the body of the Lambda handler:

// Dynamo DB Params
const {promisify} = require('util');
const AWS = require('aws-sdk');
const dynamoDB  = new AWS.DynamoDB.DocumentClient();
const dynamoDBGetAsync = promisify(dynamoDB.get).bind(dynamoDB );
const https = require('https');

exports.handler = async function(event,context) {
    let probID = JSON.stringify(event.ID);
    probID = probID.replace(/"/g, '');      

    let params = {
        TableName : '<dummy_table>',
        Key:{
            'Server':<serverid>,
            'Service':'process2'
        }
    };

    //fetching the details from Dynamo DB 
    let dataResult= await dynamoDBGetAsync(params);   

    const message = 'Sample Message';
    let jsonResult = await functionCall(dataResult);
    let obj = JSON.parse(jsonResult);
    // presumably you want to return something here (not sure if obj or something else)
    return obj;
}

function functionCall(data) {
    // Options and headers for the HTTP request
    const options = {
        host: 'dummy.execute-api.us-east-1.amazonaws.com',
        port: 443,
        path: '/dev/test',
        method: 'POST',
        headers: {
            'Accept':'*/*',
            'cache-control':'no-cache',
            'Content-Type': 'application/json'
        }
    };
    const body= "{\"msg\": "+ data + "\"}";
    console.log('BODY.....:'+body);

    // make this function awaitable by returning a promise
    return new Promise((resolve, reject) => {
      let req = https.request(options, (res) => {
        console.log('IN HTTPS REQUEST.....');
        let responseString = '';
        console.log("statusCode:" + res.statusCode);

        res.setEncoding('UTF-8');
        // Collect response data as it comes back.
        res.on('data', function(data) {
          responseString += data;
        });

        res.on('end', function() {
          // complete the promise successfully
          resolve(responseString);
        });
      });

      req.on('error', function(e) {
        console.error('HTTP error: ' + e.message);
        // complete the promise with error (will throw if awaited)
        reject('Request completed with error(s).');
      });

      req.write(body);
      req.end();
    });
}

By the way - you don't really need promisify to work with DynamoDB using async/await. The DynamoDB client has built-in support for promises that you can await. Just call .promise() on your operation and await on that. For example, you could simply write:

let dataResult = await dynamoDB.get(params).promise();