React-ive Meteor
http://react-ive.meteor.com
Building a realtime backend and clientside optimistic UI for React can be difficult. Meteor makes it easy to build an app with optimistic UI updating and realtime data streaming (view updates as DB records change).
TL;DR
This repo aims to be a sandbox that developers can learn how to create React apps with a Meteor backend. We'll have several branches with different ways to architect/use the app (such as a full flux spec).
Meteor with Flux or Relay type experiments can be a bit complicated so the master branch will be the most simple version that beginners can pickup and start running.
The Meteor Development Group is working on making a deeper and more seamless integration with Meteor and React. Checkout the progress Here. The master branch is using the preview release of this package.
#### Benefits of using React with Meteor
- Automatic optimistic UI updates (latency compensation)
- Universal JS shared on client and server (isomorphic)
- Real time database updates (like Uber's realtime driver location)
- Hot code reloads
- Mini-Mongo client data store (easy clientside querying)
- Build system for preprocessing Sass, Babel, etc...
- Clean server-side code with fibers (soon with promise ES7 async/await)
- Query based data subscriptions instead of REST (also API REST supported)
- Easy microservice implementations via DDP
- Modular, swap out any default components you wish
Cons of using Meteor
- No live-query support for SQL (non-reactive SQL supported via GraphQL)
- No client-side file import system yet (v 1.3 will change this)
Fallacies of Meteor
- Not secure - This has been resolved for quite a while... try to modify someone else's post!
- Not scalable - Many companies are using Meteor with large user bases
- Only supports Mongo - With GraphQL any promise based driver will work
- Meteor is only for realtime streams - works well with without realtime updates too!
These cons are on the core roadmap and will be resolved in the near future. However some of those could be blockers for your project.
Usage
curl https://install.meteor.com/ | sh
more infomake dev
ormeter --settings '.config/devel/settings.json'
## Data
Fetching data with Meteor is quite different than a traditional REST system. Instead of making requests to single resource endpoints, you can subscribe to one or more publications of data. If this data in the database changes, you'll receive the new data in the client (instantly when using Mongo's Oplog). Using the MeteorMixin, this new data is synced with your subscription/state which re-renders your views.
This new data is sent into a store called Mini-Mongo. It acts like an in memory Mongo database which turns out to be a really convenient way to access store data. Since this is in memory, ops are very fast (basically a key/value lookup). If you were using the PostgreSQL database you would use SQL to query the Mini-PostgreSQL store instead.
If you prefer a flux architecture, you can use Tracker to watch for changes and then emit a changed event.
The whole data cycle looks like this:
- grandparent component renders children components
- children tell grandparent which data/fields they need
- grandparent subscribes to data all at one time
- server publishes data to client if authorized
- client accepts data and inserts into Mini-Mongo client store
- grandparent receives message that data they care about changed
- grandparent triggers re-render (via Meteor mixin)
- data flows from grandparent down to all children
New Meteor users can get tripped up in this process by sending too much data to the client, causing slowdown. This project uses a GraphQL/Relay inspired subscription to help prevent over publishing data. Each component specify what fields they need and their grandparent takes care of the actual query. This grandparent pushes down new data through their props. This makes testing very easy as the fetching only happens in one place. This data fetching system is a first draft so it may change some.
Views
The master branch of this repo (and live example) use a mixture of Meteor's Blaze (handlebars like syntax) templates and React templates. Blaze is ideal for content that has no state and very little helpers. These large pages (like the about example) would be cumbersome to use in JSX.
In this setup, the router renders 'page' templates which have the sole responsibility of handling static markup and rendering the topmost React components. These Blaze pages don't subscribe to data, that responsibility is for the topmost components. Example Code
The rendering path looks something like this:
- route /feed , routes.js render static
client/pages/feed.html
template - static
feed
template renders FeedList and CreatePost React components - FeedList sets up data pub/sub and renders children
- CreatePost renders children
- view is ready
However if your app looks very 'app like' (Spotify, Slack, etc...) and not 'page like', using 100% React views is a better approach. See the full-react
branch (soon) to see how you can render React views in using the React-Router module.
## Meteor Methods
Meteor provides an RPC style method that can be called on the client or on the server (even other non servers with a DDP adapter). You simply write a method on the server and on the client you call Meteor.call('multiply', 2, 5);
. On the server the call would directly return 10
because we have fibers. On the client we can't block so the last argument would be a callback with params error
and response
. You also have access to a few resources inside the method like this.userId
. It will contain the caller's userId if they are authenticated. Meteor uses these methods under the hood for the Authors.insert({name: 'foo'})
calls. However we're using our own model methods to bypass the hard to reason allow/deny rules. The Meteor methods turn out to be pretty good at standing in for a Flux Dispatcher.
## Models
The implementation of data models used in this project is just one of many ways to do it. Meteor does not have an ORM or Model layer so you're up to rolling your own or using a package. There are a few nice model packages but they have lots of magic involved. For simplicity i'm using a pattern i've used in several production apps. However to keep code a bit smaller, the inheritance uses __proto__
which is not supported in old IE. You would need a more verbose method to make this work with unsupported (old) browsers. These models also are not full featured like Mongoose or ActiveRecord, but again for simplicity sake these will work just fine.
## Load Order
Meteor doesn't currently have a file based module import system like Browserfy, Webpack, or ES6 modules (though we can import them and expose globally like this example). However i've heard this is coming in the near future. Currently Meteor has a set of rules it uses for loading. Read the full rundown here. client/
folders are only sent to the client and server
folders are only sent to the server. The both
folder will be sent to both client and server.
## Code Generator
No one likes typing boilerplate. If this project's folder structure works for you, Meteor Generate can save a lot of time. I'm in the middle of converting my old MVC patterns with Blaze over to Component structured React. This should be on NPM soon but in the mean time keep an eye out on this branch. It creates models, pages, routes, React components with Sass styles, and more.
## Security
In short, trust nothing on the client. Assume everything being passed from the client to server could be a malicious value. Meteor provides utilities to check all inputs to make sure they are the shape and type you expect. This prevents Mongo injections. If they're not the shape/type you expect, reject it and throw an error. remove the insecure package. It automatically allows full access to insert/update/remove methods. This would be great for a throw away prototype or for learning the very basics but it's a security hazard.
The autopublish package should also be removed. This will send your entire serverside database to the client and load it into the Mini-Mongo store. Not only is this slow with large data sets but it's a security hazard.
Only send authorized data to the client. It's up to you to verify if a user should be allowed to subscribe to a publication. They can't access it in Mini-Mongo unless you send it to them. You only want to send data down that they are allowed/supposed to use.