Read from DynamoDB with Lambda nodejs for Alexa Skill

Viewed 2323

Iam new to Skill and nodejs development and got my first problem very soon.

Basically, iam trying to read data from DynamoDB and let it speak over Alexa.

var title;

exports.handler = (event, context, callback) => { 
    getData();
    alexa = Alexa.handler(event, context, callback); 
    alexa.appId = APP_ID;
    alexa.registerHandlers(handlers);
    alexa.execute();     
};

const handlers = {
    'LaunchRequest': function () {
        this.emit('DoSomethingIntent');
    },
    'DoSomethingIntent': function () {      
        this.response.speak('Here are your data ' + title);
        this.emit(':responseReady');        
    },
};

function getData() {
    var ddb = new AWS.DynamoDB.DocumentClient({region:'eu-west-1'});
    var params = {
        TableName: 'data', 
        Key: {'data_id' : 1,},
    };      
    ddb.get(params, function(err, data) {
        if (err) {
        }else{
           title = data.Item.title;             
        }
    });
}

The problem is that the DynamoDB.DocumentClient.get function is running asynchron and at the same time when the DoSomethingIntent runs the title variable is undefined.

What would be the best practice to solve this problem?

The only Solution which has worked for me so far was that:

ddb.get(params, function(err, data) {
    if (err) {

    }else{
        title = data.Item.title;                                      
        alexa.registerHandlers(handlers);
        alexa.execute(); 
    }
});

But it does not seem very practical for me!

2 Answers

This is happening because ddb.get() function is asynchronous and callback is running after execution of your handler. So there is no certainty of variable title getting populated before your response is sent.

You can use native promise here.

Modify your getData function like below:

function getData() {
  var ddb = new AWS.DynamoDB.DocumentClient({region:'eu-west-1'});
  var params = {
      TableName: 'data', 
      Key: {'data_id' : 1,},
  };

  return new Promise((resolve,reject) => {
    ddb.get(params, function(err, data) {
        if (err) {
        }else{
          title = data.Item.title;
          resolve(title);       
        }
    });
  })
}

And do following changes in your handler function and response handler:

exports.handler = (event, context, callback) => { 
    // Remove getData() from here
    alexa = Alexa.handler(event, context, callback); 
    alexa.appId = APP_ID;
    alexa.registerHandlers(handlers);
    alexa.execute();     
};

const handlers = {
    'LaunchRequest': function () {
        this.emit('DoSomethingIntent');
    },
    'DoSomethingIntent': function () {
      getData()
      .then( (title) => {
        this.response.speak('Here are your data ' + title);
        this.emit(':responseReady'); 
      })
      .catch( (error) => { // Handler error gracefully
        console.log(error);
        this.response.speak('Something is wrong. Try later.')
        this.emit(':responseReady');
      })
    },
};
Related