Search code examples
node.jsgoogle-cloud-functionsdialogflow-esdialogflow-es-fulfillmentfulfillment

Make HTTP GET from fulfillment in Node.JS


I'm trying to make my own google action and I want to call an external api to get responses.

Here is my code:

const { conversation } = require('@assistant/conversation');
const functions = require('firebase-functions');
const app = conversation({debug:true});
const https = require('https');

app.handle('Tester', conv => {
  // Implement your code here
  conv.add("ok it works");
});

app.handle('Tester2', conv => {
  // Implement your code here
  let url = 'https://jsonplaceholder.typicode.com/users?_limit=2';
  //const url = "https://samples.openweathermap.org/data/2.5/weather?q=London,uk&appid=b6907d289e10d714a6e88b30761fae22";
  
  http_req(url).then((message)=> {
    console.log(message[0].name);
      conv.add(message[0].name);
    //return Promise.resolve();
  });
});

function http_req(url) {
  return new Promise((resolve, reject) => {
      https.get(url, function(resp) {
          var json = "";
          resp.on("data", function(chunk) {
              //console.log("received JSON response: " + chunk);
              json += chunk;
          });

          resp.on("end", function() {
              let jsonData = JSON.parse(json);
                                console.log(jsonData[0].name);
                resolve(jsonData);
          });
      }).on("error", (err) => {
          reject("Error: " + err.message);
      });
  });
}

exports.ActionsOnGoogleFulfillment = functions.https.onRequest(app);

The logs: enter image description here

Error text:

Error: Response has already been sent. Is this being used in an async call that was not returned as a promise to the intent handler?

The problem is that the assistant won't say the conv.add(message[0].name); (obviusly it has a value)

Thanks in advance!


Solution

  • Thanks to a reddit user https://www.reddit.com/r/GoogleAssistantDev/comments/lia5r4/make_http_get_from_fulfillment_in_nodejs/gn23hi3?utm_source=share&utm_medium=web2x&context=3

    This error messages tells you just about all you need to know! Your call to con.add() is indeed being used in an asynchronous call (the callback chained to the Promise you created from http_req), and you are indeed not returning that Promise.

    Here's what's happening:

    Google calls your 'Tester2' handler

    You start an asynchronous HTTP request via http_req, encapsulated in a Promise

    Your function completes before the HTTP request does

    Google sees that you are not returning anything from your handler and assumes that you're done, so it sends the Response

    The HTTP request finishes and its Promise resolves, calling your code attached by the then() function

    The simple solution here is to return the Promise created by your http_req(...).then(...) code, so Google will know that you're not just quite done, and it should wait for that Promise to resolve before sending the Response.

    If you can use async/await it becomes a bit clearer:

    app.handle('Tester2', async conv => {
      // Implement your code here
      let url = 'https://jsonplaceholder.typicode.com/users?_limit=2';
      //const url = "https://samples.openweathermap.org/data/2.5/weather?q=London,uk&appid=b6907d289e10d714a6e88b30761fae22";
    
      const message = await http_req(url);
      console.log(message[0].name);
      conv.add(message[0].name);
    });