Search code examples
node.jsaws-lambdahttprequestalexa-skills-kit

HTTP request with Alexa and Lambda on SDK v2, how to make it works?


I am playing with ASK SDK v2 provided by Amazon in order to make Skill for Alexa but I face an architectural problem :

First of all, the HTTP request works like a charm but I would like to return speach response if and only if my HTTP request is complete but I don't even know if it's possible because of the "handle" function that should return something (look at comments) :

const MyIntentHandler = {

  canHandle(handlerInput) {
    const request = handlerInput.requestEnvelope.request;
    return request.type === 'LaunchRequest' || (request.type === 'IntentRequest' && request.intent.name === 'MyIntent');
  },
  handle(handlerInput) {

    var options = {
      host: 'http://foo.com',
      port: 80,
      path: '/mypath',
      method: 'GET'
    };

    var req = http.request(options, function(result){
      result.on("end", function(){
        //I would like to return speak here like that :
        //return handlerInput.responseBuilder.speak("It works").withSimpleCard("MyTestApp", "It works").getResponse()
      })
    });
    req.end();
    //And I would like to remove this line to manage response in result.on("end", function(){}) above
    return handlerInput.responseBuilder.speak("It works").withSimpleCard("MyTestApp", "It works").getResponse();
  },
};

Any idea to deal with this ?


Solution

  • I found the official way to make it :

    1) Create a new funtion that manage http request and return a promise :

    function httpGet(options) {
      return new Promise(((resolve, reject) => {
    
        const request = http.request(options, (response) => {
          response.setEncoding('utf8');
          let returnData = '';
    
          if (response.statusCode < 200 || response.statusCode >= 300) {
            return reject(new Error(`${response.statusCode}: ${response.req.getHeader('host')} ${response.req.path}`));
          }
    
          response.on('data', (chunk) => {
            returnData += chunk;
          });
    
          response.on('end', () => {
            resolve(JSON.parse(returnData));
          });
    
          response.on('error', (error) => {
            reject(error);
          });
        });
        
        request.on('error', function (error) {
          reject(error);
        });
        
        request.end();
      }));
    }

    2) In the Intent, return a promise in which you call your httpGet function :

    const MyIntentHandler = {
    
      canHandle(handlerInput) {
        const request = handlerInput.requestEnvelope.request;
        return request.type === 'LaunchRequest' || (request.type === 'IntentRequest' && request.intent.name === 'MyIntent');
      },
      handle(handlerInput) {
    
        var options = {
          host: 'http://foo.com',
          port: 80,
          path: '/mypath',
          method: 'GET'
        };
    
        return new Promise((resolve, reject) => {
         httpGet(options).then((response) => {
           resolve(handlerInput.responseBuilder.speak("It is done.").getResponse());
         }).catch((error) => {
            resolve(handlerInput.responseBuilder.speak('Thor is not available at the moment. Please try again later or contact your administrator.')
            .getResponse());
          });
        });
      },
    };

    This is the way to do it properly. My example is base on alexa petmatch sample.