by Michael Brook, CTO of Pitchly
@michaelcbrook
Meteor is a reactive full-stack JavaScript framework that allows you to get shit done quickly
Data layer
Data access layer
API layer
Model
View
Frontend
Backend
Data layer
Thin permission layer
View
Frontend
Backend
Frontend:
Blaze
Meteor's template rendering engine
Tracker
Meteor's reactive system
Backend:
Publications
"Get" data from the server
Methods
"Post" data to the server
MongoDB
The underlying database
Reactive programming is a development philosophy where instead of telling the computer how to do something, you tell it what to do.
When the data changes, update the view that depends on that data automatically
Alt:
chat.pitchly.net
client
Event: send message
Event: toggle hint box
AJAX call
API endpoint
Data access layer
Find DOM element
$('.my-element')
Set HTML
$el.html(response)
<div>Message</div>
Event 2
Event 3
Event 4
Wait, but the user logged out.
Wut?
I want my app to be real time!
3
1
0
1
1
0
0
1
0
1
0
1
0
1
1
0
0
1
1
1
0
1
Subscribe: Only give me last 100 messages
Publish: Here are the last 100 messages
Helper reacts to data or UI change
client
1
{{#each messages}}
<div class="message">{{message}}</div>
{{/each}}
Event: send message
Method validates
& updates
UI state
Event: toggle hint box
The server publishes a refined data set.
The client subscribes to that data set and continues to receive updates for the duration of the session.
Meteor.publish("messages", function() {
return Messages.find({}, {
fields: { name: 1, message: 1, createdAt: 1, announcement: 1 },
limit: 100,
sort: { createdAt: -1 }
});
});
Meteor.subscribe("messages");
Client
Server
Meteor.publish("messages", function() {
if (!this.userId) {
this.ready();
return;
}
return Messages.find({ ownerId: this.userId }, {
fields: { name: 1, message: 1, createdAt: 1, announcement: 1 },
limit: 100,
sort: { createdAt: -1 }
});
});
Server (restricted to logged-in user)
Methods validate input and can commit to the database
Meteor.call("sendMessage", { message }, (error, response) => {
if (error) alert(error.reason);
});
Client
Meteor.methods({
'sendMessage'(data) {
check(data, {
message: String
});
if (data.message=="") {
throw new Meteor.Error("message-empty", "Your message is empty");
}
Messages.insert({
message: data.message,
createdAt: new Date()
});
}
});
Server
Meteor.methods({
'sendMessage'(data) {
if (!Meteor.userId()) {
throw new Meteor.Error("logged-out", "You must be logged in.");
}
check(data, {
message: String
});
if (data.message=="") {
throw new Meteor.Error("message-empty", "Your message is empty");
}
Messages.insert({
ownerId: Meteor.userId(),
message: data.message,
createdAt: new Date()
});
}
});
Server (validating logged-in user)
Templates contain helpers, events & other templates
JS
<body>
{{#each messages}}
<div class="message {{#if announcement}}announcement{{/if}}">
{{message}}{{#if announcement}} <img src="/tada.png" class="emoji" alt="tada">{{/if}}
</div>
{{/each}}
<form>
<input type="text" placeholder="Type your message here..."><button type="submit">Send</button>
</form>
</body>
HTML
Template.body.onCreated(function bodyOnCreated() {
this.subscribe("messages");
});
Template.body.helpers({
messages() {
return Messages.find({}, { sort: { createdAt: 1 } });
}
});
Template.body.events({
'submit form'(event, instance) {
event.preventDefault();
const $input = $(event.currentTarget).find('input');
Meteor.call("sendMessage", { message: $input.val() }, (error, response) => {
if (error) {
alert(error.reason);
} else {
$input.val(""); //empty input on success
}
});
}
});
Helpers are functions that automatically re-run whenever a reactive data source inside the function changes.
A reactive data source can be data from the database, UI Session variables, or ReactiveVars attached to the template
The return value of a helper is generally rendered into a template using {{...}} syntax.
Minimum Viable Code - Live Chat in < 20 lines
<body>
{{#each messages}}<div>{{message}}</div>{{/each}}
<form><input type="text"><button type="submit">Send</button></form>
</body>
Client HTML
import { Template } from 'meteor/templating';
import { Mongo } from 'meteor/mongo';
import './main.html';
const Messages = new Mongo.Collection('messages');
Template.body.helpers({
messages() { return Messages.find({}, { sort: { createdAt: 1 } }); }
});
Template.body.events({
'submit form'(event, instance) {
event.preventDefault();
const $input = $(event.currentTarget).find('input');
Messages.insert({ message: $input.val(), createdAt: new Date() });
$input.val("");
}
});
Client JS
But where's the backend?
Wait, is that Mongo?!
Minimongo was developed by the people at Meteor to have identical syntax and function as MongoDB but on the browser.
Data loaded from the server via pub/sub is automatically inserted into Minimongo and you can query it as you would the real database.
Minimongo is a reactive data source.
(You don't even have to use it with a real database)
For non-critical apps, you can create an app that saves and retrieves data to/from a MongoDB database without creating any backend.
The point of pub/sub is to create the security needed by production apps.
When Method code is shared between the backend and frontend, Meteor can predict the result of the Method immediately and render it to the client.
In case of error, it will undo the change.
The result is zero latency.
(Using this, backend code may be visible to the client but it can't be run by the client.)
members() {
return Members.find({ groupId: "1234" });
}
Mongo cursors can be iterated on directly and their properties get passed on.
{{#each members}}
<div class="name">{{name}}</div>
<div class="employeeId">{{employeeId}}</div>
{{/each}}
helper
You don't always need to save state in the DB. Often times, you want a UI element to "toggle" between states, saved locally.
this.activeTab = new ReactiveVar("shoes")
activeTab() {
return Template.instance().activeTab.get();
}
helper
'click .tab'(event, instance) {
instance.activeTab.set(this.tab);
}
event
<div class="tab {{#if equals activeTab "shoes"}}active{{/if}}">Shoes</div>
<div class="tab {{#if equals activeTab "pants"}}active{{/if}}">Pants</div>
equals (var1, var2) {
return (var1===var2);
}
helper
Sessions behave similarly to ReactiveVar, except they are globally available for as long as the user is on the site.
Sessions can also hold multiple properties.
Session.set("layout", "grid")
Session.get("layout")
(Session can survive a hot load push)
Oh yeah, another cool thing about Meteor is anytime there is a change to the app, the changes push to all clients right away.
How does Meteor know which code is on the frontend and which is on the backend?
Meteor is very opinionated...
Option 1
if (Meteor.isClient) { ... }
if (Meteor.isServer) { ... }
Option 2
client
server
* recommended
Meteor handles account management for you. The 'users' collection holds all user data, and Meteor takes care of everything from password encryption to "forgot password" workflows to signup emails. You can build or not build the UI yourself.
Account packages can be installed to support login for:
You can make any Meteor app a mobile app. Meteor integrates with Cordova.
meteor add-platform ios
meteor add-platform android
Desktop also supported via a third-party package, using Electron
Everything in one code base
Meteor is a full-stack framework whereas React and Vue are frontend frameworks.
Meteor is merely a collection of many different toolsets combined into one, as such each component is interchangeable.
React and Vue are used in Meteor very often. More so nowadays.
No. It used to be you didn't have a choice. Nowadays, you can use other data stores via GraphQL and Apollo.
GraphQL was developed by Facebook. Apollo was created by the Meteor team and is the #1 GraphQL frontend client.
Very recently, Apollo and GraphQL have added support for pub/sub, for the first time creating a "real-time" API.
https://medium.com/@michaelcbrook/how-to-get-apollo-2-0-working-with-graphql-subscriptions-321388be030c
"Premature scaling is the number one cause of startup death. You shouldn’t spend time optimizing a product when you don’t even know if users want it."
- Startup Genome
"It was certainly brave to build an app for three platforms in a little over three months with hopes to reach thousands. Meteor not only made it possible to build the app in that time frame, but Meteor was able to scale wonderfully."
- David Woody, Game Developer
Meteor Galaxy
MDG's paid hosted solution
MUP
http://meteor-up.com/
(deploy anywhere)
I deployed to AWS with MUP in under 45 mins.
$8/mo.
Out of the box, Meteor does some pretty awesome things
If you're interested in learning Meteor or JavaScript, I'm interested in teaching.
Register: http://iowacodeschool.com/
We've built an app platform that helps companies manage & action their data. (it's actually pretty sweet)
We're hiring! https://pitchly.net/
My name is Michael C. Brook
michael@pitchly.net
https://medium.com/@michaelcbrook
chat.pitchly.net