Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Return Meteor.http results in method

I have a Meteor method that wraps around an http.get. I am trying to return the results from that http.get into the method's return so that I can use the results when I call the method.
I can't make it work though.

Here's my code:

(In shared folder)

Meteor.methods({
    getWeather: function(zip) {
        console.log('getting weather');
        var credentials = {
            client_id: "string",
            client_secret: "otherstring"
        }

        var zipcode = zip;

        var weatherUrl = "http://api.aerisapi.com/places/postalcodes/" + zipcode + "?client_id=" + credentials.client_id + "&client_secret=" + credentials.client_secret;

        weather = Meteor.http.get(weatherUrl, function (error, result) {
            if(error) {
                console.log('http get FAILED!');
            }
            else {
                console.log('http get SUCCES');
                if (result.statusCode === 200) {
                    console.log('Status code = 200!');
                    console.log(result.content);

                    return result.content;
                }
            }
        });
        return weather;
    }
});

For some reason, this does not return the results even though they exist and the http call works: console.log(result.content); does indeed log the results.

(Client folder)

  Meteor.call('getWeather', somezipcode, function(error, results) {
     if (error)
        return alert(error.reason);

     Session.set('weatherResults', results);
  });

Of course here, the session variable ends up being empty.
(Note that this part of the code seems to be fine as it returned appropriately if I hard coded the return with some dummy string in the method.)

Help?

like image 912
oliv23 Avatar asked Sep 04 '14 20:09

oliv23


3 Answers

In your example Meteor.http.get is executed asynchronously.

See docs:

HTTP.call(method, url [, options] [, asyncCallback])

On the server, this function can be run either synchronously or asynchronously. If the callback is omitted, it runs synchronously and the results are returned once the request completes successfully. If the request was not successful, an error is thrown

Switch to synchronous mode by removing asyncCallback:

try {
  var result = HTTP.get( weatherUrl );
  var weather = result.content;
} catch(e) {
  console.log( "Cannot get weather data...", e );
}
like image 150
Kuba Wyrobek Avatar answered Nov 10 '22 16:11

Kuba Wyrobek


Kuba Wyrobek is correct, but you can also still call HTTP.get asynchronously and use a future to stop the method returning until the get has responded:

var Future = Npm.require('fibers/future');

Meteor.methods({
    getWeather: function(zip) {
        console.log('getting weather');
        var weather = new Future();
        var credentials = {
            client_id: "string",
            client_secret: "otherstring"
        }

        var zipcode = zip;

        var weatherUrl = "http://api.aerisapi.com/places/postalcodes/" + zipcode + "?client_id=" + credentials.client_id + "&client_secret=" + credentials.client_secret;

        HTTP.get(weatherUrl, function (error, result) {
            if(error) {
                console.log('http get FAILED!');
                weather.throw(error);
            }
            else {
                console.log('http get SUCCES');
                if (result.statusCode === 200) {
                    console.log('Status code = 200!');
                    console.log(result.content);

                    weather.return(result);
                }
            }
        });
        weather.wait();
    }
});

There's not really much advantage to this method over a synchronous get in this case, but if you're ever doing something on the server which can benefit from something like an HTTP call running asynchronously (and thus not blocking the rest of the code in your method), but you still needs to wait for that call to return before the method can, then this is the right solution. One example would be where you need to execute multiple non-contingent gets, which would all have to wait for each other to return one by one if executed synchronously.

More here.

like image 37
richsilv Avatar answered Nov 10 '22 16:11

richsilv


Sometimes asynchronous calls are preferable. You can use async/await syntax for that, and you need to promisify HTTP.get.

import { Meteor } from 'meteor/meteor';
import { HTTP } from 'meteor/http';

const httpGetAsync = (url, options) =>
    new Promise((resolve, reject) => {
        HTTP.get(url, options, (err, result) => {
            if (err) {
                reject(err);
            } else {
                resolve(result);
            }
        });
    });

Meteor.methods({
    async 'test'({ url, options }) {
        try {
            const response = await httpGetAsync(url, options);
            return response;
        } catch (ex) {
            throw new Meteor.Error('some-error', 'An error has happened');
        }
    },
});

Notice that meteor test method is marked as async. This allows using await operator inside it with method calls which return Promise. Code lines following await operators won't be executed until returned promise is resolved. In case the promise is rejected catch block will be executed.

like image 1
humkins Avatar answered Nov 10 '22 18:11

humkins