1

I am new to NodeJS. I am confused regarding how to have multiple API calls done in single lambda function. This is not lambda/aws specific question it's more of a callback related confusion.

Here's the requirement.

I want to get all the instances and all the RDS list and collect them both in single array and return them.

Here's some code I have written, which semantically works I verified.

'use strict';

const AWS = require('aws-sdk');

function getInstancesByRegion(regionName) {
  var instances = [];
  var ec2withRegion = new AWS.EC2({
    region: regionName
  });
  ec2withRegion.describeInstances({}, function(err, data) {
    if (err) {
      console.error(err.toString());
    } else {
      var currentTime = new Date();
      console.log(currentTime.toString());

      for (var r = 0, rlen = data.Reservations.length; r < rlen; r++) {
        var reservation = data.Reservations[r];
        for (var i = 0, ilen = reservation.Instances.length; i < ilen; ++i) {
          var instance = reservation.Instances[i];

          var name = '';
          for (var t = 0, tlen = instance.Tags.length; t < tlen; ++t) {
            if (instance.Tags[t].Key === 'Name') {
              name = instance.Tags[t].Value;
            }
          }
          instances.push({
            id: instance.InstanceId,
            type: instance.InstanceType,
            name: name
          });          
        }
      }
      return instances;
    }
  });
}
function getDatabasesByRegion(regionName) {
  var dbInstances = [];
  var rdsWithRegion = new AWS.RDS({
    region: regionName
  });
  rdsWithRegion.describeDBInstances({}, function(err, data) {
    if (err) {
      console.error(err.toString());
    } else {
      var currentTime = new Date();
      console.log(currentTime.toString());
      for (var r = 0, rlen = data.DBInstances.length; r < rlen; r++) {
        var dbInstance = data.DBInstances[r];
        dbInstances.push({
          id: dbInstance.DBInstanceIdentifier
        });
      }
      return dbInstances;
    }
  });
}

exports.handler = (event, context, callback) => {
  var ec2 = new AWS.EC2();
  var params = {};

  var resources = [];
  ec2.describeRegions(params, function(err, data) {
    if (err) console.log(err, err.stack); // an error occurred

    for (var i = 0; i < data.Regions.length; i++) {
      var regionName = data.Regions[i].RegionName;
      console.log("Current RegionName is " + regionName); 
      resources.push({
        region: regionName,
        instances: getInstancesByRegion(regionName),
        databases: getDatabasesByRegion(regionName)
      });      
    }
  });
  callback(null, resources);
};
3
  • "which semantically works I verified" Nope. You are calling async functions in a synchronous way. Commented Jan 16, 2018 at 23:06
  • @dashmug : Exactly that's the issue, Not sure how to call multiple async functions and wait for all them and then return. Commented Jan 17, 2018 at 4:09
  • @dashmug: By semantically I meant the functions are working fine, I see that they are fetching instances and RDS list, I printed them in success callback. Commented Jan 17, 2018 at 4:11

1 Answer 1

4

Your code can be much cleaner if you use Javascript Promises instead of callbacks.

AWS SDK provides support for Promises by using .method(params).promise() instead of .method(params, callback).

Reference: Using Javascript Promises (AWS SDK)

You can then rewrite your code using promises. Promise.all() is the key in your use case.

This is how I would do it...

const AWS = require('aws-sdk')

const ec2 = new AWS.EC2()


function getInstancesByRegion(regionName) {
    const ec2withRegion = new AWS.EC2({
        region: regionName,
    })

    return ec2withRegion.describeInstances({}).promise()
        .then(data => data.Reservations.reduce((acc, reservation) => {
            const instances = reservation.Instances.map(instance => ({
                id: instance.InstanceId,
                type: instance.InstanceType,
                name: instance.Tags.find(tag => tag.Key === 'Name').Value,
            }))

            return acc.concat(instances)
        }, []))
}


function getDatabasesByRegion(regionName) {
    const rdsWithRegion = new AWS.RDS({
        region: regionName,
    })

    return rdsWithRegion.describeDBInstances({}).promise()
        .then(data => data.DBInstances.map(dbInstance => ({
            id: dbInstance.DBInstanceIdentifier,
        })))
}


function getResourcesByRegion(region) {
    return Promise.all([
        getInstancesByRegion(region),
        getDatabasesByRegion(region),
    ])
        .then(results => ({
            region,
            instances: results[0],
            databases: results[1],
        }))
}


exports.handler = (event, context, callback) => ec2.describeRegions({})
    .promise()
    .then((data) => {
        const getResources = data.Regions
            .map(region => getResourcesByRegion(region.RegionName))

        return Promise.all(getResources)
    })
    .then(resources => callback(null, resources))
    .catch(callback)
Sign up to request clarification or add additional context in comments.

3 Comments

This is amazing. Just one question. when you do return ec2withRegion.describeInstances({}).promise() in getInstancesByRegion. What does it actually return? A promise, if it does then shouldn't then used after the function call and not in the return statement it self? Sorry I primarily use java in which even with threads it is easier to understand.
Not sure how to explain it but we could rewrite it using async/await syntax (node 8+) and it might be more readable but then it won't be compatible with AWS Lambda's NodeJS runtime (node 6.10). Why not write your Lambda functions in Java though if that's your expertise?
Thanks, I will learn more about Promise and try to understand. I just tried with async library also that also works now. Will accept the answer.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.