How to return data from an async meteor method?

I want to make a http call in a meteor method and return the resulting body to the client. I’m using the request NPM on the client and the http call works fine but I can’t seem to figure out how to get the body back to the client. A web search turns up dozens of “answers”, none of which are helpful. Many say “do the http call on the client”. I can’t do that due to cross-origin restrictions and the querystring contains a private API key that I can’t expose on the client. Most of the answers are from 2012 and for Meteor 0.x, I’m looking for something current. Some combination of promises and/or async/await seem likely to be the answer but nothing I’ve tried works. My non-working code follows:

client.html:
<p>{{{TemplateVar.get "SocialMediaLoginText"}}}</p>

client.js:

import { TemplateVar } from 'meteor/frozeman:template-var';

Template.SocialMedia.events({
  'click .js-connectSocialMedia': function(event, template) {
    var service = event.target.attributes['data-original-title'].nodeValue;
    Meteor.call('ConnectSocialMedia', service, function(error, result) {
      if (error) {
        console.log('ERROR: ConnectSocialMedia(' + service + ') returned:', error.message);
        TemplateVar.set(template, 'SocialMediaLoginText', "Error!!");
      } else {
        TemplateVar.set(template, 'SocialMediaLoginText', result);
     }
   });
    return false;
  },  // End of click
});

server.js:

import { Meteor } from 'meteor/meteor'
import { check } from 'meteor/check'

Meteor.methods ({
  async ConnectSocialMedia(service) {
    check(service, String);
    try {
      if (!this.userId) {
        throw new Meteor.Error('500', 'Must be logged in to connect a service.');
      }

      if (service == 'Disqus') {
        var request = require('request');

        var options = {
          url: 'https://disqus.com/api/oauth/2.0/authorize/',
          qs: { client_id: 'myClientID',
                scope: 'read',
                response_type: 'code',
                redirect_uri: 'MyCallbackURL'
          },
          headers: {
            'X-accountId': this.userId
          }
        };

        await request.get(options, function(error, response, body) {
          if (!error && response.statusCode == 200) {
             return body;
          }
        });
      } else {  // End of Disqus
        return "Unknown service: " + service;
      }
    } catch (exception) {
      throw new Meteor.Error('500', exception.message);
    }

    return "xxx";
  },
});

The async and await keywords make no difference. I’ve also tried using the request-promise npm but that doesn’t do what I need either. Can anyone give me a definitive method with a real-world example on how to solve this problem? I’m about ready to try using raw websockets as a solution :frowning:

1 Like

What about using the synchronous fiber version of HTTP instead?

Meteor.methods({
  'HTTP.get'(url){
    let result = HTTP.get(url)
    return result
  }
})
1 Like

Here is the answer and a solution to your question: https://stackoverflow.com/questions/38428027/why-await-is-not-working-for-node-request-module

TL;DR You can only await something that returns a promise.

2 Likes

Thanks @tomsp, now I understand the problem. Switching from “await request” to “await request-promise” works as expected and solves my problem. Thanks.

Duh!!! I had forgotten (if I ever actually knew) that there was a synchronous version of HTTP.get on the server side. Works like a charm. Thanks @herteby.