Throughout my prior experience with web development, I've always been afloat the LAMP stack. As of recent I've become infatuated with the MEAN stack, and have built a couple of neat little applications. Moving onward, I heard Meteor mentioned over an IRC chat and decided to jump on the bandwaggon.
For the past week, I've been trying to set up an application within Meteor. I start planning concise structures and documenting control flow, but when it gets to the actual application, I start to lose myself during development. My problem standing, is I don't truly understand isomorphic javascript.
Using Meteor, when exactly is server code executed? Server code is beyond the scope of the client, as it should be for security purposes, but then how is it actually isomorphic? I know of mongo-client, and I find it pretty nifty. Though how exactly should I be structuring an application within Meteor?
Let's say I'm creating a controller for "users", and this controller will be loaded into two possible solutions.
./server/app.js
$scope.controller = { };
if( !$scope.session.exists )
{
$scope.controller =
{
authenticate: function(email, password) {
// authenticate credentials and create session
},
create: function(form) {
// create new user
}
};
}
else
{
$scope.controller =
{
end: function(call) {
try {
call();
} catch(err) {
// do nothing, optional callback
}
// end user session
}
};
}
How will the above code work in coherence with my client code? If I create a simple login form
./client/login.html
<template name="login">
<form>
<input type="text" name="email" placeholder="Email Address">
<input type="password" name="password" placeholder="Password">
<button type="submit">Login</button>
</form>
</template>
./client/login.js
Template.login.events({
// handle the form submission
'submit form': function(event) {
// stop the form from submitting
event.preventDefault();
// ???
// event.target.email.value
// event.target.password.value
}
});
How will this template event make use of $scope.controller.authenticate? Well, it can't, because authenticate is exclusively on the server. Hence, I would need to move the authentication process to the client, which wouldn't make any sense, therefore making my brain numb at the ideology of isomorphic javascript.
I know these questions are somewhat vague, but to put it short and simple, what is the extent of the relationship between server sided and client sided code within Meteor?
Foreword: It seems that you are using Angular on the backend, but Meteor is supposed to be self sufficient out of the box (you can of course integrate other frameworks like Angular, but it is sometimes hacky and not required).
In Meteor the "communication" between client and server works in two ways:
DB reactivity: Depending on your allow/deny rules for each Collection, the client can directly interact with the client side DB (miniMongo). Such interactions (delete, update, etc) will be replicated on the server DB (and validated against your allow/deny rules for security). Be careful, any app comes with the insecure package installed which allows for total control on the DB from the client (for prototyping purpose, but this can be confusing).
Methods (best way IMO, and complementary): This is the part where Meteor may be truly isomorphic. Methods can be declared on server only, to be accessed from the client, or on both client and server (in a file located outside /client and /server for example). In the first case the client will call the method and wait for the answer from the server in the Meteor.call callback, in the second case, the client will call the method but the method will be run on both client and server, server actually doing the job and client simulating the method in order to compensate the latency between the client and the server (and give the impression of instantaneity to the user). If the result on the Server is different (i.e. different data after modification) the client's method result will be invalidated and reverted.
Methods work like below (example without latency compensation), and I think it is what you are currently looking for:
//SERVER
Meteor.methods({
doSomething: function (params) {
//method logic
},
doSomethingElse: function (params) {
//method logic
}
});
//CLIENT
Meteor.call('doSomething', params, function (error, result) {
//get the error or the result of the call
});
You have to remember that Meteor is really about data synchronization through publish/subscribe. lots of what you will do inside methods will be translated in a DB modification and then synchronized to the client through reactivity (provided that these data are published on the client).
So depending on your publications/subscriptions you may not need to handle servers calls and results like in a usual application, i.e. you may call the method and do nothing else as the data will be automatically synchronized through a publication.
Related
This a snippet from the todo list tutorial. Variable checked is represented both on the client and the server side? How the client and the server communicate to make checked consistent?
Template.task.events({
'click .toggle-checked'() {
// Set the checked property to the opposite of its current value
Tasks.update(this._id, {
$set: { checked: ! this.checked },
});
},
'click .delete'() {
Tasks.remove(this._id);
},
});
checked is an attrubite defined on a Tasks object, as defined in this app.
In Meteor, the definitive record of this object is stored on the server (in MongoDB), however there is a client side cache that is also being manipulated here, known as MiniMongo. The Meteor framework does a lot of work in the background (via the DDP protocol) to keep the server and client side objects in sync.
In this case the following is happening when a user clicks on a checkbox (firing the 'click .toggle-checked' event code) in the Tasks.update method:
First update client side MiniMongo Cache - this is known as Optimistic UI, and enables the client UI to respond fast (without waiting for the server)
Send a message to the server (Meteor Method) that the client wants to update the Tasks object, by setting the clicked variable to a new value.
Message requesting update received by server, which checks this is a valid operation, and either processes it (updating MongoDB version of the Tasks object, or refuses to process the update as appropriate.
Server will send out a DDP update of the resulting status of the Tasks object to all clients that have subscribed to a publication that includes it.
Clients that have previously subscribed will receive this DDP update, and will replace their MiniMongo version with the Server's version of the Tasks object, ensuring that all Clients are in sync with the Server.
Now in the ideal case, when the server accepts the clients changes, the new version of Tasks received (in step 5) by the initiating client will match the object it optimistically updated (in step 1).
However by implementing all these steps the Meteor framework also synchronizes other clients, and handles the case when the server rejects the update, or possibly modifies additional fields, as appropriate for the application.
Luckily though, this is all handled by the Meteor framework, and all you need to do is call Tasks.update for all this magic to happen!
Meteor likes the blur the lines between client and server. There are things you can do to abstract code -- for instance, javascript files (among all files) inside the /server directory to restrict access to it. This means that client users can't see this code.
/client obviously is the opposite. You can check a file with isClient and isServer.
Now, what does this mean to your code?
Depending on where your code is, there are different access levels. However, inside the script, there basically is no difference. checked is known on server/client inside that script because that's how Meteor runs, the blurred line between client and server makes this possible.
Meteor employs something called "database everywhere" which means it doesn't matter where the code is called, because it will run.
I'm just trying to verify if an Account exists with a particular email, however I learned that Accounts.findUserByEmail() only works server-side.
It would appear the repeatedly-suggested way would be to define a Meteor.method() and do all the work in there. Unfortunately I apparently have no idea what I'm doing because I'm getting an error that no one else has been getting.
component.js:
Meteor.call('confirm', email);
methods.js:
Meteor.methods({
'confirm': (email) => {
if (Accounts.findUserByEmail(email)) {
return;
}
}
});
All I get is this error:
Exception while simulating the effect of invoking 'confirm' TypeError: Accounts.findUserByEmail is not a function
Am I completely misunderstanding the dynamic of Meteor.methods + Meteor.call? Is it not actually server-side??
Currently using Meteor package, accounts-password#1.3.3
Meteor simulates method calls in the front-end too by running "stubs" of your methods. The idea is to have a better user experience because the UI is updated immediately before the server has responded. However this also means that if you run server-only code in Meteor methods, you have to make sure that the code is only run on the server:
Meteor.methods({
'confirm': (email) => {
if (Meteor.isServer && Accounts.findUserByEmail(email)) {
return;
}
}
});
Alternatively, you can place the above method definition in a file that is only loaded on the server, like any file on the /server-directory or (recommended) in /imports to a file that is only included by server code. Then you shouldn't need to use Meteor.isServer separately.
If your client-side code includes a method definition, it is treated as a stub, which means that it is run in a special mode that provides "optimistic UI" and its effects on data are undone once the actual server method returns its response to the client.
It could be worthwhile to implement different versions of (at least some of the) methods for the client and server, and to avoid including some of them on the client altogether.
If you choose to use the same function on both the client and the server, there are Meteor.isServer, Meteor.isClient and this.isSimulation (the latter is specifically for the methods), that allow you to execute some of the blocks only on the client/server.
Note that the code in your question does not do what you expect it to, and you do not check the method argument.
For this specific use case, you should probably only implement the method on the server (simply don't import its code in your client build):
Meteor.methods({
isEmailInSystem(email) {
check(email, String);
return !!Accounts.findUserByEmail(email);
}
});
You can read more about the method lifecycle in The Meteor Guide.
From the guide (gist, some details omitted):
Method simulation runs on the client - If we defined this Method in client and server code, as all Methods should be, a Method simulation is executed in the client that called it.
The client enters a special mode where it tracks all changes made to client-side collections, so that they can be rolled back later. When this step is complete, the user of your app sees their UI update instantly with the new content of the client-side database, but the server hasn’t received any data yet.
A method DDP message is sent to the server
Method runs on the server
Return value is sent to the client
Any DDP publications affected by the Method are updated
updated message sent to the client, data replaced with server result, Method callback fires
After the relevant data updates have been sent to the correct client, the server sends back the last message in the Method life cycle - the DDP updated message with the relevant Method ID. The client rolls back any changes to client side data made in the Method simulation in step 1, and replaces them with the actual changes sent from the server in step 5.
Lastly, the callback passed to Meteor.call actually fires with the return value from step 4. It’s important that the callback waits until the client is up to date, so that your Method callback can assume that the client state reflects any changes done inside the Method.
Let's say I want to create a ToDo list using angular. I have a REST API that stores the items in db and provides basic operations. Now when I want to connect my angular app to the REST api I found two ways to do so following some tutorials online:
1.Data gets handled in backend only: A service gets created that has a getAllTodos function. This function gets directly attached to scope (e.g. to use it in ng-repeat):
var getAllTodos = function() {
//Todo: Cache http request
return $http...;
}
var addTodo = function(todo) {
//Todo: Clear cache of getAllTodos
$http...
}
2.Data gets handled in frontend too. There is a init function that initializes the todo variable in the service.
var todos = [];
var init = function() {
$http...
todos = //result of $http;
};
var getAllTodos = function() {
return todos;
};
var addTodo = function(todo) {
$http...
todos.push(todo);
}
I've seen both ways in several tutorials but I'm wondering what would be the best way? The first one is used in many tutorials where the author from the start has in mind to attach it to a REST API. The second one is often used when the author at first wants to create the functionality in the frontend and later wants to store data permanently using a backend.
Both ways have its advantages and disadvantages. The first one reduces code duplication in frontend and backend, the second one allows faster operations because it can be handled frontend first and the backend can be informed about changed afterwards.
//EDIT: Frontend is Angular.JS Client for me, backend the REST API on the server.
Separation of Frontend and Backend is often done for security reasons. You can locate Backend on a separate machine and then restrict access to that machine to only calls originating from the Frontend. The theory is that if the Frontend is compromised, the Backend has a lower risk factor. In reality if someone has compromised any machine on your network then the entire network is at risk on one level or another.
Another reason for a Backend/Frontend separation would be to provide database access through the Backend to multiple frontend clients. You have a single Backend with access to the DB and either multiple copies of the Frontend or different Frontends that access the Backend.
Your final design needs to take into account the possible security risks and also deployment and versioning. With the multiple-tier approach you can deploy individual Frontends without having to drop the Backend, and you can also "relocate" parts of the application without downtime. The more flexible the design of your application, the deployment may be more complicated. The needs of your application will depend on if you are writing a simple Blog or a large Enterprise application.
You need frontend and backend functionality. In frontend you preprape data which are being send and in the backend you make request to server.
All meteor methods can be called same way from client and server side.
Let's say user knows or can predict all the method names on server, then he is able to call them and use it's result however he want.
example:
A method which performs cross domain http request and return response can be used to overload server by calling huge amounts of data Meteor.call(httpLoad, "google.com");, or a method which load data from mongo can be used to access database documents if the client know document _id Meteor.call(getUserData, "_jh9d3nd9sn3js");.
So, how to avoid this situations, may be there is a better way to store server-only functions than in Meteor.methods({...})?
Meteor methods are designed to be accessed from the client, if you don't want this, you just need to define a normal javascript function on the server. A really basic example would be:
server/server.js:
someFunction = function(params) {
console.log('hello');
}
As long as it's in the server folder, the function won't be accessible from the client.
For coffeescript users, each file is technically a separate scope, so you would have to define a global variable with #, e.g.
#someFunction = (params) ->
console.log 'hello'
or if you want to scope the function to a package:
share.someFunction = (params) ->
console.log 'hello'
If you have methods that need to be accessible from the client but only for say admin users, you need to add those checks at the start of the meteor method definition:
Meteor.methods({
'someMethod': function(params) {
var user = Meteor.user();
if (user && (user.isAdmin === true)) {
// Do something
} else {
throw new Meteor.Error(403, 'Forbidden');
}
}
});
I'm not going to vouch for the security of this example - it's just that, an example - but hopefully it gives you some idea of how you would secure your methods.
EDIT: Noticed the other answers mention using a if (Meteor.isServer) { ... } conditional. Note that if you are doing this inside methods which are also accessible on the client, the user will be still be able to see your server code, even if they can't run it. This may or may not be a security problem for you - basically be careful if you're hardcoding any 3rd-party API credentials or any kind of sensitive data in methods whose code can be accessed from the client. If you don't need the method on the client, it would be better to just use normal JS functions. If you're wrapping the whole Meteor.methods call with a isServer conditional, the code will be on the server only, but can still be called from the client.
as rightly stated in other answers, your methods will always be accessible from the client (per design). yet, there is a simple workaround to check if the call originates from the client or from the server. if you do a
if ( this.connection == null )
this will return true if the method was called from server. like that you can restrict the method body execution to 'secure' calls.
I think this page explains it: http://meteortips.com/first-meteor-tutorial/methods/
I'm quoting:
"The safer approach is to move these functions to the isServer conditional, which means:
Database code will execute within the trusted environment of the server.
Users won’t be able to use these functions from inside the Console, since users don’t have direct access to the server.
Inside the isServer conditional, write the following:
Meteor.methods({
// methods go here
});
This is the block of code we’ll use to create our methods."
and so on. I hope this helps.
With proper app design, you shouldn't care whether a request was through the web UI or via something typed in a console window.
Basically, don't put generic, abuse worthy functions in Meteor.methods, implement reasonable access controls, and rate-limit and/or log anything that could be a problem.
Any server-side function defined in Meteor.methods will have access to the current user id through this.userid. This userid is supplied by Meteor, not a client API parameter.
Since that Meteor Method server-side code knows the login status and userid, it can then do all the checking and rate limiting you want before deciding to do that thing that the user asked it to do.
How do you rate limit? I've not looked for a module for this lately. In basic Meteor you would add a Mongo collection for user actions accessible server-side only. Insert timestamped, userid specific data on every request that arrives via a Meteor method. Before fulfilling a request in the server method code, do a Mongo find for how many such actions occurred from this userid in a relevant period. This is a little work and will generates some overhead, but the alternative of rate-limiting via a server-wide underscore-style debounce leaves a function open for both abuse and denial-of-service by an attacker.
I just read this post, and I do understand what the difference is. But still in my head I have the question. Can/Should I use it in the same App/Website? Say I want the AngularJs to fetch content and update my page, connecting to a REST api and all of that top stuff. But on top of that I also want a realtime chat, or to trigger events on other clients when there is an update or a message received.
Does Angular support that? Or I need to use something like Socket.io to trigger those events? Does it make sense to use both?
If someone could help me or point me to some good reading about that if there is a purpose for using both of them together.
Hope I'm clear enough. thank you for any help.
Javascript supports WebSocket, so you don't need an additional client side framework to use it. Please take a look at this $connection service declared in this WebSocket based AngularJS application.
Basically you can listen for messages:
$connection.listen(function (msg) { return msg.type == "CreatedTerminalEvent"; },
function (msg) {
addTerminal(msg);
$scope.$$phase || $scope.$apply();
});
Listen once (great for request/response):
$connection.listenOnce(function (data) {
return data.correlationId && data.correlationId == crrId;
}).then(function (data) {
$rootScope.addAlert({ msg: "Console " + data.terminalType + " created", type: "success" });
});
And send messages:
$connection.send({
type: "TerminalInputRequest",
input: cmd,
terminalId: $scope.terminalId,
correlationId: $connection.nextCorrelationId()
});
Usually, since a WebSocket connection is bidirectional and has a good support, you can also use it for getting data from the server in request/response model. You can have the two models:
Publisher/Subscriber: Where the client declares its interest in some topics and set handlers for messages with that topic, and then the server publish (or push) messages whenever it sees fit.
Request/response: Where the client sends a message with a requestID (or correlationId), and listen for a single response for that requestId.
Still, you can have both if you want, and use REST for getting data, and WebSocket for getting updates.
In server side, you may need to use Socket.io or whatever server side framework in order to have a backend with WebSocket support.
As noted in the answer in your linked post, Angular does not currently have built-in support for Websockets. So, you would need to directly use the Websockets API, or use an additional library like Socket.io.
However, to answer your question of if you should use both a REST api and Websockets in a single Angular application, there is no reason you can't have both standard XmlHttpRequest requests for interacting with a REST api, using $http or another data layer library such as BreezeJS, for certain functionality included in various parts of the application and also use Wesockets for another part (e.g. real time chat).
Angular is designed to assist with handling this type of scenario. A typical solution to would be to create one or more controllers to handle the application functionality and update your page and then creating separate Services or Factories that encapsulate the data management of each of your data end points (i.e. the REST api and the realtime chat server), which are then injected into the Controllers.
There is a great deal of information available on using angular services/factories for managing data connections. If you're looking for a resource to help guide you on how to build an Angular application and where data services would fit in, I would recommend checking out John Papa's AngularJS Styleguide, which includes a section on Data Services.
For more information about factories and services, you can check out AngularJS : When to use service instead of factory