Error when calling http method from client in meteor - meteor

I am trying to consume a REST API in the meteor application. Inside the server.js file which is in the server folder, I have written this code:
Meteor.methods({
checkTwitter: function () {
this.unblock();
return Meteor.http.call("GET", "http://search.twitter.com/search.json?q=perkytweets");
}
});
Inside the client.js file, which is in the client folder, I have written down this code:
Meteor.call("checkTwitter", function(error, results) {
console.log(results.content); //results.data should be a JSON object
});
I get this error message in console:
"Exception while simulating the effect of invoking 'checkTwitter' Error: Can't make a blocking HTTP call from the client; callback required".
I have the callback function defined in client due to which I don't understand this error. What is it that I am doing wrong?

Meteor.http has been deprecated, please see the HTTP package.

I think that since there's a stub, "checkTwitter" will actually also run on the client. Once the server returns, its result will overwrite the result from teh client run.
In this case, since Meteor.http.call can't run on the client without a callback, you get the error.
Try changing:
Meteor.methods({
checkTwitter: function () {
this.unblock();
return Meteor.http.call("GET", "http://search.twitter.com/search.json?q=perkytweets");
}
});
With
Meteor.methods({
checkTwitter: function () {
if (Meteor.isServer) {
this.unblock();
return Meteor.http.call("GET", "http://search.twitter.com/search.json?q=perkytweets");
}
}
});

Related

Meteor.call in client doesn't wait when the called method has another call inside

I am following what I've read around being the standard way to use Meteor.call but it's behaving strangely in this scenario:
Client:
Template.sometemplate.events({
'submit .somebutton'(event){
...
Meteor.call('stuff.someMethod', param1, function (err, res){
console.log(err);
console.log(res);
};
}
})
Server /api/stuff.js:
Meteor.methods({
'stuff.someMethod'(param1){
...
Meteor.call('otherstuff.someOtherMethod', param1, function(err, res){
if(err){ throw new Meteor.Error(400,'wrong things');}
if(res) { return 'ok';}
}
);
}
})
Server /api/otherstuff.js:
Meteor.methods({
'otherstuff.someOtherMethod'(param1){
...
return OtherStuff.findOne(query);
}
})
On the client side I click and immediately see the console.log for both err and res as undefined. Whereas in other parts of the application when the client calls a server method, which is not calling another method, the client waits for the answer before executing the asynch callback.
There must be something wrong in how I use the Meteor.call inside a server method calling another server method. The scenario is that for instance I want to insert a document and while doing so I want to check some values in order to link it to other documents from other collections.
Thank you very much,
T.
Sync call on the server
Using Meteor.call on the server does not require a callback, unless you really want to work async on the server side.
If you do not pass a callback on the server, the method invocation
will block until the method is complete. It will eventually return the
return value of the method, or it will throw an exception if the
method threw an exception. (Possibly mapped to 500 Server Error if the
exception happened remotely and it was not a Meteor.Error exception.)
Instead of passing a callback you would either return the result
return Meteor.call(...)
or assign it to a variable that is used for further processing.
const retVal = Meteor.call(...)
Better way: Externalize shared code
If two meteor methods rely on the same code (e.g. one is calling the other) you should extract this code into a shared function. This makes testing and tracing errors also easier.
server/api/common.js
export const sharedFunction = function(param1) {
// ... do somethin
return OtherStuff.findOne(query);
}
server/api/stuff.js:
import { sharedFunction } from './common.js';
Meteor.methods({
'stuff.someMethod'(param1){
// ...
const temp = sharedFunction(param1);
// ...
return result; // or temp if this should be returned to client
}
})
server/api/otherstuff.js
import { sharedFunction } from './common.js';
Meteor.methods({
'otherstuff.someOtherMethod'(param1){
return sharedFunction(param1);
}
});
Using the sharedFunction follows the concepts of DRY and Single Point of Failure.

Inside server method call, this.connection returns undefined

This Meteor server code is expected to console.log the client header but the connection object is undefined.
How can I get the client header inside the server code? Thanks
//server.js
Meteor.methods({
'someName': () => {
let header = this.connection.httpHeaders;
console.log(this.connection);
}
});
// client.js
Meteor.call('someName', params, (err, res) => {
}
);
You used an arrow function as a method callback. Arrow functions bind the value of this, and Meteor cannot override it when your method gets called. Quite a frustrating aspect of JavaScript, I know.
Fix it using a regular JavaScript function:
Meteor.methods({
'someName': function() {
let header = this.connection.httpHeaders;
console.log(this.connection);
}
});

Why client does not instantly return from client method?

client method
Meteor.methods({
insertPost: function(data) {
console.log('client')
Posts.insert(data, function(err, ret) {
console.log('client insert end')
});
},
});
server method
Meteor.methods({
insertPost: function(data) {
console.log('server')
Meteor._sleepForMs(200000);
Posts.insert(data, function(err, ret) {
console.log('server insert end')
});
},
});
client submit
'click #save': function(e) {
// data = ....
Meteor.call('insertPost', data, function(error) {
Router.go('/');
});
},
Why does client stuck at form page, instead of instantly going to '/'.
Here is the meteor documentation about it.
Calling methods on the client defines stub functions associated with
server methods of the same name. You don't have to define a stub for
your method if you don't want to. In that case, method calls are just
like remote procedure calls in other systems, and you'll have to wait
for the results from the server.
If you do define a stub, when a client invokes a server method it will
also run its stub in parallel. On the client, the return value of a
stub is ignored. Stubs are run for their side-effects: they are
intended to simulate the result of what the server's method will do,
but without waiting for the round trip delay. If a stub throws an
exception it will be logged to the console.

Meteor.call and latency compensation

I'm trying to understand why am I getting the error when calling a meteor server method. It works on the server side but it's throwing errors in the browser.
This is my server code in /server/methods.js file:
Meteor.methods({
getTicketSettings: function(){
var getTicketConfig = function(callback){
Assets.getText('ticketCustomizing.json', function(error, res){
if (error)
throw new Meteor.Error({error:'ticket-getCustomizing', reason:'No se pudo recuperar la configuraciĆ³n.'});
else callback && callback(null, JSON.parse(res));
});
}
var syncAssetRetrieve = Meteor.wrapAsync(getTicketConfig);
var result = syncAssetRetrieve();
return result;
},
});
And this is in my client/server code in /lib/initialization.js file:
App.config.tickets.tipos = new Mongo.Collection('tipos');
Meteor.startup(function(){
moment.locale('es');
var ticketSettingsObj = Meteor.call('getTicketSettings');
console.log(ticketSettingsObj);
_.map(ticketSettingsObj.tipos, function(tipo){
App.config.tickets.tipos.insert(tipo);
});
});
When I run my application I have the JSON object logged in the console but the browser is showing this error: Uncaught TypeError: Cannot read property 'tipos' of undefined in my /lib/initialization.js here:
_.map(ticketSettingsObj.tipos, function(tipo){
App.config.tickets.tipos.insert(tipo);
});
Obviously I misunderstood something but still wondering...
You need to pass a callback to the Meteor.call. The server can run it synchronously, blocking until it gets a return, but the client cannot so ticketSettingsObj will always be undefined.
See Meteor docs
Without error handling (and untested):
Meteor.call('getTicketSettings', function(error, result){
console.log(result);
_.map(result.tipos, function(tipo){
App.config.tickets.tipos.insert(tipo);
});
});
Do a console.log(App.config.tickets) and see if it returns a valid object. If it doesn't then you have defined the object App.config.tickets only on server side. If this is intentional and you only want this to be accessible on server side then then add a if(Meteor.isServer) or move the definition it to a file inside /server directory.

Writing/Converting Meteor Synchronous Functions

This has been bothering me for a while so I thought I'd just do a quick QA on it:
If one has a normal nodeJS module or something and it has a async function on the server side. How do I make it synchronous. E.g how would I convert the nodejs fs.stat asynchronous function to a synchronous one.
e.g I have
server side js
Meteor.methods({
getStat:function() {
fs.stat('/tmp/hello', function (err, result) {
if (err) throw err;
console.log(result)
});
}
});
If I call it from the client I get back undefined as my result because the result is in a callback.
There is a function (undocumented) called Meteor.wrapAsync.
Simply wrap the function up
Meteor.methods({
getStat:function() {
var getStat = Meteor._wrapAsync(fs.stat);
return getStat('/tmp/hello');
}
});
Now you will get the result of this in the result of your Meteor.call. You can convert any async function that has a callback where the first parameter is an error and the second the result.

Resources