ISOMORPHIC REALTIME APPS WITH METE R Facebook-quality apps without Facebook’s money Stephan Hochhaus http://de.slideshare.net/stephanhochhaus/ meteor-not-just-for-rockstars Laure Philips The road so far s t n e m u c o D c i t a St Static Clie nts HTML age 1990 2000 HTML, CSS, JavaScript PhP / Perl / Java / … Client Server JavaScript Libraries MySql Database LAMP age 2000 2010 e c a f r e t n I r e s U Client = 3 Tier Mo del LAMP age 2000 2010 One Page Apps t n e i l C Rich Offline Clien t Storage JavaScript age 2010 … JavaScript conquered the server Job Profile Node Infographic January 2015 Smaller Technology Stack HTML, CSS, JavaScript Client Align Technology PhP / Perl / Java / … Server Align Technology JavaScript Libraries MySql Align Technology Align Technology Database Smaller Technology Stack HTML, CSS, JavaScript JavaScript Client Server JavaScript Libraries NoSql Database T “ ierless (also called single-tier, multi-tier or isomorphic) programming enables developing a web application as a single mono-linguistic application, which renders its development akin to that of a desktop application. ” Opa Links Hop Ur/Web GWT Mete r JavaScript era Meteor Framework 2010 2011 Meteor 1.0 2014 The Meteor Stack Meteor Node.js Meteor Packages MongoDB Why is it so easy to learn? $ curl https://install.meteor.com/ | sh $ $ cd leaderboard meteor => started your app => app running at http://localhost:3000 Why is it so easy to learn? 1 Tierless (Isomorphism) 2 Synchronicity 3 Reactivity 4 Smart Clients 1 Tierless Code 1 2 3 4 5 6 7 8 9 10 12 13 14 15 16 17 18 20 21 if (Meteor.isClient) { Template.player.events({ 'click': function () { // Event handler } }); } CLIENT SERVER if (Meteor.isServer) { Meteor.startup(function () { var names = ["Ada Lovelace", "Grace Hopper", "Marie Curie"]; _.each(names, function (name) { Players.insert({ name: name, score: Math.floor(Random.fraction() * 10) * 5 }); }); }); } leaderboard.js Accessing Data throughout the Stack 1 var name = response.name 1 GET 2 http://server/users/ 3 name/12345 1 SELECT name 2 FROM users 3 WHERE id = 12345 1 Clien Server Databa Accessing Data throughout the Stack 1 var name = response.name 1 GET 2 http://server/users/ 3 name/12345 1 SELECT name 2 FROM users 3 WHERE id = 12345 1 Users.find( 2 {_id : 12345}, 3 {fields : 4 {name : 1}}) 1 Users.find( 2 {_id : 12345}, 3 {fields : 4 {name : 1}}) 1 Users.find( 2 {_id : 12345}, 3 {fields : 4 {name : 1}}) 1 Clien Server Databa Accessing Data throughout the Stack 1 2 3 4 5 6 7 8 9 10 11 12 13 14 var Players = new Mongo.Collection("players"); if (Meteor.isClient) { Players.find({}, { sort: { score: -1, name: 1 } }); } if (Meteor.isServer) { Meteor.startup(function () { Players.insert({ name: "Laure Philips", score: Math.floor(Random.fraction() * 10) * 5 }); }); } 1 1 Mini Databases create read update delete Offline Storage Higher Availability mini-MongoDB API MongoDb API MongoDb Client Server Database 2 Event-Based Applications Need Callbacks Event Event Event single-threaded event handling split off to child process Event … event loop Disk Network Process callback … 2 Callback Hell 1 users.findOne({userId: 12345}, function (err, user) { 2 var logIn = {userName : user.name, when: new Date}; 3 logins.insert(login, function (err, done) { 4 console.log(‘Login saved’); 5 }) 6 }) 2 Synchronicity with Meteor 1 2 3 4 var user = Meteor.users.findOne({userId: 12345}; var logIn = {userName : user.name, when: new Date}; logins.insert(login); console.log(‘Login saved’); 2 The power of Fibers wait Fiber #1 (one per client by default) 0 10 (idle cpu time) 20 30 40 ms DB.connect Connection.query ExternalApi.makeCall Connection.save Request.send Reactivity : no more event-spaghettis 3 3 Traditional Programming 1 2 3 4 5 var a = 2 var b = 5 var c = a + b console.log(c) // c is 7 1 a = 5 2 console.log(c) 3 // c is still 7 1 c = a + b 2 // c is finally 10 3 Traditional Programming 1 2 3 4 5 var a = 2 var b = 5 var c = a + b console.log(c) // c is 7 1 a = 5 2 console.log(c) 3 // c is still 7 1 c = a + b 2 // c is finally 10 Reactive Programming 1 2 3 4 5 var a = 2 var b = 5 var c = a + b console.log(c) // c is 7 3 Traditional Programming 1 2 3 4 5 var a = 2 var b = 5 var c = a + b console.log(c) // c is 7 1 a = 5 2 console.log(c) 3 // c is still 7 1 c = a + b 2 // c is finally 10 Reactive Programming 1 2 3 4 5 var a = 2 var b = 5 var c = a + b console.log(c) // c is 7 1 a = 5 2 console.log(c) 3 // c is automagically 10 Reactive Programming in Meteor 1 2 3 4 5 6 7 8 9 10 11 12 13 Session.set("a", 2); Session.set("b", 5); Tracker.autorun(function () { var a = Session.get("a"); var b = Session.get("b"); Session.set("c", a + b); console.log(Session.get("c")) // c is 7 }); Session.set("a", 5); // c is 10 3 Reactive Programming in Meteor Reactive data sources Session variables Collection.findOne && Collection.find Meteor.user() Reactive computations Tracker.autorun Templates 3 3 Meteor Templates L M T H f o e c ie p a is te la p m A te ta a d ic m a n y d in ta n o c n a c t tha 1 2 3 4 5 6 7 8 9 10 11 12 13 <head> <title>Demo</title> </head> <body> <h1>Welcome to Meteor!</h1> {{> hello}} </body> <template name="hello"> <button>Click Me</button> <p>You've pressed the button {{counter}} times.</p> </template> 3 Meteor Templates L M T H f o e c ie p a is te la p m A te ta a d ic m a n y d in ta n o c n a c t tha 1 2 3 4 5 6 7 8 9 10 11 12 13 <head> 1 Session.setDefault('counter', 0); <title>Demo</title>2 Template.hello.helpers({ </head> 3 counter: function () { 4 return Session.get('counter'); <body> 5 } <h1>Welcome to Meteor!</h1> 6 }); {{> hello}} 7 }); </body> <template name="hello"> <button>Click Me</button> <p>You've pressed the button {{counter}} times.</p> </template> 4 Smart Clients Distributed Data Protocol Client RPC HTTP Server JSON 4 Smart Clients Distributed Data Protocol Client 1 Meteor.call('hello', ‘jsconf.be', 2 function (err, res) { 3 console.log(res); 4 }) RPC HTTP Server JSON 4 Smart Clients Distributed Data Protocol Client 1 Meteor.call('hello', ‘jsconf.be', 2 function (err, res) { 3 console.log(res); 4 }) RPC HTTP Server JSON 1 Meteor.methods({ 2 'hello' : function (name) { 3 return 'hello' + name; 4 } 5 }) 4 Smart Clients Triggers reactive updates Client App App Template Template DB DB Tracker Server Tracker Pushes changes App DB LiveQuery Meteor Packages Native autopublish session template accounts Community Meteor: some pitfalls 1 2 3 4 5 6 7 8 9 10 11 12 13 14 var car = { name : 'Tesla model S', color : 'white' }; Session.set('favoriteCar', car); Tracker.autorun(function () { var favcar = Session.get('favoriteCar'); console.log('Your favorite car is ' + favcar.name) }); car.color = 'red'; car.name = 'Ferrari F80'; Meteor: some pitfalls 1 Players = new Mongo.Collection("players"); 2 3 if (Meteor.isClient) { 4 5 Tracker.autorun( { 6 var players = Players.find(); 7 /* Which player was removed / updated / added ? */ 8 }); 9 } Meteor: some pitfalls 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 var slides = new Mongo.Collection('slides'); if (Meteor.isClient) { /* Runs every time slide is changed */ Tracker.autorun( function () { var slide = slides.findOne({}); Reveal.slide(slide.current_slide) }) /* Runs every time user logs in / out */ Tracker.autorun( function () { if (Meteor.user()) Reveal.configure({controls: true}) }) } if (Meteor.isServer) { Accounts.createUser({email:'jsconf@be', password: 'demo'}) } Meteor: some pitfalls 1 var slides = new Mongo.Collection('slides'); 2 s y e k I P A in a rt e c e k li n o ti a rm fo in e iv it s n e s t a th t n a 3 if (Meteor.isClient) { rt o “ It’s imp ly n o re rv e s a in r (o k c lo b 4 /* Runshin every time slide is changed */ r e rv e S is r. o te e M a it w d e s u only b5e Tracker.autorun( function () { e th ll a r fo le ib is v e b l il w s y e k r u o y e is rw e th o r, o te e 6 var slide = slides.findOne({}); M in file) 7 Reveal.slide(slide.current_slide) world to see. “ 8 }) 9 /* Runs every time user logs in / out */ s e g a k c a p d n a r 10 Tracker.autorun( function () { o te e M n o l a < Tutori 11 if (Meteor.user()) 12 Reveal.configure({controls: true}) 13 }) 14 } 15 16 if (Meteor.isServer) { 17 Accounts.createUser({email:'jsconf@be', password: 'demo'}) 18 } Meteor: some pitfalls 1 2 3 4 5 6 7 8 9 10 11 Template.hello.helpers({ counter : function () { return Session.get('counter'); } }); Template.hello.events({ 'click button' : function () { Session.set('counter', Session.get(‘counter') + random()); } }); App DB App DB counter = 42 counter = 101 App DB Last Writer Wins Ghent Brussels Antwerp 4 295 tweets 38 700 followers 11 172 questions tagged meteor Ghent Brussels Antwerp 4 295 tweets 38 700 followers 11 172 questions tagged meteor Demo time Code can be found at https://github.com/lphilips/meteordemo