mirror of
https://github.com/meteor/meteor.git
synced 2026-05-02 03:01:46 -04:00
data section
Conflicts: docs/client/concepts.html
This commit is contained in:
@@ -84,72 +84,120 @@ for images, `favicon.ico`, `robots.txt`, and anything else.
|
||||
<template name="data">
|
||||
{{#better_markdown}}
|
||||
|
||||
<h2 id="data">Data</h2>
|
||||
<h2 id="data">Data and security</h2>
|
||||
|
||||
Meteor makes writing distributed client code as simple as talking to a
|
||||
local database. It's a clean and simple approach, much easier than
|
||||
building individual RPC endpoints, slow roundtrips to the server, and
|
||||
orchestrating invalidation messages.
|
||||
local database. It's a clean, simple, and secure approach that obviates
|
||||
the need to implement individual RPC endpoints, manually cache data on
|
||||
the client to avoid slow roundtrips to the server, and carefully
|
||||
orchestrate invalidation messages to every client as data changes.
|
||||
|
||||
Every Meteor client includes an in-memory database cache. Each client's
|
||||
cache holds valid copies of some set of documents that are stored in a
|
||||
server's master database. When a matching document in that database
|
||||
changes, Meteor automatically synchronizes that change to every
|
||||
subscribed client.
|
||||
In Meteor, the client and server share the same database API. The same
|
||||
exact application code -- like validators and computed properties -- can
|
||||
often run in both places. But while code running on the server has
|
||||
direct access to the database, code running on the client does *not*.
|
||||
This distinction is the basis for Meteor's data security model.
|
||||
|
||||
To manage the client caches, your server code *publishes* sets of
|
||||
documents, and your client code *subscribes* to those sets. For
|
||||
example, if you are building a chat system, the server might publish two
|
||||
sets: the set of all rooms, and the set of all messages in a given room.
|
||||
Each client would subscribe to the master set of available rooms and the
|
||||
set of messages in the currently-selected room. Once subscribed, the
|
||||
client uses its cache as a fast local database, dramatically simplifying
|
||||
your client model code.
|
||||
{{#note}}
|
||||
By default, a new Meteor app includes the `autopublish` and `insecure`
|
||||
packages, which together mimic the effect of each client having full
|
||||
read/write access to the server's database. These are useful
|
||||
prototyping tools, but typically not appropriate for production
|
||||
applications. When you're ready, just remove the packages.
|
||||
{{/note}}
|
||||
|
||||
Meteor's protocol for distributing document updates is database
|
||||
agnostic. By default, Meteor applications use the
|
||||
familiar [MongoDB API](http://www.mongodb.org/display/DOCS/Manual):
|
||||
servers store documents in MongoDB collections, and clients cache those
|
||||
documents in a client-side cache that implements the same Mongo API for
|
||||
queries and updates.
|
||||
Every Meteor client includes an in-memory database cache. To manage the
|
||||
client cache, the server *publishes* sets of JSON documents, and the
|
||||
client *subscribes* to those sets. As documents in a set change, the
|
||||
server patches each client's cache.
|
||||
|
||||
// server: publish all room documents, and per-room messages
|
||||
Meteor.publish("chatrooms");
|
||||
Each document set is defined by a publish function on the server. The
|
||||
publish function runs each time a new client subscribes to a document
|
||||
set. The data in a document set can come from anywhere, but the common
|
||||
case is to publish a database query.
|
||||
|
||||
// server: publish all room documents
|
||||
Meteor.publish("all-rooms", function () {
|
||||
return Rooms.find(); // everything
|
||||
);
|
||||
|
||||
// server: publish all messages for a given room
|
||||
Meteor.publish("messages", function (roomId) {
|
||||
return Messages.find({room: roomId});
|
||||
});
|
||||
|
||||
// client: subscribe to all rooms, and messages in the first room
|
||||
Meteor.subscribe("chatrooms");
|
||||
Meteor.subscribe("messages", Chatrooms.find()[0]._id);
|
||||
// server: publish the set of parties the logged-in user can see.
|
||||
Meteor.publish("parties", function () {
|
||||
return Parties.find({$or: [{"public": true},
|
||||
{invited: this.userId},
|
||||
{owner: this.userId}]});
|
||||
});
|
||||
|
||||
Document modifications also propagate automatically. Modification
|
||||
instructions like `insert`, `remove`, and `update` are executed
|
||||
immediately on the client's cached data. *At the same time*, the
|
||||
client sends that instruction up to the server, which executes the same
|
||||
change against the master database. Usually the client and server
|
||||
agree, but should they differ (permissions checking or overlapping with
|
||||
another client, for example), the server's result will publish back down
|
||||
to the client. And of course, all other clients with a matching
|
||||
subscription automatically receive an updated document.
|
||||
Publish functions can provide different results to each client. In the
|
||||
last example, a logged in user can only see `Party` documents that
|
||||
are public, that the user owns, or that the user has been invited to.
|
||||
|
||||
// create new message, executes on both client and server.
|
||||
Messages.insert({room: 2413, text: "hello!"});
|
||||
Once subscribed, the client uses its cache as a fast local database,
|
||||
dramatically simplifying client code. Reads never require a costly
|
||||
round trip to the server. And they're limited to the contents of the
|
||||
cache: a query for every document in a collection on a client will only
|
||||
return documents the server is publishing to that client.
|
||||
|
||||
Putting it all together, these techniques accomplish *latency
|
||||
compensation*. Clients hold a fresh copy of the data they need, and
|
||||
never need to wait for a roundtrip to the server. And when clients
|
||||
// client: start a parties subscription
|
||||
Meteor.subscribe("parties");
|
||||
|
||||
// client: return array of Parties this client can read
|
||||
return Parties.find().fetch(); // synchronous!
|
||||
|
||||
Sophisticated clients can turn subscriptions on and off to control how
|
||||
much data is kept in the cache and manage network traffic. When a
|
||||
subscription is turned off, all its documents are removed from the cache
|
||||
unless the same document is also provided by another active
|
||||
subscription.
|
||||
|
||||
When the client *changes* one or more documents, it sends a message to
|
||||
the server requesting the change. The server checks the proposed change
|
||||
against a set of allow/deny rules you write as JavaScript functions.
|
||||
The server only accepts the change if all the rules pass.
|
||||
|
||||
// server: don't allow client to insert a party
|
||||
Parties.allow({
|
||||
insert: function (userId, party) {
|
||||
return false;
|
||||
}
|
||||
});
|
||||
|
||||
// client: this will fail
|
||||
var party = { ... };
|
||||
Parties.insert(party);
|
||||
|
||||
If the server accepts the change, it applies the change to the database
|
||||
and automatically propagates the change to other clients subscribed to
|
||||
the affected documents. If not, the update fails, the server's database
|
||||
remains untouched, and no other client sees the update.
|
||||
|
||||
Meteor has a cute trick, though. When a client issues a write to the
|
||||
server, it also updates its local cache immediately, without waiting for
|
||||
the server's response. This means the screen will redraw right away.
|
||||
If the server accepted the update -- what ought to happen most of the
|
||||
time in a properly behaving client -- then the client got a jump on the
|
||||
change and didn't have to wait for the round trip to update its own
|
||||
screen. If the server rejects the change, Meteor patches up the
|
||||
client's cache with the server's result.
|
||||
|
||||
Putting it all together, these techniques accomplish latency
|
||||
compensation. Clients hold a fresh copy of the data they need, and
|
||||
never need to wait for a roundtrip to the server. And when clients
|
||||
modify data, those modifications can run locally without waiting for the
|
||||
confirmation from the server, while still giving the server final say
|
||||
over the requested change.
|
||||
|
||||
You can substitute another database for MongoDB by providing a
|
||||
server-side database driver and/or a client-side cache that implements
|
||||
an alternative API. The `mongo-livedata` is a good starting point for
|
||||
such a project.
|
||||
|
||||
XXX should we mention security/auth at all here, or just expect folks to keep
|
||||
reading until the accounts section?
|
||||
{{#note}}
|
||||
The current release of Meteor supports MongoDB, the popular document
|
||||
database, and the examples in this section use the
|
||||
[MongoDB API](http://www.mongodb.org/display/DOCS/Manual). Future
|
||||
releases will include support for other databases.
|
||||
{{/note}}
|
||||
|
||||
{{/better_markdown}}
|
||||
</template>
|
||||
@@ -441,7 +489,7 @@ discussion.
|
||||
<template name="packages_concept">
|
||||
{{#better_markdown}}
|
||||
|
||||
<h2 id="smartpackages">Smart Packages</h2>
|
||||
<h2 id="smartpackages">Smart packages</h2>
|
||||
|
||||
Meteor has an unusually powerful package system. All of the
|
||||
functionality you've read about so far is implemented as standard
|
||||
|
||||
@@ -81,11 +81,11 @@ var toc = [
|
||||
],
|
||||
"Concepts", [
|
||||
"Structuring your app",
|
||||
"Data",
|
||||
"Data and security",
|
||||
"Reactivity",
|
||||
"Live HTML",
|
||||
"Templates",
|
||||
"Smart Packages",
|
||||
"Smart packages",
|
||||
"Accounts",
|
||||
"Deploying"
|
||||
],
|
||||
|
||||
Reference in New Issue
Block a user