Meteor - call method from client & server method - meteor

What will happen if from a method that is shared by the client and the server I call another method that is on the server only? Will it get called twice? Only once from the server? Only once from the client?
//lib/methods.js
Meteor.methods({
test: function() {
/*do some stuff that needs to update the UI quickly*/
Meteor.call('doSomeSecureStuff', Meteor.isClient);
}
});
//server/methods.js
import secureStuff from './secureStuff.js';
Meteor.methods({
doSomeSecureStuff: function(originIsClient) {
console.log(originIsClient);
secureStuff();
}
});
From my tests it only gets called once from the server, but since I've found no doc on that I wanted to make sure 1) this is what actually happen and 2) will stay like this in the future
(As suggested by the example, a use case for which I can't just wrap the server part in Meteor.isServer is when I need to load code that is only available on the server)

Yes, only once on the server.
You can wrap the server part of a shared method with this.isSimulation
When you run a shared method it first runs a simulation on the client and then on server - updating the client with its results (which are usually the same - which is why it's called Optimistic UI).

Related

Router.current().route.getName() is returning an error server side

Router.current().route.getName() is returning an error when I use it in a method call (server side method). I thought they say Iron-Router is supposed to work both client and server side. The error I get is
Error invoking Method 'mySeverSideMethod': Internal server error [500]
Please help.
You are half way right, the router works on both client and server. However the server-side implementation is meant for server side routes (eg. REST endpoints). There is no "state" sharing between client/server with iron:router (when invoked inside methods), so Router.current().route.getName() is going to throw you this error, because Router.current() is undefined.
Yes, iron:router can create server side routes, but that api is client only
From the docs:
Router.route('/download/:file', function () {
// NodeJS request object
var request = this.request;
// NodeJS response object
var response = this.response;
this.response.end('file download content\n');
}, {where: 'server'});
You have access to the NodeJS request object so you should be able to find what you need there, e.g. this.request.route, this.request.path.
When calling a Method, you're not going through a 'route' as defined by Iron-Router: it's a route defined by the Meteor framework. It does not care what route the client is on.
So, if you need to know from what page the client is calling the endpoint, you should pass it as a parameter to the Method.
Meteor.methods({
"myEndPoint": function(route) {
// use route here.
return //something
}
})

Does Meteor.call in server side without callback block server?

For example:
server/method.js
Meteor.methods({
insertPost: function(post) {
//call another method
var ret = Meteor.call('longTimeMethod', post.data); // A
// ...
}
})
Meteor doc says
If you do not pass a callback on the server, the method invocation will block until the method is complete.
since nodejs is single thread, if A costs 60 seconds, the whole server will not response to any requests during this 60s?
No, not the whole server will block, only the fiber running for this particular client. If you don't want that, then you can simply call this.unblock() before your method call, and a new fiber will be used for future method calls from that client, see http://docs.meteor.com/#/full/method_unblock.

Call frontend methods from external meteor application

I am making a dockerized services-based application. Some of the services will be written in meteor, some won't.
One of the services is a registration service, where users can register for the platform.
When doing microservices, normally I do the following:
var MyService = DDP.connect(service_url);
var MyOtherService = DDP.connect(other_service_url);
var RegistrationService = DDP.connect(registration_service_url);
What I want to do is use the loginWithFacebook method. The issue is that using Meteor.loginWithFacebook on the frontend will invoke its backend methods on the main frontend server.
However, I want to invoke its backend methods on the RegistrationService server (which has the relevant packages). The reason is because I am using the Accounts.onCreateUser hook to do extra stuff, and also because I want to keep the registration service separate from the frontend.
Just for clarity, even though it is not correct, imagine I have this:
'click #facebook-login': function() {
Meteor.loginWithFacebook(data, callback)
}
However, I want the loginWithFacebook method to use the server-side methods from RegistrationService when calling the client-side method .loginWithFacebook, so I actually want to do something to the effect of the following:
'click #facebook-login': function() {
RegistrationService.loginWithFacebook(data, callback)
}
Any help on this will be greatly appreciated. Thank you!
I believe you are looking for DDP.connect. Basically underneath meteor all calls to the server from the client and all communication from the server to the client use Distributed Data Protocol. (https://www.meteor.com/ddp) As the documentation points out by default a client opens a DDP connection to the server it is loaded from. However, in your case, you'd want to use DDP.connect to connect to other servers for various different tasks, such as a registration services server for RegistrationService. (http://docs.meteor.com/#/full/ddp_connect) As a simplified example you'll be looking to do something like this:
if (Meteor.isClient) {
var registrationServices = DDP.connect("http://your.registrationservices.com:3000");
Template.registerSomething.events({
'click #facebook-login': function(){
registrationServices.call('loginWithFacebook', data, function(error, results){ ... }); // registration services points to a different service from your default.
}
});
}
Don't forget that you can also have various DDP.connect's to your various microservices. These are akin to web service connections in other applications.
You can maybe achieve connection through your other service by specifying the service's remote connection to Accounts and Meteor.users:
var RegistrationService = DDP.connect(registration_service_url);
Accounts.connection = RegistrationService;
Meteor.users = new Meteor.Collection('users',{connection: RegistrationService});
Then would call Meteor.loginWithFacebook and it should use the other app's methods for logging in.

Meteor client side methods

I have a Meteor.method defined on the server side (in .js in /server) and I can call it just fine (with callback) from a client-side template script.
I want to do a similar thing but would like it all to be client side so I moved the method to a client script but the result comes back as 'undefined'.
Template.showDialog.events({
'click #clickme' : function() {
Meteor.call('foo', 'ola', function(error, result) {
alert('here');
alert(result);
});
}
});
Meteor.methods({
foo: function (myarg) {
return myarg+'CLI';
}
});
See the Meteor docs, where it is explained that methods on the client are stubs, not actual methods:
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.
Since the result is ignored, you're seeing undefined. Don't use methods on the client for this purpose. Just use a javascript function.
From the docs:
Calling methods on the client defines stub functions associated with server methods of the same name.
Basically, you need to define the method on the server side. It's also not clear why you'd want to define a method on the client and then call it on the client as well. Would a vanilla javascript function not do the job perfectly well?
Apologies if I've misunderstood what you're trying to achieve here.

Connect to different Hubs on different pages

I am building a single page app which uses sammy.js, knockout.js and SignalR. The main page (index.html) loads additional html pages into a div based upon the client side route.
I have 2 SignalR hubs, one is connected to by the initial page for server side push data and this works fine. However one of the pages which are loaded when the user navigates to it should also connect to a different hub.
In the main page I am doing the following:
window.hubReady = $.connection.hub.start()
var hub1 = $.connection.hub1;
hub1.updateReceived = function () {
alert('data from server');
}
window.hubReady.done(function() {
hub1.server.start();
});
In the second page I have:
var hub2 = $.connection.hub2;
hub2.updateReceived = function () {
alert('data from server');
}
window.hubReady.done(function() {
hub2.server.start();
});
However I never receive any updates in the second page.
Any idea where I am going wrong?
In order to receive updates from a hub you must have at least 1 client side function declared for that hub when the connection is started. Judging from the libraries you are using I'm assuming you have a single page application and therefore don't instantiate your hub2 data until the connection has already started.
So an easy fix would be to just declare a hub2 client side function alongside your hub1 client side function before start is called. If you want to add more client side functions after the connection has started you'll have to use the .on method.
AKA:
hub2.on("updateReceived", function () {
alert("data from server");
});
I have created a lib called SignalR.EventAggregatorProxy.
it proxies between server side domain events and client side code. Its designed with MVVM and SPA in mind and takes care of all the hub plumbing. Check the wiki for how to set it up.
https://github.com/AndersMalmgren/SignalR.EventAggregatorProxy/wiki
once its configured all you need to subscribe to a event is
ViewModel = function() {
signalR.eventAggregator.subscribe(MyApp.Events.TestEvent, this.onTestEvent, this);
};
MyApp.Events.TestEvent corresponds to a server side .NET event. You can also constraint which event should go to which usera

Resources