7

The user types a message into a chat client (a website). This message is sent through to a cloud function set up on firebase. The cloud function then queries a 3rd party API which returns a response. This response needs to be sent back to the client to be displayed.

So basically my client calls a cloud function like so...

var submitMessage = firebase.functions().httpsCallable('submitMessage');
submitMessage({message: userMessage}).thenfunction(result) {
  //Process result
});

My cloud function looks like this...

exports.submitMessage = functions.https.onCall((data, context) => {
  request({
    url: URL,
    method: "POST",
    json: true,
    body: queryJSON //A json variable I've built previously
  }, function (error, response, body) {
    //Processes the result (which is in the body of the return)
  });

return {response: "Test return"};
});

I have included the request package and the API call itself works perfectly. I can print the result to the console from within the return function of the request. However, obviously because the request is asynchronous I can't just create a global variable and assign the result body to it. I have seen that you can call a callback function once the request is finished. However, I need to somehow pass that through to the cloud function return value. So put simply, I need to do this...

exports.submitMessage = functions.https.onCall((data, context) => {

var gBody;

request({
    url: URL,
    method: "POST",
    json: true,
    body: queryJSON //A json variable I've built previously
  }, function (error, response, body) {
    gBody = body;
  });

return gBody;
});

(Yes, I am aware of this post... How do I return the response from an asynchronous call? but yeah as I said I need the variable scope to be within the cloud function itself so that I am able to return the value back to the client. Either I don't understand the methods used in that post or it does not accomplish what I am asking)

Frank van Puffelen
  • 565,676
  • 79
  • 828
  • 807

1 Answers1

14

The approach in your last snippet can't work: by the time your return gBody runs the callback from the 3rd party API hasn't been called yet, so gBody is empty.

As the Cloud Functions documentation says:

To return data after an asynchronous operation, return a promise. The data returned by the promise is sent back to the client.

So you just return a promise, and then later resolve that promise with the data from the 3rd party API.

exports.submitMessage = functions.https.onCall((data, context) => {
  return new Promise(function(resolve, reject) {
    request({
      url: URL,
      method: "POST",
      json: true,
      body: queryJSON //A json variable I've built previously
    }, function (error, response, body) {
      if (error) {
        reject(error);
      } 
      else {
        resolve(body)
      } 
    });
  });
});
Frank van Puffelen
  • 565,676
  • 79
  • 828
  • 807
  • Cheers for the help mate! How would I process this promise on the client though? – Bulleyeaccuracy Aug 27 '18 at 03:33
  • 3
    The result of a promise is sent back to the client the exact same way as for a regular `return`. – Frank van Puffelen Aug 27 '18 at 03:46
  • [Edit] Sorry for bad formatting, I'm pretty new to this forum. I've tried doing the following but doesn't work... `submitMessage({message: userMessage{).then(function(result) { result.then(function(result) { console.log(result); }); });` – Bulleyeaccuracy Aug 27 '18 at 03:51
  • 1
    Never mind, I didn't realise the result could be read like a regular JSON variable. `result.data` gives you what was originally retrieved from the 3rd party API. Cheers for your help mate!! – Bulleyeaccuracy Aug 27 '18 at 04:06