diff --git a/db.json b/db.json deleted file mode 100644 index f18689c..0000000 --- a/db.json +++ /dev/null @@ -1 +0,0 @@ -{"meta":{"version":1,"warehouse":"2.2.0"},"models":{"Asset":[{"_id":"themes/meteor/source/fonts/percolate.eot","path":"fonts/percolate.eot","modified":0,"renderable":1},{"_id":"themes/meteor/source/fonts/percolate.ttf","path":"fonts/percolate.ttf","modified":0,"renderable":1},{"_id":"themes/meteor/source/fonts/percolate.woff","path":"fonts/percolate.woff","modified":0,"renderable":1},{"_id":"themes/meteor/source/images/favicon.png","path":"images/favicon.png","modified":0,"renderable":1},{"_id":"themes/meteor/source/images/icon-white.svg","path":"images/icon-white.svg","modified":0,"renderable":1},{"_id":"themes/meteor/source/images/logo-coralspace-left.svg","path":"images/logo-coralspace-left.svg","modified":0,"renderable":1},{"_id":"themes/meteor/source/script/main.js","path":"script/main.js","modified":0,"renderable":1},{"_id":"themes/meteor/source/script/smooth-scroll.min.js","path":"script/smooth-scroll.min.js","modified":0,"renderable":1},{"_id":"themes/meteor/source/style/style.less","path":"style/style.less","modified":0,"renderable":1},{"_id":"themes/meteor/source/fonts/percolate.svg","path":"fonts/percolate.svg","modified":0,"renderable":1},{"_id":"themes/meteor/source/images/logo.png","path":"images/logo.png","modified":0,"renderable":1},{"_id":"themes/meteor/source/images/OSXsmall.png","path":"images/OSXsmall.png","modified":0,"renderable":1},{"_id":"themes/meteor/source/images/tuxSmall.png","path":"images/tuxSmall.png","modified":0,"renderable":1},{"_id":"themes/meteor/source/images/windowsSmall.png","path":"images/windowsSmall.png","modified":0,"renderable":1}],"Cache":[{"_id":"themes/meteor/.git","hash":"049265de896a0397e3c70e6313762e3c26f93523","modified":1474889374000},{"_id":"themes/meteor/_config.yaml","hash":"2bbc5bebc9a3102442c4d4b89d4ae0c829fd4e62","modified":1474889374000},{"_id":"source/index.md","hash":"13d4e4f70cdffa8b46a7d4e5a9de787ba851cbae","modified":1520602202163},{"_id":"source/commandline.md","hash":"0b329d957b5386c4280d372fcb876d29015ba01d","modified":1474889325000},{"_id":"themes/meteor/layout/layout.ejs","hash":"9c495eaef6bd651b5112cd99e2d98c1dc4fcf394","modified":1478707506886},{"_id":"themes/meteor/layout/page.ejs","hash":"e63b94be51c6ccd49b302c9c91a27873f73c1779","modified":1478101465155},{"_id":"source/api/accounts-multi.md","hash":"f86155b4d0ff6c8d9c3c5ad883c53eb6625c2b29","modified":1474889325000},{"_id":"source/api/accounts.md","hash":"2e04b2e0b51c4037d77aad2bbf6a5f82d3d922ec","modified":1474889325000},{"_id":"source/api/assets.md","hash":"23d669772ecc4ec8f1bd5d517bcd8a965fa78dbd","modified":1474889325000},{"_id":"source/api/blaze.md","hash":"d75884756afddfd2bc791c3e3384df73e71f70a9","modified":1474889325000},{"_id":"source/api/check.md","hash":"170d63a00796364506ee4397241062d2e276d454","modified":1474889325000},{"_id":"source/api/collections.md","hash":"1d715fc4a18cc8430248757fa37b14ce57b3730c","modified":1474889325000},{"_id":"source/api/connections.md","hash":"dd194f4c3b60bf51779e477580db9325a6dcaafb","modified":1474889325000},{"_id":"source/api/core.md","hash":"07aba32faf25ed16fc6442fa59ebaea2ececb16d","modified":1474889325000},{"_id":"source/api/ejson.md","hash":"626c38a1341ea12a3f957615e5200092b91963bf","modified":1474889325000},{"_id":"source/api/email.md","hash":"97a80f9b11f3c9d6bc7265201527d29bed77083f","modified":1474889325000},{"_id":"source/api/http.md","hash":"eb2a09d9f2d65120d688b47e42e1e06b4e153364","modified":1474889325000},{"_id":"source/api/methods.md","hash":"2a9c64efb7135baf43a9e88bc4fd06aa6040482d","modified":1474889325000},{"_id":"source/api/mobile-config.md","hash":"cc49bf8a8c411d91ca7f584e8761e7e3baeee5c9","modified":1474889325000},{"_id":"source/api/packagejs.md","hash":"2da4cb7c02733ed934172dd0ff6d66aafef79610","modified":1474889325000},{"_id":"source/api/pubsub.md","hash":"ebc3e8107cb22e75191bff600dfd36e43f12eab5","modified":1474889325000},{"_id":"source/api/passwords.md","hash":"d223dbc16d787843fdb10ef421d6f411b5bb1d79","modified":1474889325000},{"_id":"source/api/reactive-var.md","hash":"5eaff15aed6dfcfb87c12b0593a60e760d63e801","modified":1474889325000},{"_id":"source/api/session.md","hash":"de19fade87018a3a20a26d570feb8112d41daabe","modified":1474889325000},{"_id":"source/api/templates.md","hash":"149a1d828bd90b3b0e038d8a41b2ea5491751cb4","modified":1474889325000},{"_id":"source/api/timers.md","hash":"9ce32673bd46b7c61662ce708289a46237e6d4d4","modified":1474889325000},{"_id":"source/api/tracker.md","hash":"62b255f29d1201c116acc196ce6f76fb38abee25","modified":1474889325000},{"_id":"source/packages/accounts-ui.md","hash":"d39b2c6c7c31c45fa3f385d07f28ea9b1cb4431d","modified":1474889325000},{"_id":"source/packages/appcache.md","hash":"88d1f57aeb3c3b7ee3c54bf9a820d05ae644acaa","modified":1474889325000},{"_id":"source/packages/coffeescript.md","hash":"a00aceb918ea7d16331531960168af7795ed90ed","modified":1474889325000},{"_id":"source/packages/audit-argument-checks.md","hash":"14e35b60b49aff6bc6fd398f12e799e834289407","modified":1474889325000},{"_id":"source/packages/jquery.md","hash":"10866aa89a5a93b288342c00539e52922b6b6cc0","modified":1474889325000},{"_id":"source/packages/less.md","hash":"b4d4a20a5a96ffe17b7c5e4a72a1c79960730a2c","modified":1474889325000},{"_id":"source/packages/ecmascript.md","hash":"a000de71dc604eb453363540f8743b56feca7444","modified":1474889325000},{"_id":"source/packages/markdown.md","hash":"029caba5c4b521899f513d6eab2117ee4bbf1071","modified":1474889325000},{"_id":"source/packages/oauth-encryption.md","hash":"96d52e09c837cbb137752de21b749507f25d2820","modified":1474889325000},{"_id":"source/packages/modules.md","hash":"3c169bea0e926cdd28e2d2b040bae07de1d2ae53","modified":1474889325000},{"_id":"source/packages/random.md","hash":"b689a94ec85207d1fa7950a23294298bb0843680","modified":1474889325000},{"_id":"source/packages/spacebars.md","hash":"13454fe7dc69f4f741775fae978307151d8a8c2e","modified":1474889325000},{"_id":"source/packages/underscore.md","hash":"6d3cc369754de4d2b341032cd28bb8523aaebe92","modified":1474889325000},{"_id":"source/packages/webapp.md","hash":"8071b9ee8705fb99a8f236e2de3bf9ac2350d503","modified":1474889325000},{"_id":"themes/meteor/source/fonts/percolate.eot","hash":"9f46f6ed3453808cb206b5b8c7c399f8c7c50b4b","modified":1474889374000},{"_id":"themes/meteor/source/fonts/percolate.ttf","hash":"ae93904a100e0c875bb382cb88709de4cd8b6a37","modified":1474889374000},{"_id":"themes/meteor/source/fonts/percolate.woff","hash":"f475ff4ac3d777c8d56bc7040d6f8576b86398a5","modified":1474889374000},{"_id":"themes/meteor/source/images/favicon.png","hash":"c019ad5135fb00e9d5a8a000a7162f4876cf583b","modified":1474889374000},{"_id":"themes/meteor/source/images/icon-white.svg","hash":"037afdf5842532cdc696bd93541be8a273ddf9dc","modified":1474889374000},{"_id":"themes/meteor/source/images/logo-coralspace-left.svg","hash":"ce3fa809cc5498c23597f68fc7f94d72f0deb453","modified":1474889374000},{"_id":"themes/meteor/source/script/main.js","hash":"aa6f1d81ab38e3d8e0dbdef9a574d2330f8ff5a4","modified":1474889374000},{"_id":"themes/meteor/source/script/smooth-scroll.min.js","hash":"c80cccc6c68f32b348b3d8459eb89686871b2e38","modified":1474889374000},{"_id":"themes/meteor/source/style/style.less","hash":"6517fbcd0c75aeb1bd4fc6f0c9a834fcef9a77e1","modified":1474889374000},{"_id":"themes/meteor/layout/partials/illustration-help.ejs","hash":"03fcaff5811b72920c87fa9154ffc4853b30b8de","modified":1474889374000},{"_id":"themes/meteor/layout/partials/illustration-guide.ejs","hash":"f490bb8fd8e5abd0e71c97c92a335a0802e329e1","modified":1474889374000},{"_id":"themes/meteor/layout/partials/illustration-compass.ejs","hash":"8072852c211ea881b8facccbe00d975b2507d990","modified":1474889374000},{"_id":"themes/meteor/layout/partials/illustration-logs.ejs","hash":"47638a8a92ab5e5a7a143e1588ca9baaec371f9d","modified":1474889374000},{"_id":"themes/meteor/source/style/_global/panel.less","hash":"da39a3ee5e6b4b0d3255bfef95601890afd80709","modified":1474889374000},{"_id":"themes/meteor/layout/partials/illustration-support.ejs","hash":"4ac49bda616e8046bd4175fbb6e891dfcab658c0","modified":1474889374000},{"_id":"themes/meteor/layout/partials/illustration-github.ejs","hash":"ad171cc4d41acf187831c415cb1928d5ef7a1142","modified":1474889374000},{"_id":"themes/meteor/layout/partials/sidebar.ejs","hash":"2a8d739e3b6d6c1f497ae8ffdd3f9dbe0efe38d6","modified":1478707476014},{"_id":"themes/meteor/source/fonts/percolate.svg","hash":"b7c6df677a47fd66316ce90839550bfad6fd462f","modified":1474889374000},{"_id":"themes/meteor/source/style/_global/Checkbox.less","hash":"9971b489264f4b37fb901cd8417accba7becf270","modified":1474889374000},{"_id":"themes/meteor/source/style/_global/Input.less","hash":"5be8dadb507ff338ae7cea539cae535b7779334c","modified":1474889374000},{"_id":"themes/meteor/source/style/_global/Textarea.less","hash":"4c6089bb5f41dde32d1828d6d27a0899f8e3c8cf","modified":1474889374000},{"_id":"themes/meteor/source/style/_global/Select.less","hash":"9065734a36e52c8ce2830d403da3852eebe3590c","modified":1474889374000},{"_id":"themes/meteor/source/style/_global/Radio.less","hash":"ecc255428e2883b49d074648a290de07b7a9d4d5","modified":1474889374000},{"_id":"themes/meteor/source/style/_global/animation.less","hash":"2c01fa20c282b133dda12a852fb61baba0c0af82","modified":1474889374000},{"_id":"themes/meteor/source/style/_global/base.less","hash":"0fce53b3187d99296dedd0fcd90887a4b336ad83","modified":1474889374000},{"_id":"themes/meteor/source/style/_global/button.less","hash":"d76568349602485c2ec7d0d3380091c4a5e8e2ca","modified":1474889374000},{"_id":"themes/meteor/source/style/_global/form.less","hash":"dcb4320c34011659a1e2974a7ae5b28b444cc519","modified":1474889374000},{"_id":"themes/meteor/source/style/_global/docsearch.less","hash":"26a087b4b6748822b7323584ba8dc375c7462641","modified":1474889374000},{"_id":"themes/meteor/source/style/_global/formatting.less","hash":"7d9dbfc393136e7a28fff7076f74bf5b7f3670cd","modified":1478702733534},{"_id":"themes/meteor/source/style/_global/icon.less","hash":"b6f93082065f57b48185c6fcbffafe5dfc98c86f","modified":1474889374000},{"_id":"themes/meteor/source/style/_global/mobile.less","hash":"d4f28dae69f89ce6e9646774ce6d07d6f677c683","modified":1474889374000},{"_id":"themes/meteor/source/style/_global/link.less","hash":"a80547ffbacd58422e7095aadbbd73ba0c650e31","modified":1474889374000},{"_id":"themes/meteor/source/style/_global/nav.less","hash":"e3eb2ec6e2245f6b4d40589c104618f9ed69f37f","modified":1474889374000},{"_id":"themes/meteor/source/style/_global/syntax.less","hash":"ff408a13d195a50b3310cdbf5ca31961393e7983","modified":1474889374000},{"_id":"themes/meteor/source/style/_theme/layout.less","hash":"655fcc41ad239d5a219aabc4c79c0ac970b6c8bd","modified":1474889374000},{"_id":"themes/meteor/source/style/_theme/panel.less","hash":"9438a41518ecdaa01d64ae65317ff6e27b03720d","modified":1474889374000},{"_id":"themes/meteor/source/style/_theme/nav.less","hash":"a558ca89e296a7e94d3a29e998aa119c877c065e","modified":1474889374000},{"_id":"themes/meteor/source/style/_theme/content.less","hash":"c90d7765335d1ee1eca5726ada84d7cb35a61745","modified":1474889374000},{"_id":"themes/meteor/source/style/_theme/sidebar.less","hash":"5fe1fb4c64bcf10d8bfe0caf63d2f969ed206a6c","modified":1474889374000},{"_id":"themes/meteor/source/style/_util/clearfix.import.less","hash":"8dd7f85394fc6e232cad232ea54e605a06be002e","modified":1474889374000},{"_id":"themes/meteor/source/style/_util/color.import.less","hash":"5c88b7bc663df13316e3748f8516f4324f603bd2","modified":1477889942561},{"_id":"themes/meteor/source/style/_util/easing.import.less","hash":"69523005d6afbd184cfa91d629cf1a931ebf8fd1","modified":1474889374000},{"_id":"themes/meteor/source/style/_util/helper.import.less","hash":"7baf147d5c4bac8c5ef4b2690af3b185b4df56d2","modified":1474889374000},{"_id":"themes/meteor/source/style/_util/link.import.less","hash":"a6856c0b5017b7bb688d7a2cf2d5f3bd682df6fe","modified":1474889374000},{"_id":"themes/meteor/source/style/_util/normalize.import.less","hash":"02fe53286d071637534d5aa2c57c76c168c0d521","modified":1474889374000},{"_id":"themes/meteor/source/style/_util/index.import.less","hash":"2a38f287116d66f9848229b59d03464b4f9b18b9","modified":1474889374000},{"_id":"themes/meteor/source/style/_util/scrollbar.import.less","hash":"1a45d8862fafd8d62f4e2562278a88ed3008ac32","modified":1474889374000},{"_id":"themes/meteor/source/style/_util/lesshat.import.less","hash":"491b7bc1426fb35bf17c28418aadab66b480b5e0","modified":1474889374000},{"_id":"themes/meteor/source/style/_util/text.import.less","hash":"3c1b4559dd5cf7b6b37a42ab8b7ebd9e40e2eee9","modified":1474889374000},{"_id":"themes/meteor/source/style/_util/ui.import.less","hash":"b577e1c8092b87f10baef86c0f9d30125b2cdf94","modified":1474889374000},{"_id":"themes/meteor/source/style/_util/typography.import.less","hash":"c8deee0f5ad8b529eeb097d90ef7a3f2f91441f6","modified":1474889374000},{"_id":"source/guide/exposure.md","hash":"6893c7b267318a295ab6b0e9217321b57570cff9","modified":1477242714256},{"_id":"source/guide/links.md","hash":"25c0c110477f71369760a06b5e4e0ced17f884e7","modified":1477238465174},{"_id":"source/guide/query.md","hash":"2778f710129d12fe1fb312bc0a3e4105d5a8e770","modified":1477242714252},{"_id":"source/api/collectionLinks.md","hash":"56d88ef0790046a8072cff0aace7eab16df4057b","modified":1474904935000},{"_id":"source/old_api/accounts-multi.md","hash":"f86155b4d0ff6c8d9c3c5ad883c53eb6625c2b29","modified":1474889325000},{"_id":"source/old_api/accounts.md","hash":"2e04b2e0b51c4037d77aad2bbf6a5f82d3d922ec","modified":1474889325000},{"_id":"source/old_api/assets.md","hash":"23d669772ecc4ec8f1bd5d517bcd8a965fa78dbd","modified":1474889325000},{"_id":"source/old_api/blaze.md","hash":"d75884756afddfd2bc791c3e3384df73e71f70a9","modified":1474889325000},{"_id":"source/old_api/check.md","hash":"170d63a00796364506ee4397241062d2e276d454","modified":1474889325000},{"_id":"source/old_api/collections.md","hash":"1d715fc4a18cc8430248757fa37b14ce57b3730c","modified":1474889325000},{"_id":"source/old_api/connections.md","hash":"dd194f4c3b60bf51779e477580db9325a6dcaafb","modified":1474889325000},{"_id":"source/old_api/core.md","hash":"07aba32faf25ed16fc6442fa59ebaea2ececb16d","modified":1474889325000},{"_id":"source/old_api/ejson.md","hash":"626c38a1341ea12a3f957615e5200092b91963bf","modified":1474889325000},{"_id":"source/old_api/email.md","hash":"97a80f9b11f3c9d6bc7265201527d29bed77083f","modified":1474889325000},{"_id":"source/old_api/http.md","hash":"eb2a09d9f2d65120d688b47e42e1e06b4e153364","modified":1474889325000},{"_id":"source/old_api/methods.md","hash":"2a9c64efb7135baf43a9e88bc4fd06aa6040482d","modified":1474889325000},{"_id":"source/old_api/mobile-config.md","hash":"cc49bf8a8c411d91ca7f584e8761e7e3baeee5c9","modified":1474889325000},{"_id":"source/old_api/packagejs.md","hash":"2da4cb7c02733ed934172dd0ff6d66aafef79610","modified":1474889325000},{"_id":"source/old_api/passwords.md","hash":"d223dbc16d787843fdb10ef421d6f411b5bb1d79","modified":1474889325000},{"_id":"source/old_api/pubsub.md","hash":"ebc3e8107cb22e75191bff600dfd36e43f12eab5","modified":1474889325000},{"_id":"source/old_api/reactive-var.md","hash":"5eaff15aed6dfcfb87c12b0593a60e760d63e801","modified":1474889325000},{"_id":"source/old_api/session.md","hash":"de19fade87018a3a20a26d570feb8112d41daabe","modified":1474889325000},{"_id":"source/old_api/templates.md","hash":"149a1d828bd90b3b0e038d8a41b2ea5491751cb4","modified":1474889325000},{"_id":"source/old_api/timers.md","hash":"9ce32673bd46b7c61662ce708289a46237e6d4d4","modified":1474889325000},{"_id":"source/old_api/tracker.md","hash":"62b255f29d1201c116acc196ce6f76fb38abee25","modified":1474889325000},{"_id":"source/api/collection.md","hash":"56d88ef0790046a8072cff0aace7eab16df4057b","modified":1474904935000},{"_id":"source/links/intro.md","hash":"22563ced472e63aac548720e73c40ab9afdc822b","modified":1474944651000},{"_id":"source/links/addLinks.md","hash":"fb9ff624d61dd6f7c3a801dc9efaafb726e768cf","modified":1474945427000},{"_id":"themes/meteor/source/images/logo.png","hash":"77000ddb64c4ce7c6052165396c095bf56db02df","modified":1477889765000},{"_id":"source/api/reference.md","hash":"c9d62f4f296cda47e8d7fe9c867589cb8db8504d","modified":1476081598852},{"_id":"source/packages/boilerplate.md","hash":"b79d5a279c49629ffec2fae5830d8832e0474061","modified":1476277449040},{"_id":"source/packages/live.md","hash":"1a1e31abcc882e2c4ab4f88051b93828f398da46","modified":1476244662000},{"_id":"source/guide/namedQuery.md","hash":"fb9b64e1178aa6634dd1e1b0fa27367c95a7db64","modified":1476888260769},{"_id":"public/packages/boilerplate.html","hash":"665174f7e1b4feff6855a21d0d55bb44ab33a07a","modified":1476888401514},{"_id":"public/packages/live.html","hash":"0a2e82ff9a2cd1a265b719c224b5d356e8ef51f7","modified":1476888401515},{"_id":"public/index.html","hash":"c2ce0e968ba2673721a92a3e500a0192f8c47443","modified":1522417804583},{"_id":"public/guide/links.html","hash":"c5f969dbf38c8e880c0f298a26d5de4af725a25c","modified":1477242743358},{"_id":"public/guide/query.html","hash":"44cc7663225cd95a1074e35998c6e3f072f7b7a8","modified":1477242743358},{"_id":"public/api/reference.html","hash":"5804cc3431b97d6f22e75bdcaf78a97a7f1ce98c","modified":1476085992422},{"_id":"public/guide/exposure.html","hash":"8b234e6ae9c612847fd356e09c78d9a8a4d44bb8","modified":1477242743358},{"_id":"public/guide/namedQuery.html","hash":"18e7c7262deab6642396e1162075d13afaa43464","modified":1476888401516},{"_id":"source/api/exposure.md","hash":"b5ec5e9db871bb693b9e25ab6cf5d59ad0702b54","modified":1476888279565},{"_id":"source/api/namedQuery.md","hash":"eda9a78bc73477331f156a28948402034f259e07","modified":1476245109000},{"_id":"source/api/links.md","hash":"4933e81d277eb4f400180ee17100d0c1dfd8e5d5","modified":1476245463000},{"_id":"source/api/query.md","hash":"70ad66dd1db063ce9788d30386c00b8618adcb8f","modified":1477238135558},{"_id":"public/api/exposure.html","hash":"7bb3d6eb402621c8175d21560f69fbbfb0b870bf","modified":1476888401516},{"_id":"public/api/namedQuery.html","hash":"307f9da138f1b2e7f72b5bee136e74017b0c3da7","modified":1476888401516},{"_id":"public/api/links.html","hash":"603498b87891bb87385fdd492fee5b189d8d865b","modified":1476888401516},{"_id":"public/api/query.html","hash":"1e98a577a8bc9afb73a3ee2c15e4b359557890d2","modified":1477242743359},{"_id":"source/gettingStarted.md","hash":"7199eaf4f890d3f93a33aa637eb36176d62d7743","modified":1477475995711},{"_id":"source/chapters/1/intro.md","hash":"cb2971a1754a57e54c83a23157aa213bbbd41526","modified":1520594913090},{"_id":"source/chapters/1/collections.md","hash":"cf74c6feffb69bbbc55983924bde1609fd565873","modified":1520524429078},{"_id":"source/chapters/1/methods.md","hash":"11db47129eeda370d9afacfa74bcaa49af3f6ce6","modified":1522417454548},{"_id":"source/chapters/1/pubsub.md","hash":"cf56584b2bc44923b8132199d667875573f56a2a","modified":1520595003064},{"_id":"source/chapters/1/reactivity.md","hash":"52ba34f85e2782568bc760b67303f255c007d08e","modified":1520524429083},{"_id":"source/chapters/1/users.md","hash":"d19779346e35124c76eba98327de154e17d88aa0","modified":1520524429084},{"_id":"source/chapters/1/security.md","hash":"62ed45ca9d6ad9fcc04efd65d9c91dc6021c439a","modified":1520524429084},{"_id":"source/chapters/1/meteorSnacks.md","hash":"a79bdb5d941f78e389069200801cc4537c43bd89","modified":1520524429081},{"_id":"public/chapters/1/reactivity.html","hash":"54970acb3dc4f3627c278c818d94bf8a214256fc","modified":1520595083353},{"_id":"public/chapters/1/intro.html","hash":"e9f572f91560bb203324e270621aa63f098451c7","modified":1520595083353},{"_id":"public/chapters/1/collections.html","hash":"57d2d088ed8413821975c93134d9a1cf2cd13006","modified":1520595083353},{"_id":"public/chapters/1/methods.html","hash":"b598cd826e52eb3fd20b87b34b0b582b37eed326","modified":1522417804583},{"_id":"public/chapters/1/pubsub.html","hash":"37fec96ed2df55a0a9ef92b25d6bd38cb5cb4ee8","modified":1520595083353},{"_id":"public/chapters/1/users.html","hash":"e2b562c6e1995db44012c4d1a478bf0a08bb0e91","modified":1520595083353},{"_id":"public/chapters/1/security.html","hash":"2737ad5a231b3b5aa868805eb72e991753bfd98a","modified":1520595083353},{"_id":"public/chapters/1/meteorSnacks.html","hash":"e36d193ae47415a58998681f69a84aad40ff128c","modified":1520595083353},{"_id":"source/chapters/2/blaze.md","hash":"da39a3ee5e6b4b0d3255bfef95601890afd80709","modified":1478600232572},{"_id":"source/chapters/2/react.md","hash":"c9946991b722c8e858ce1cad425bc72b39ad005c","modified":1496388591575},{"_id":"source/chapters/2/intro.md","hash":"d0f42b6b850ab7dabf7ca79455ac4ad562dca7fc","modified":1510320330252},{"_id":"source/chapters/1/conclusion.md","hash":"9eccbb93600975cd1757971b05f2ab06f1852e44","modified":1520524429079},{"_id":"public/chapters/2/blaze.html","hash":"5fe67948b8d91195c5cb4d2fd8a04960a3d10377","modified":1478606307193},{"_id":"public/chapters/1/conclusion.html","hash":"400cbfdd96b679ebcf727be19f7f65f0e9be5efb","modified":1520595083352},{"_id":"public/chapters/2/react.html","hash":"309d9e152c2761074fb3109dde57101e2aafb280","modified":1520595083354},{"_id":"public/chapters/2/intro.html","hash":"de9e88de4c830d7f06716d94075d2679c0b55985","modified":1520595083353},{"_id":"source/chapters/2/conclusion.md","hash":"79f51574ff02cf98e8cb66e92d4534fc0df22d26","modified":1520282492064},{"_id":"themes/meteor/source/images/OSXsmall.png","hash":"b4bee61cda163ca0df6c258c690e222f3fbebb78","modified":1508484836976},{"_id":"themes/meteor/source/images/tuxSmall.png","hash":"05a369c1b8db9cc8b2d1e35ee3ec814cceaddc21","modified":1508484836976},{"_id":"themes/meteor/source/images/windowsSmall.png","hash":"2c2e638acae06d0c25c0575d1f697a0f8a21a1a0","modified":1508484836976},{"_id":"source/chapters/3/devtools.md","hash":"da39a3ee5e6b4b0d3255bfef95601890afd80709","modified":1508760585205},{"_id":"source/chapters/3/fixtures.md","hash":"dc1e1ef0513c62a4d043265f5bf60ba0e69f746f","modified":1508926625389},{"_id":"source/chapters/3/reusable-modules.md","hash":"2d7cfc86989315c5172e18bfe55021dd772cd65c","modified":1508917989387},{"_id":"source/chapters/3/structure.md","hash":"7a756a3191eb69e172c2a98255a868769524bcae","modified":1508929763021},{"_id":"source/chapters/3/uploads.md","hash":"86e6dd86cec2befeba48524c49a130a693ffa42b","modified":1508922004378},{"_id":"source/chapters/3/emails.md","hash":"116fdedf481cb6a2eaf57736c2478e6991426533","modified":1508930053435},{"_id":"source/chapters/3/events.md","hash":"c3924617f1be95cf1bbaef2e9ef723631e1a7f3c","modified":1508924426387},{"_id":"source/chapters/3/intro.md","hash":"d10d8d0b606a831df5e8a42295bef780276ca8ae","modified":1510317917017},{"_id":"source/chapters/3/linting.md","hash":"513d0a59bc42f4f91f25bb8d79dda2184afdba41","modified":1520282492068},{"_id":"source/chapters/3/methods.md","hash":"e5b3b7cd723f63b3c0a9bb0299705cb1acf50397","modified":1508924890949},{"_id":"source/chapters/3/microservices.md","hash":"2b2f8827964a6f5f0ad7adeb77de86ecf4ddb6ad","modified":1508925651999},{"_id":"source/chapters/3/services.md","hash":"a65fb78269111eb652f080c31a84bdf44c8aca31","modified":1520282492070},{"_id":"source/chapters/3/persistence-layer.md","hash":"3c93d4c402bba22c7c8bcac937990157af9b8515","modified":1508930817604},{"_id":"source/chapters/3/testing.md","hash":"96c85a5da8fac788896193f6f704541dc3acbd69","modified":1508923468963},{"_id":"source/chapters/3/publications.md","hash":"16e3603bc97b064ffa3eae3e046ff2dbc377fa7c","modified":1508924952955},{"_id":"source/chapters/3/conclusion.md","hash":"32e232d95db63356a6af259e7c3e8a4bdab82ece","modified":1510317917013},{"_id":"public/chapters/2/conclusion.html","hash":"c766d6132cfff498c09a0907331c499686e90b5e","modified":1520595083353},{"_id":"public/chapters/3/uploads.html","hash":"4fd659272347953f2658c6e4b74b24825df07a2c","modified":1520595083353},{"_id":"public/chapters/3/conclusion.html","hash":"75297e7a9e2f15989e5db98202283cba70e642de","modified":1520595083352},{"_id":"public/chapters/3/intro.html","hash":"4eb058f128c105a76c0d0cea9d10381e542271ca","modified":1520595083354},{"_id":"public/chapters/3/events.html","hash":"42b7722cead8824fe296f09ac54c3ecfab3cf1b2","modified":1520595083355},{"_id":"public/chapters/3/structure.html","hash":"5521bd59663e20d49961236cfc52726c4fad88f5","modified":1520595083356},{"_id":"public/chapters/3/testing.html","hash":"aa0c89e2d75d04196963790b3842684859b918eb","modified":1520595083356},{"_id":"public/chapters/3/services.html","hash":"ec566820a7b88b7d27beda2b4c30a7b943d8b9e4","modified":1520595083356},{"_id":"public/chapters/3/microservices.html","hash":"82a8037080558323ebc22969361a7420ffac056b","modified":1520595083356},{"_id":"public/chapters/3/emails.html","hash":"92681378d0886ece60603193a979c09290a8f924","modified":1520595083354},{"_id":"public/chapters/3/methods.html","hash":"e3e15e249c24dbe15471a689a4597a68b4cf86e8","modified":1520595083355},{"_id":"public/chapters/3/linting.html","hash":"f9f331a2fe7d763d46d2a6617e17539ce5e5d9f1","modified":1520595083355},{"_id":"public/chapters/3/reusable-modules.html","hash":"9d649a6673142e4ad150db2f491d428a214474f9","modified":1520595083356},{"_id":"public/chapters/3/persistence-layer.html","hash":"7a34b728c4eb1a67db371874aaa89820e3cf5212","modified":1520595083356},{"_id":"public/chapters/3/fixtures.html","hash":"072cc15cf2cddf749beb928543c80de1e7489cbb","modified":1520595083355},{"_id":"public/chapters/3/publications.html","hash":"69c67bc04dfc3f66dfeb0630b3c0e405d222d1eb","modified":1520595083356},{"_id":"public/fonts/percolate.eot","hash":"9f46f6ed3453808cb206b5b8c7c399f8c7c50b4b","modified":1508930984596},{"_id":"public/fonts/percolate.woff","hash":"f475ff4ac3d777c8d56bc7040d6f8576b86398a5","modified":1508930984596},{"_id":"public/fonts/percolate.ttf","hash":"ae93904a100e0c875bb382cb88709de4cd8b6a37","modified":1508930984596},{"_id":"public/images/logo-coralspace-left.svg","hash":"ce3fa809cc5498c23597f68fc7f94d72f0deb453","modified":1508930984596},{"_id":"public/images/favicon.png","hash":"c019ad5135fb00e9d5a8a000a7162f4876cf583b","modified":1508930984596},{"_id":"public/images/icon-white.svg","hash":"037afdf5842532cdc696bd93541be8a273ddf9dc","modified":1508930984597},{"_id":"public/images/OSXsmall.png","hash":"b4bee61cda163ca0df6c258c690e222f3fbebb78","modified":1508930984597},{"_id":"public/images/logo.png","hash":"77000ddb64c4ce7c6052165396c095bf56db02df","modified":1508930984597},{"_id":"public/images/tuxSmall.png","hash":"05a369c1b8db9cc8b2d1e35ee3ec814cceaddc21","modified":1508930984598},{"_id":"public/images/windowsSmall.png","hash":"2c2e638acae06d0c25c0575d1f697a0f8a21a1a0","modified":1508930984598},{"_id":"public/script/main.js","hash":"aa6f1d81ab38e3d8e0dbdef9a574d2330f8ff5a4","modified":1508930984615},{"_id":"public/script/smooth-scroll.min.js","hash":"c80cccc6c68f32b348b3d8459eb89686871b2e38","modified":1508930984615},{"_id":"public/fonts/percolate.svg","hash":"b7c6df677a47fd66316ce90839550bfad6fd462f","modified":1508930984615},{"_id":"public/style/style.css","hash":"8e03b766cac454473fb5f5779a73e34e40669667","modified":1508930984905},{"_id":"source/chapters/2/css.md","hash":"ddaef8a1fb5dd5075073be4b3360ceb5130c439d","modified":1520282492064},{"_id":"source/chapters/4/conclusion.md","hash":"32e232d95db63356a6af259e7c3e8a4bdab82ece","modified":1509703037588},{"_id":"source/chapters/4/emails.md","hash":"116fdedf481cb6a2eaf57736c2478e6991426533","modified":1509703037588},{"_id":"source/chapters/4/events.md","hash":"c3924617f1be95cf1bbaef2e9ef723631e1a7f3c","modified":1509703037596},{"_id":"source/chapters/4/linting.md","hash":"5601084345a3a0a66172706f4ba6ea22491f7954","modified":1509703037604},{"_id":"source/chapters/4/intro.md","hash":"d10d8d0b606a831df5e8a42295bef780276ca8ae","modified":1509705665384},{"_id":"source/chapters/4/methods.md","hash":"e5b3b7cd723f63b3c0a9bb0299705cb1acf50397","modified":1509703037616},{"_id":"source/chapters/4/microservices.md","hash":"2b2f8827964a6f5f0ad7adeb77de86ecf4ddb6ad","modified":1509703037616},{"_id":"source/chapters/4/persistence-layer.md","hash":"3c93d4c402bba22c7c8bcac937990157af9b8515","modified":1509703037616},{"_id":"source/chapters/4/publications.md","hash":"16e3603bc97b064ffa3eae3e046ff2dbc377fa7c","modified":1509703037616},{"_id":"source/chapters/4/reusable-modules.md","hash":"2d7cfc86989315c5172e18bfe55021dd772cd65c","modified":1509703037616},{"_id":"source/chapters/4/services.md","hash":"60bb9191fca8513917d5be4458464ceb5d8abbb7","modified":1509703037616},{"_id":"source/chapters/4/testing.md","hash":"96c85a5da8fac788896193f6f704541dc3acbd69","modified":1509703037624},{"_id":"source/chapters/4/uploads.md","hash":"86e6dd86cec2befeba48524c49a130a693ffa42b","modified":1509703037624},{"_id":"source/chapters/4/fixtures.md","hash":"dc1e1ef0513c62a4d043265f5bf60ba0e69f746f","modified":1509703037604},{"_id":"source/chapters/4/structure.md","hash":"7a756a3191eb69e172c2a98255a868769524bcae","modified":1509703037616},{"_id":"source/chapters/3/react.md","hash":"c9946991b722c8e858ce1cad425bc72b39ad005c","modified":1496388591576},{"_id":"public/sitemap.xml","hash":"d543bfaed1a70db8af5c4c973fa0b038aff8569b","modified":1522417804546},{"_id":"public/chapters/2/css.html","hash":"3d472d470dbfaf4b76d24643a24e2d0130f38356","modified":1520595083356}],"Category":[],"Data":[],"Page":[{"title":"Hello","description":"Getting started with Meteor","disqusPage":"Chapter 1: Intro","_content":"\n## Installing the framework\n\nTo install the framework, please follow the steps from the meteor official website: [meteor.com](https://www.meteor.com/install)\n\n## Tools\nThe tool that works best for us, and the one which we recommend you to use is WebStorm from Jetbrains.\nYou can download Webstorm from [here](https://www.jetbrains.com/webstorm/download).\n\nTo learn more about Webstorm and how to better use it, [go here](https://www.jetbrains.com/webstorm/documentation/).\n\nA free alternative to Webstorm would be VSCode. This is an extensible, feature-rich code editor that is\nrelatively easy to learn. \nYou can download it from [here](https://code.visualstudio.com/).\n\n## Creating a project\n\nAfter you have installed Meteor, you can easily create a new project by running the following command in \nyour terminal:\n\n```\nmeteor create myProjectName\n```\n\n## Start Meteor\n\nNow you have to start up the project you've just created by running the following commands:\n\n```\ncd myProjectName\nmeteor run\n```\n\nThat's it! You've just created your first project in Meteor!\nNow, to view your work, type *http://localhost:3000* in your browser's address bar and hit Enter.\nYes ! It's **THAT** easy!\n\n## Application folder structure\n\nThis is the basic folder structure that an application, such as the one you've just created, should be made up of:\n\n\n\n```bash\nclient\n main.js # this file contains: /imports/startup/client\ndb # This is where our persistence layer is. Including external APIs\nimports\n ui # contains anything User-Interface related\n startup\n client\n index.js # loads everything that is needed for the client to function (ex: routes, css, anything concerning the client)\n server\n index.js # loads everything that is needed for the server to function\n api # contains the rest\n server\n main.js # this file contains: import /imports/startup/server\n```\nAs a reminder, you could always use the github repository. For that, go [here](https://github.com/cult-of-coders/meteor-tuts-tutorial/tree/1-clean-installation)\n\nEverything in \"/client\" and \"/server\" is automatically loaded, as it is explained [here.](https://guide.meteor.com/structure.html#load-order)\n\nIn general we will need more control over our application, and in order to gain that control\nwe will refrain from using auto-loading in our projects.\n\nThis compiles all the modules we specified into our project with the \"import\" keyword, when we type **\"meteor run\"** \ninto the command line, but does not load all of them. \n\nBecause of this, everything in \"/imports\" must be explicitly imported so Meteor can \"gain knowledge\" about it.\nThis also gives us the ability to build modular applications.\n[Read more about this here.](https://danmartensen.svbtle.com/build-better-apps-with-es6-modules)\n\n\n## Importing from NPM\n\nFor now, we won't get into a lot of details, but the basic idea is that with Meteor you have access to the\n full NPM ecosystem, allowing you to import modules from http://www.npmjs.com.\n\n\"moment\" is a library you will use when working with dates and times in your projects.\n\nNow, open a new terminal (leave the one with server open) and in the root directory of the project we are working on, write\n\n```\nmeteor npm install --save moment\n```\n\nWe use \"--save\" here because it will save it in our packages.json, meaning that if we work in collaboration with\n other developers, when they download the project to start working on it, they will have the same package \n installed, with the specified version thus making their life much easier. \n\nTo use \"moment\" in your project, at a basic level, use this code snippet:\n\n```\n// file: (for example) /imports/startup/server/index.js\nimport moment from 'moment';\n\nconst date = moment(new Date()).format('YYYY-MM-DD')\nconsole.log(date)\n```\n\nIf added to the server side you will see the current date printed on your terminal, otherwise, if added client side you will see it on the browser's console.\n\n## Atmosphere\n\nAtmosphere is a package manager which is specific to Meteor only, just like npm is for NodeJs. \nIt helps you manage your project's dependencies easily.\nYou can find great resources to help you with Collections, Server-Side Routes, Mailing, etc.\n\nYou can learn more about it on the project's [webpage](https://atmospherejs.com).\n\nAdding a package is as simple as typing this into the console:\n```\nmeteor add accounts-base accounts-password\n\n```\n\nThat's it. You now have added an user management package in in your application. And it's ready for you to use it!\nThere are a lot of cool and useful packages out there that we'll explore during these tutorials.\n\n## Importing from Atmosphere\nYou can also use the modular approach with Meteor packages!\n\nTo import an atmosphere package we prefix it with `meteor/`\n\n```js\nimport { Accounts } from 'meteor/accounts-base'\n\n```\n\nDon't worry too much about this, we are going to explore them later in this tutorial.\n\n## Importing from local files\n\nAs you saw in the example for creating the project's folder structure, we have the ability to import from \nlocal files by using an \"absolute path\":\n```\nimport myService from '/imports/startup/server/myService';\n```\n\nYou can also use a relative path:\n```\nimport {sum} from './helpers';\n```\nIn order to see how this exactly works in an example, you can go on the github repository, [here](https://github.com/cult-of-coders/meteor-tuts-tutorial)\nPretty easy right ?\n\nWould you like to find more about importing and exporting ? You can read more here:\n- [http://exploringjs.com/es6/ch_modules.html](http://exploringjs.com/es6/ch_modules.html)\n- [https://stackoverflow.com/questions/25494365/es6-module-export-options/34842087#34842087](https://stackoverflow.com/questions/25494365/es6-module-export-options/34842087#34842087)\n\n\n## Need Help ?\n\nIf you get stuck and need help, you can ask other Meteor evangelists out there:\n- https://forums.meteor.com\n- Go to the #meteor channel on FreeNode Server: [https://webchat.freenode.net/](https://webchat.freenode.net/)\n- Contact Cult of Coders for consultancy, [here](https://www.cultofcoders.com/contact)\n\nMake sure you Google your questions first, to find out if somebody had the same problem as well \n(and most likely has found an answer to that problem), before asking the community!\n\n\n","source":"chapters/1/intro.md","raw":"---\ntitle: 'Hello'\ndescription: Getting started with Meteor\ndisqusPage: 'Chapter 1: Intro'\n---\n\n## Installing the framework\n\nTo install the framework, please follow the steps from the meteor official website: [meteor.com](https://www.meteor.com/install)\n\n## Tools\nThe tool that works best for us, and the one which we recommend you to use is WebStorm from Jetbrains.\nYou can download Webstorm from [here](https://www.jetbrains.com/webstorm/download).\n\nTo learn more about Webstorm and how to better use it, [go here](https://www.jetbrains.com/webstorm/documentation/).\n\nA free alternative to Webstorm would be VSCode. This is an extensible, feature-rich code editor that is\nrelatively easy to learn. \nYou can download it from [here](https://code.visualstudio.com/).\n\n## Creating a project\n\nAfter you have installed Meteor, you can easily create a new project by running the following command in \nyour terminal:\n\n```\nmeteor create myProjectName\n```\n\n## Start Meteor\n\nNow you have to start up the project you've just created by running the following commands:\n\n```\ncd myProjectName\nmeteor run\n```\n\nThat's it! You've just created your first project in Meteor!\nNow, to view your work, type *http://localhost:3000* in your browser's address bar and hit Enter.\nYes ! It's **THAT** easy!\n\n## Application folder structure\n\nThis is the basic folder structure that an application, such as the one you've just created, should be made up of:\n\n\n\n```bash\nclient\n main.js # this file contains: /imports/startup/client\ndb # This is where our persistence layer is. Including external APIs\nimports\n ui # contains anything User-Interface related\n startup\n client\n index.js # loads everything that is needed for the client to function (ex: routes, css, anything concerning the client)\n server\n index.js # loads everything that is needed for the server to function\n api # contains the rest\n server\n main.js # this file contains: import /imports/startup/server\n```\nAs a reminder, you could always use the github repository. For that, go [here](https://github.com/cult-of-coders/meteor-tuts-tutorial/tree/1-clean-installation)\n\nEverything in \"/client\" and \"/server\" is automatically loaded, as it is explained [here.](https://guide.meteor.com/structure.html#load-order)\n\nIn general we will need more control over our application, and in order to gain that control\nwe will refrain from using auto-loading in our projects.\n\nThis compiles all the modules we specified into our project with the \"import\" keyword, when we type **\"meteor run\"** \ninto the command line, but does not load all of them. \n\nBecause of this, everything in \"/imports\" must be explicitly imported so Meteor can \"gain knowledge\" about it.\nThis also gives us the ability to build modular applications.\n[Read more about this here.](https://danmartensen.svbtle.com/build-better-apps-with-es6-modules)\n\n\n## Importing from NPM\n\nFor now, we won't get into a lot of details, but the basic idea is that with Meteor you have access to the\n full NPM ecosystem, allowing you to import modules from http://www.npmjs.com.\n\n\"moment\" is a library you will use when working with dates and times in your projects.\n\nNow, open a new terminal (leave the one with server open) and in the root directory of the project we are working on, write\n\n```\nmeteor npm install --save moment\n```\n\nWe use \"--save\" here because it will save it in our packages.json, meaning that if we work in collaboration with\n other developers, when they download the project to start working on it, they will have the same package \n installed, with the specified version thus making their life much easier. \n\nTo use \"moment\" in your project, at a basic level, use this code snippet:\n\n```\n// file: (for example) /imports/startup/server/index.js\nimport moment from 'moment';\n\nconst date = moment(new Date()).format('YYYY-MM-DD')\nconsole.log(date)\n```\n\nIf added to the server side you will see the current date printed on your terminal, otherwise, if added client side you will see it on the browser's console.\n\n## Atmosphere\n\nAtmosphere is a package manager which is specific to Meteor only, just like npm is for NodeJs. \nIt helps you manage your project's dependencies easily.\nYou can find great resources to help you with Collections, Server-Side Routes, Mailing, etc.\n\nYou can learn more about it on the project's [webpage](https://atmospherejs.com).\n\nAdding a package is as simple as typing this into the console:\n```\nmeteor add accounts-base accounts-password\n\n```\n\nThat's it. You now have added an user management package in in your application. And it's ready for you to use it!\nThere are a lot of cool and useful packages out there that we'll explore during these tutorials.\n\n## Importing from Atmosphere\nYou can also use the modular approach with Meteor packages!\n\nTo import an atmosphere package we prefix it with `meteor/`\n\n```js\nimport { Accounts } from 'meteor/accounts-base'\n\n```\n\nDon't worry too much about this, we are going to explore them later in this tutorial.\n\n## Importing from local files\n\nAs you saw in the example for creating the project's folder structure, we have the ability to import from \nlocal files by using an \"absolute path\":\n```\nimport myService from '/imports/startup/server/myService';\n```\n\nYou can also use a relative path:\n```\nimport {sum} from './helpers';\n```\nIn order to see how this exactly works in an example, you can go on the github repository, [here](https://github.com/cult-of-coders/meteor-tuts-tutorial)\nPretty easy right ?\n\nWould you like to find more about importing and exporting ? You can read more here:\n- [http://exploringjs.com/es6/ch_modules.html](http://exploringjs.com/es6/ch_modules.html)\n- [https://stackoverflow.com/questions/25494365/es6-module-export-options/34842087#34842087](https://stackoverflow.com/questions/25494365/es6-module-export-options/34842087#34842087)\n\n\n## Need Help ?\n\nIf you get stuck and need help, you can ask other Meteor evangelists out there:\n- https://forums.meteor.com\n- Go to the #meteor channel on FreeNode Server: [https://webchat.freenode.net/](https://webchat.freenode.net/)\n- Contact Cult of Coders for consultancy, [here](https://www.cultofcoders.com/contact)\n\nMake sure you Google your questions first, to find out if somebody had the same problem as well \n(and most likely has found an answer to that problem), before asking the community!\n\n\n","date":"2018-03-09T11:28:33.091Z","updated":"2018-03-09T11:28:33.090Z","path":"chapters/1/intro.html","_id":"ciuqri0q20001ufjxcqpoqtlv","comments":1,"layout":"page","content":"
To install the framework, please follow the steps from the meteor official website: meteor.com
\nThe tool that works best for us, and the one which we recommend you to use is WebStorm from Jetbrains.\nYou can download Webstorm from here.
\nTo learn more about Webstorm and how to better use it, go here.
\nA free alternative to Webstorm would be VSCode. This is an extensible, feature-rich code editor that is\nrelatively easy to learn.
You can download it from here.
After you have installed Meteor, you can easily create a new project by running the following command in \nyour terminal:
\n1 | meteor create myProjectName |
Now you have to start up the project you’ve just created by running the following commands:
\n1 | cd myProjectName |
That’s it! You’ve just created your first project in Meteor!\nNow, to view your work, type http://localhost:3000 in your browser’s address bar and hit Enter.\nYes ! It’s THAT easy!
\nThis is the basic folder structure that an application, such as the one you’ve just created, should be made up of:
\n1 | client |
As a reminder, you could always use the github repository. For that, go here
\nEverything in “/client” and “/server” is automatically loaded, as it is explained here.
\nIn general we will need more control over our application, and in order to gain that control\nwe will refrain from using auto-loading in our projects.
\nThis compiles all the modules we specified into our project with the “import” keyword, when we type “meteor run” \ninto the command line, but does not load all of them.
\nBecause of this, everything in “/imports” must be explicitly imported so Meteor can “gain knowledge” about it.\nThis also gives us the ability to build modular applications.\nRead more about this here.
\nFor now, we won’t get into a lot of details, but the basic idea is that with Meteor you have access to the\n full NPM ecosystem, allowing you to import modules from http://www.npmjs.com.
\n“moment” is a library you will use when working with dates and times in your projects.
\nNow, open a new terminal (leave the one with server open) and in the root directory of the project we are working on, write
\n1 | meteor npm install --save moment |
We use “–save” here because it will save it in our packages.json, meaning that if we work in collaboration with\n other developers, when they download the project to start working on it, they will have the same package \n installed, with the specified version thus making their life much easier.
\nTo use “moment” in your project, at a basic level, use this code snippet:
\n1 | // file: (for example) /imports/startup/server/index.js |
If added to the server side you will see the current date printed on your terminal, otherwise, if added client side you will see it on the browser’s console.
\nAtmosphere is a package manager which is specific to Meteor only, just like npm is for NodeJs. \nIt helps you manage your project’s dependencies easily.\nYou can find great resources to help you with Collections, Server-Side Routes, Mailing, etc.
\nYou can learn more about it on the project’s webpage.
\nAdding a package is as simple as typing this into the console:\n1
meteor add accounts-base accounts-password
That’s it. You now have added an user management package in in your application. And it’s ready for you to use it!\nThere are a lot of cool and useful packages out there that we’ll explore during these tutorials.
\nYou can also use the modular approach with Meteor packages!
\nTo import an atmosphere package we prefix it with meteor/
1 | import { Accounts } from 'meteor/accounts-base' |
Don’t worry too much about this, we are going to explore them later in this tutorial.
\nAs you saw in the example for creating the project’s folder structure, we have the ability to import from \nlocal files by using an “absolute path”:\n1
import myService from '/imports/startup/server/myService';
You can also use a relative path:\n1
import {sum} from './helpers';
In order to see how this exactly works in an example, you can go on the github repository, here\nPretty easy right ?
\nWould you like to find more about importing and exporting ? You can read more here:
\nIf you get stuck and need help, you can ask other Meteor evangelists out there:
\nMake sure you Google your questions first, to find out if somebody had the same problem as well \n(and most likely has found an answer to that problem), before asking the community!
\n","site":{"data":{}},"excerpt":"","more":"To install the framework, please follow the steps from the meteor official website: meteor.com
\nThe tool that works best for us, and the one which we recommend you to use is WebStorm from Jetbrains.\nYou can download Webstorm from here.
\nTo learn more about Webstorm and how to better use it, go here.
\nA free alternative to Webstorm would be VSCode. This is an extensible, feature-rich code editor that is\nrelatively easy to learn.
You can download it from here.
After you have installed Meteor, you can easily create a new project by running the following command in \nyour terminal:
\n1 | meteor create myProjectName |
Now you have to start up the project you’ve just created by running the following commands:
\n1 | cd myProjectName |
That’s it! You’ve just created your first project in Meteor!\nNow, to view your work, type http://localhost:3000 in your browser’s address bar and hit Enter.\nYes ! It’s THAT easy!
\nThis is the basic folder structure that an application, such as the one you’ve just created, should be made up of:
\n1 | client |
As a reminder, you could always use the github repository. For that, go here
\nEverything in “/client” and “/server” is automatically loaded, as it is explained here.
\nIn general we will need more control over our application, and in order to gain that control\nwe will refrain from using auto-loading in our projects.
\nThis compiles all the modules we specified into our project with the “import” keyword, when we type “meteor run” \ninto the command line, but does not load all of them.
\nBecause of this, everything in “/imports” must be explicitly imported so Meteor can “gain knowledge” about it.\nThis also gives us the ability to build modular applications.\nRead more about this here.
\nFor now, we won’t get into a lot of details, but the basic idea is that with Meteor you have access to the\n full NPM ecosystem, allowing you to import modules from http://www.npmjs.com.
\n“moment” is a library you will use when working with dates and times in your projects.
\nNow, open a new terminal (leave the one with server open) and in the root directory of the project we are working on, write
\n1 | meteor npm install --save moment |
We use “–save” here because it will save it in our packages.json, meaning that if we work in collaboration with\n other developers, when they download the project to start working on it, they will have the same package \n installed, with the specified version thus making their life much easier.
\nTo use “moment” in your project, at a basic level, use this code snippet:
\n1 | // file: (for example) /imports/startup/server/index.js |
If added to the server side you will see the current date printed on your terminal, otherwise, if added client side you will see it on the browser’s console.
\nAtmosphere is a package manager which is specific to Meteor only, just like npm is for NodeJs. \nIt helps you manage your project’s dependencies easily.\nYou can find great resources to help you with Collections, Server-Side Routes, Mailing, etc.
\nYou can learn more about it on the project’s webpage.
\nAdding a package is as simple as typing this into the console:\n1
meteor add accounts-base accounts-password
That’s it. You now have added an user management package in in your application. And it’s ready for you to use it!\nThere are a lot of cool and useful packages out there that we’ll explore during these tutorials.
\nYou can also use the modular approach with Meteor packages!
\nTo import an atmosphere package we prefix it with meteor/
1 | import { Accounts } from 'meteor/accounts-base' |
Don’t worry too much about this, we are going to explore them later in this tutorial.
\nAs you saw in the example for creating the project’s folder structure, we have the ability to import from \nlocal files by using an “absolute path”:\n1
import myService from '/imports/startup/server/myService';
You can also use a relative path:\n1
import {sum} from './helpers';
In order to see how this exactly works in an example, you can go on the github repository, here\nPretty easy right ?
\nWould you like to find more about importing and exporting ? You can read more here:
\nIf you get stuck and need help, you can ask other Meteor evangelists out there:
\nMake sure you Google your questions first, to find out if somebody had the same problem as well \n(and most likely has found an answer to that problem), before asking the community!
\n"},{"title":"Collections","description":"How we store our data in Meteor.","disqusPage":"Chapter 1: Collections","_content":"\n## Let's talk data!\n\nMeteor uses MongoDB as its default database. You can use any database you want, because you have access to\n*http://www.npmjs.com*, therefore you have access to almost all the existing database drivers out there \n(a database driver is a program which implements a protocol for connecting to a specific database, like MongoDB, MySQL, SqLite and so on). \n\n## How to create a collection?\n\n```js\n// imports/db/posts/collection.js\nimport { Mongo } from 'meteor/mongo';\n\nconst Posts = new Mongo.Collection('posts');\n\nexport default Posts;\n```\n\n## Inserting data\n\nWe already learned how to create and use a method so we are going to insert in the database a Post\n```js\n// file: /imports/api/posts/methods.js\n// do not forget to import it in the '/imports/startup/server/index.js';\nimport {Meteor} from 'meteor/meteor'\nimport Posts from '/imports/db/posts'; // or, as it's on github, for simplicity, import {Posts} from '/db';\n\nMeteor.methods({\n 'post.create'(post) {\n Posts.insert(post);\n }\n});\n\n```\nTo test this, you can go in the browser's console and try something like this:\n```js\nconst data = {title: 'test', description: 'some description'};\nMeteor.call('post.create', data); // this will call the 'post.create' method and will add a post in the database\n```\nWe can add in the database whatever we want, from strings to array of objects. The sky is the limit!\n\n## Mongo tools - check what's in the database\nYou could go in a new terminal, go in the root directory of your project and then `meteor mongo` or use an application like [robomongo](https://robomongo.org/) so you can visualize the data from your database\n\nIf we choose to use the terminal and run the command written above, we could check all the posts from the database by typing: `db.posts.find({})`, this will search for all the posts from the database and retrieve an array of objects of there are any.\n\n## Find all posts\n```js\n// file: /imports/api/posts/methods.js\n 'post.list' () {\n return Posts.find().fetch();\n }\n```\n\nTo be able to query MongoDB efficiently, you need to know some things about selectors (meaning what are you going to select from your collection).\nRead [this](https://docs.mongodb.com/manual/reference/operator/query/#query-and-projection-operators) to get acquainted with them.\n\nThe second argument for `find()` is useful for sorting, limiting the amount of received documents, to get only specific fields and many others. Read [this](https://docs.meteor.com/api/collections.html#Mongo-Collection-find) for more details\n\n## Updating a post\n```js\n 'post.edit' (_id, post) {\n Posts.update(_id, {\n $set: {\n title: post.title,\n description: post.description\n }\n });\n }\n```\n\nThe first argument represents the data that you \"want to update\", and the second tells the system \"how you update it\".\nThe idea is you can do a lot of things, and the options are endless.\nSo if you want to read more about updating in MongoDB, go [here](https://docs.mongodb.com/manual/reference/operator/update/).\n## Remove a post with a specific _id\n```js\n 'post.remove' (_id){\n Posts.remove(_id);\n }\n```\n\n## Fetch a post with a specific _id\n```js\n 'post.get' (_id) {\n return Posts.findOne(_id);\n }\n```\n\n## Work more to learn more\n\nNow let's practice what we've learned in this section with a few simple exercises:\n\nWe'll consider a post in the following format:\n```js\n\nconst post = {\ntitle: 'something', // string\ndescription: 'meteor-tuts', // string\nisApproved: true, // boolean\nviews: 10,\nlinkedPostIds: ['aaa', 'bbb', 'ccc', 'ddd'],\ncreatedAt: new Date // Date\n}\n```\n#### 1. Posts with a specific title\nMake a method which returns all the posts with title: 'test'\n```js\nMeteor.call('post.get_by_title', callback)\n```\n\n#### 2. Posts with views\nMake a method which returns all the posts that have between 100 and 200 views and that returns only the title (as fields)\n```js\nMeteor.call('post.get_by_views', {title: 'test', views: 300}, callback)\n```\n\n#### 3. Removing posts\nMake a method that removes all the posts with title: 'test' or description: 'test'\n```js\nMeteor.call('post.remove_by_test'm callback)\n```\n\n#### 4. Updating posts\nMake a method that increments the number of views by 1 for each post that is approved\n```js\nMeteor.call('post.increment_views', callback)\n```\n\n#### 5. Remove old posts\nMake a method which removes all the posts from last month (from the first day of month, to the last day of that month)\n```js\nMeteor.call('post.remove_from_last_month', callback)\n```\n\n\n\n","source":"chapters/1/collections.md","raw":"---\ntitle: Collections\ndescription: How we store our data in Meteor.\ndisqusPage: 'Chapter 1: Collections'\n---\n\n## Let's talk data!\n\nMeteor uses MongoDB as its default database. You can use any database you want, because you have access to\n*http://www.npmjs.com*, therefore you have access to almost all the existing database drivers out there \n(a database driver is a program which implements a protocol for connecting to a specific database, like MongoDB, MySQL, SqLite and so on). \n\n## How to create a collection?\n\n```js\n// imports/db/posts/collection.js\nimport { Mongo } from 'meteor/mongo';\n\nconst Posts = new Mongo.Collection('posts');\n\nexport default Posts;\n```\n\n## Inserting data\n\nWe already learned how to create and use a method so we are going to insert in the database a Post\n```js\n// file: /imports/api/posts/methods.js\n// do not forget to import it in the '/imports/startup/server/index.js';\nimport {Meteor} from 'meteor/meteor'\nimport Posts from '/imports/db/posts'; // or, as it's on github, for simplicity, import {Posts} from '/db';\n\nMeteor.methods({\n 'post.create'(post) {\n Posts.insert(post);\n }\n});\n\n```\nTo test this, you can go in the browser's console and try something like this:\n```js\nconst data = {title: 'test', description: 'some description'};\nMeteor.call('post.create', data); // this will call the 'post.create' method and will add a post in the database\n```\nWe can add in the database whatever we want, from strings to array of objects. The sky is the limit!\n\n## Mongo tools - check what's in the database\nYou could go in a new terminal, go in the root directory of your project and then `meteor mongo` or use an application like [robomongo](https://robomongo.org/) so you can visualize the data from your database\n\nIf we choose to use the terminal and run the command written above, we could check all the posts from the database by typing: `db.posts.find({})`, this will search for all the posts from the database and retrieve an array of objects of there are any.\n\n## Find all posts\n```js\n// file: /imports/api/posts/methods.js\n 'post.list' () {\n return Posts.find().fetch();\n }\n```\n\nTo be able to query MongoDB efficiently, you need to know some things about selectors (meaning what are you going to select from your collection).\nRead [this](https://docs.mongodb.com/manual/reference/operator/query/#query-and-projection-operators) to get acquainted with them.\n\nThe second argument for `find()` is useful for sorting, limiting the amount of received documents, to get only specific fields and many others. Read [this](https://docs.meteor.com/api/collections.html#Mongo-Collection-find) for more details\n\n## Updating a post\n```js\n 'post.edit' (_id, post) {\n Posts.update(_id, {\n $set: {\n title: post.title,\n description: post.description\n }\n });\n }\n```\n\nThe first argument represents the data that you \"want to update\", and the second tells the system \"how you update it\".\nThe idea is you can do a lot of things, and the options are endless.\nSo if you want to read more about updating in MongoDB, go [here](https://docs.mongodb.com/manual/reference/operator/update/).\n## Remove a post with a specific _id\n```js\n 'post.remove' (_id){\n Posts.remove(_id);\n }\n```\n\n## Fetch a post with a specific _id\n```js\n 'post.get' (_id) {\n return Posts.findOne(_id);\n }\n```\n\n## Work more to learn more\n\nNow let's practice what we've learned in this section with a few simple exercises:\n\nWe'll consider a post in the following format:\n```js\n\nconst post = {\ntitle: 'something', // string\ndescription: 'meteor-tuts', // string\nisApproved: true, // boolean\nviews: 10,\nlinkedPostIds: ['aaa', 'bbb', 'ccc', 'ddd'],\ncreatedAt: new Date // Date\n}\n```\n#### 1. Posts with a specific title\nMake a method which returns all the posts with title: 'test'\n```js\nMeteor.call('post.get_by_title', callback)\n```\n\n#### 2. Posts with views\nMake a method which returns all the posts that have between 100 and 200 views and that returns only the title (as fields)\n```js\nMeteor.call('post.get_by_views', {title: 'test', views: 300}, callback)\n```\n\n#### 3. Removing posts\nMake a method that removes all the posts with title: 'test' or description: 'test'\n```js\nMeteor.call('post.remove_by_test'm callback)\n```\n\n#### 4. Updating posts\nMake a method that increments the number of views by 1 for each post that is approved\n```js\nMeteor.call('post.increment_views', callback)\n```\n\n#### 5. Remove old posts\nMake a method which removes all the posts from last month (from the first day of month, to the last day of that month)\n```js\nMeteor.call('post.remove_from_last_month', callback)\n```\n\n\n\n","date":"2018-03-08T15:53:49.078Z","updated":"2018-03-08T15:53:49.078Z","path":"chapters/1/collections.html","_id":"ciure80fa0001qtjxsc1d0ona","comments":1,"layout":"page","content":"Meteor uses MongoDB as its default database. You can use any database you want, because you have access to\nhttp://www.npmjs.com, therefore you have access to almost all the existing database drivers out there \n(a database driver is a program which implements a protocol for connecting to a specific database, like MongoDB, MySQL, SqLite and so on).
\n1 | // imports/db/posts/collection.js |
We already learned how to create and use a method so we are going to insert in the database a Post\n1
2
3
4
5
6
7
8
9
10// file: /imports/api/posts/methods.js
// do not forget to import it in the '/imports/startup/server/index.js';
import {Meteor} from 'meteor/meteor'
import Posts from '/imports/db/posts'; // or, as it's on github, for simplicity, import {Posts} from '/db';
Meteor.methods({
'post.create'(post) {
Posts.insert(post);
}
});
To test this, you can go in the browser’s console and try something like this:\n1
2const data = {title: 'test', description: 'some description'};
Meteor.call('post.create', data); // this will call the 'post.create' method and will add a post in the database
We can add in the database whatever we want, from strings to array of objects. The sky is the limit!
\nYou could go in a new terminal, go in the root directory of your project and then meteor mongo
or use an application like robomongo so you can visualize the data from your database
If we choose to use the terminal and run the command written above, we could check all the posts from the database by typing: db.posts.find({})
, this will search for all the posts from the database and retrieve an array of objects of there are any.
1 | // file: /imports/api/posts/methods.js |
To be able to query MongoDB efficiently, you need to know some things about selectors (meaning what are you going to select from your collection).\nRead this to get acquainted with them.
\nThe second argument for find()
is useful for sorting, limiting the amount of received documents, to get only specific fields and many others. Read this for more details
1 | 'post.edit' (_id, post) { |
The first argument represents the data that you “want to update”, and the second tells the system “how you update it”.\nThe idea is you can do a lot of things, and the options are endless.\nSo if you want to read more about updating in MongoDB, go here.
\n1 | 'post.remove' (_id){ |
1 | 'post.get' (_id) { |
Now let’s practice what we’ve learned in this section with a few simple exercises:
\nWe’ll consider a post in the following format:\n1
2
3
4
5
6
7
8
9
const post = {
title: 'something', // string
description: 'meteor-tuts', // string
isApproved: true, // boolean
views: 10,
linkedPostIds: ['aaa', 'bbb', 'ccc', 'ddd'],
createdAt: new Date // Date
}
Make a method which returns all the posts with title: ‘test’\n1
Meteor.call('post.get_by_title', callback)
Make a method which returns all the posts that have between 100 and 200 views and that returns only the title (as fields)\n1
Meteor.call('post.get_by_views', {title: 'test', views: 300}, callback)
Make a method that removes all the posts with title: ‘test’ or description: ‘test’\n1
Meteor.call('post.remove_by_test'm callback)
Make a method that increments the number of views by 1 for each post that is approved\n1
Meteor.call('post.increment_views', callback)
Make a method which removes all the posts from last month (from the first day of month, to the last day of that month)\n1
Meteor.call('post.remove_from_last_month', callback)
Meteor uses MongoDB as its default database. You can use any database you want, because you have access to\nhttp://www.npmjs.com, therefore you have access to almost all the existing database drivers out there \n(a database driver is a program which implements a protocol for connecting to a specific database, like MongoDB, MySQL, SqLite and so on).
\n1 | // imports/db/posts/collection.js |
We already learned how to create and use a method so we are going to insert in the database a Post\n1
2
3
4
5
6
7
8
9
10// file: /imports/api/posts/methods.js
// do not forget to import it in the '/imports/startup/server/index.js';
import {Meteor} from 'meteor/meteor'
import Posts from '/imports/db/posts'; // or, as it's on github, for simplicity, import {Posts} from '/db';
Meteor.methods({
'post.create'(post) {
Posts.insert(post);
}
});
To test this, you can go in the browser’s console and try something like this:\n1
2const data = {title: 'test', description: 'some description'};
Meteor.call('post.create', data); // this will call the 'post.create' method and will add a post in the database
We can add in the database whatever we want, from strings to array of objects. The sky is the limit!
\nYou could go in a new terminal, go in the root directory of your project and then meteor mongo
or use an application like robomongo so you can visualize the data from your database
If we choose to use the terminal and run the command written above, we could check all the posts from the database by typing: db.posts.find({})
, this will search for all the posts from the database and retrieve an array of objects of there are any.
1 | // file: /imports/api/posts/methods.js |
To be able to query MongoDB efficiently, you need to know some things about selectors (meaning what are you going to select from your collection).\nRead this to get acquainted with them.
\nThe second argument for find()
is useful for sorting, limiting the amount of received documents, to get only specific fields and many others. Read this for more details
1 | 'post.edit' (_id, post) { |
The first argument represents the data that you “want to update”, and the second tells the system “how you update it”.\nThe idea is you can do a lot of things, and the options are endless.\nSo if you want to read more about updating in MongoDB, go here.
\n1 | 'post.remove' (_id){ |
1 | 'post.get' (_id) { |
Now let’s practice what we’ve learned in this section with a few simple exercises:
\nWe’ll consider a post in the following format:\n1
2
3
4
5
6
7
8
9
const post = {
title: 'something', // string
description: 'meteor-tuts', // string
isApproved: true, // boolean
views: 10,
linkedPostIds: ['aaa', 'bbb', 'ccc', 'ddd'],
createdAt: new Date // Date
}
Make a method which returns all the posts with title: ‘test’\n1
Meteor.call('post.get_by_title', callback)
Make a method which returns all the posts that have between 100 and 200 views and that returns only the title (as fields)\n1
Meteor.call('post.get_by_views', {title: 'test', views: 300}, callback)
Make a method that removes all the posts with title: ‘test’ or description: ‘test’\n1
Meteor.call('post.remove_by_test'm callback)
Make a method that increments the number of views by 1 for each post that is approved\n1
Meteor.call('post.increment_views', callback)
Make a method which removes all the posts from last month (from the first day of month, to the last day of that month)\n1
Meteor.call('post.remove_from_last_month', callback)
Well, you are probably already asking yourself what is an RPC. Even if you have already googled the term, allow us to explain to you what an RPC is.
\nFirst of all, RPC stands for “Remote Procedure Call”, which basically means that you invoke “something” with some arguments ( only if you want to use arguments, of course).\nand you expect a response, just like you would do when in a conversation.
\nIn Meteor, the usual and most recommended way of doing this, is by communicating through a websocket with the server.
\nSo what actions we can do with RPC’s ?
\n1 | // file /imports/api/methods.js |
Do not forget to import '/imports/api/methods.js'
in the server’s startup file: /imports/startup/server/index.js
otherwise meteor won’t know of the existence of this file
Methods can be called from the server-side or from the client-side. Let’s try it in the browser’s console:
\n1 | Meteor.call('find.random_number', 25, 50, (err, result ) => { |
To fully understand the reason why we added 2 arguments err
and result
, go ahead and read the explanation here
Let’s cause an error and see what happens:\nType this code in the /imports/api/methods.js file:3\n1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16// file /imports/api/methods.js
import { Meteor } from 'meteor/meteor';
Meteor.methods({
'method.checkString'(myString) {
if (myString === 'exception') {
throw new Meteor.Error(500, 'An error has occurred', 'You are not allowed to enter this string');
}
return true;
}
// Arguments for throw new Meteor.Error:
// The first argument (error): is something very general, can be a number like 500, 404,
// The second argument (reason): is a description of the error
// The third argument (details): is for providing more details about the error.
})
Now, make a method call in your browser’s console to method.checkString
and catch the exception:
1 | Meteor.call('method.checkString', 'exception', (err, result) => { |
Well, you are probably already asking yourself what is an RPC. Even if you have already googled the term, allow us to explain to you what an RPC is.
\nFirst of all, RPC stands for “Remote Procedure Call”, which basically means that you invoke “something” with some arguments ( only if you want to use arguments, of course).\nand you expect a response, just like you would do when in a conversation.
\nIn Meteor, the usual and most recommended way of doing this, is by communicating through a websocket with the server.
\nSo what actions we can do with RPC’s ?
\n1 | // file /imports/api/methods.js |
Do not forget to import '/imports/api/methods.js'
in the server’s startup file: /imports/startup/server/index.js
otherwise meteor won’t know of the existence of this file
Methods can be called from the server-side or from the client-side. Let’s try it in the browser’s console:
\n1 | Meteor.call('find.random_number', 25, 50, (err, result ) => { |
To fully understand the reason why we added 2 arguments err
and result
, go ahead and read the explanation here
Let’s cause an error and see what happens:\nType this code in the /imports/api/methods.js file:3\n1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16// file /imports/api/methods.js
import { Meteor } from 'meteor/meteor';
Meteor.methods({
'method.checkString'(myString) {
if (myString === 'exception') {
throw new Meteor.Error(500, 'An error has occurred', 'You are not allowed to enter this string');
}
return true;
}
// Arguments for throw new Meteor.Error:
// The first argument (error): is something very general, can be a number like 500, 404,
// The second argument (reason): is a description of the error
// The third argument (details): is for providing more details about the error.
})
Now, make a method call in your browser’s console to method.checkString
and catch the exception:
1 | Meteor.call('method.checkString', 'exception', (err, result) => { |
First of all, welcome aboard and congratulations on your choice of embarking on this journey. But before we start, let me tell you a little story:
\nAs years passed by and humanity evolved, apps have literally changed the way in which the world works, giving us the chance to simplify and optimize many aspects of our lives.\nHaving things simplified gave us more time, time which we, as humans started to use smartly by finding a way so simplify the process of building apps.\nAs a result of those efforts MeteorJS was born.
Important!\nTo further help your understanding we have set a github repository where you can find a branch for each of the sections\nyou will complete on this tutorial.
\nMeteor is an open-source JavaScript-based framework which allows the developing of complex web and mobile apps at a fast pace.\nThe framework’s design is based on simplicity and efficiency, thus making it a good fit for both beginner and advanced developers.
\nWe are a Romanian company solely focused on web and mobile development. Analyzing our clients needs we understood that what\nthey are looking for in a product is a perfect combination of the newest technologies and fastness, stability and scalability.\n In order to apply to all those needs and become more professional we chose to work exclusively with Meteor and React.
\nBeing in love with what we do we contributed to a series of open-source projects which you can see HERE.
\nMeteor-Tuts is one of the projects closest to our hearts designed as a useful tool for any programmer that wants to step up his/her game.\nNavigating through this complex but rather easy to understand tutorial you will be accompanied by video tutorials for each chapter and sub-chapter.\nAlso, to give you an even better overview, we will provide you with a github repository link where you can find the full project \nalthough we recommend you to take it step by step in order to achieve a better understanding and mastery of the MeteorJS framework.
This tutorial is focused on Meteor, which is a Javascript framework.\nBecause we want to teach you Meteor, we will already assume you know Javascript ES6, in order to keep this tutorial focused.
\nHowever, if you do need to learn Javascript, you can use these resources:
\nIf you are familiar with Javascript, but not with the ES6 syntax, you can use these resources to learn it:
\nThese resources are ordered in the order we thought wold serve you best to go over them. If you think you have found a \nbetter way to learn Javascript ES6, then, by all means, please use the resource which fits you better.
\nEven though our primarily focus is Meteor, we are going to use React so that you can visualize how everything works, which is the view layer we recommend for Meteor. Ideally, would be to study first React even though our primary focus on this tutorial is Meteor. To study React, we recommend you:
\nIntroduction:
\nCurriculum:
\nNow let’s sail together into the Meteor World!
\n","site":{"data":{}},"excerpt":"","more":"First of all, welcome aboard and congratulations on your choice of embarking on this journey. But before we start, let me tell you a little story:
\nAs years passed by and humanity evolved, apps have literally changed the way in which the world works, giving us the chance to simplify and optimize many aspects of our lives.\nHaving things simplified gave us more time, time which we, as humans started to use smartly by finding a way so simplify the process of building apps.\nAs a result of those efforts MeteorJS was born.
Important!\nTo further help your understanding we have set a github repository where you can find a branch for each of the sections\nyou will complete on this tutorial.
\nMeteor is an open-source JavaScript-based framework which allows the developing of complex web and mobile apps at a fast pace.\nThe framework’s design is based on simplicity and efficiency, thus making it a good fit for both beginner and advanced developers.
\nWe are a Romanian company solely focused on web and mobile development. Analyzing our clients needs we understood that what\nthey are looking for in a product is a perfect combination of the newest technologies and fastness, stability and scalability.\n In order to apply to all those needs and become more professional we chose to work exclusively with Meteor and React.
\nBeing in love with what we do we contributed to a series of open-source projects which you can see HERE.
\nMeteor-Tuts is one of the projects closest to our hearts designed as a useful tool for any programmer that wants to step up his/her game.\nNavigating through this complex but rather easy to understand tutorial you will be accompanied by video tutorials for each chapter and sub-chapter.\nAlso, to give you an even better overview, we will provide you with a github repository link where you can find the full project \nalthough we recommend you to take it step by step in order to achieve a better understanding and mastery of the MeteorJS framework.
This tutorial is focused on Meteor, which is a Javascript framework.\nBecause we want to teach you Meteor, we will already assume you know Javascript ES6, in order to keep this tutorial focused.
\nHowever, if you do need to learn Javascript, you can use these resources:
\nIf you are familiar with Javascript, but not with the ES6 syntax, you can use these resources to learn it:
\nThese resources are ordered in the order we thought wold serve you best to go over them. If you think you have found a \nbetter way to learn Javascript ES6, then, by all means, please use the resource which fits you better.
\nEven though our primarily focus is Meteor, we are going to use React so that you can visualize how everything works, which is the view layer we recommend for Meteor. Ideally, would be to study first React even though our primary focus on this tutorial is Meteor. To study React, we recommend you:
\nIntroduction:
\nCurriculum:
\nNow let’s sail together into the Meteor World!
\n"},{"title":"Publications & Subscriptions","description":"Building reactive apps with Meteor","disqusPage":"Chapter 1: Pub/Sub","_content":"\n## Going Live\n\nTo be able to understand what we mean when we say Pub/sub in Meteor, we need to explain to you what a pub/sub system is in the first place. \n\nThe explanation is fairly straightforward: a pub/sub system is a communication system, where the \"publishers\" send messages \nto the \"subscribers\". Which is why, in Meteor, you will meet, very frequently, the term of **\"reactive data\"**.\n\nHaving this feature available in Meteor means that we can build **live applications**. If we go to our post example,\nand we add a new post, all the users that are **subscribed** to that data will see the changes made to the data in real time.\n\n## Publishing in Meteor\n\nSo how would we explain publishing in a very simple manner ? Well, let's assume you're talking to a friend about your application,\n and you want to give him access to your data. In other words, you are going to make that data **public** for him.\n As such, **publishing** would be equivalent to saying \"I will give you access to this data !\".\n \n```js\n//file /imports/api/posts/publications.js\nimport { Meteor } from 'meteor/meteor';\nimport Posts from '/imports/db/posts/collection';\n\nMeteor.publish('posts', function () {\n return Posts.find();\n})\n```\n\nNow, don't forget to import it in `/imports/startup/server/index.js`!\n\nIt's a basic rule that a publication needs to return 2 things:\n1. A cursor\n2. An array of cursors\n\nIt is because of this rule that we aren't using `.fetch()` in our code.\n\n## What is a cursor?\n\nA good way to think about a cursor is to think of it as if you would think of an \"address\". \nIn the example above, we returned an address to all posts.\n\nQ: Wait, isn't that also what methods do ?To be able to understand what we mean when we say Pub/sub in Meteor, we need to explain to you what a pub/sub system is in the first place.
\nThe explanation is fairly straightforward: a pub/sub system is a communication system, where the “publishers” send messages \nto the “subscribers”. Which is why, in Meteor, you will meet, very frequently, the term of “reactive data”.
\nHaving this feature available in Meteor means that we can build live applications. If we go to our post example,\nand we add a new post, all the users that are subscribed to that data will see the changes made to the data in real time.
\nSo how would we explain publishing in a very simple manner ? Well, let’s assume you’re talking to a friend about your application,\n and you want to give him access to your data. In other words, you are going to make that data public for him.\n As such, publishing would be equivalent to saying “I will give you access to this data !”.
\n1 | //file /imports/api/posts/publications.js |
Now, don’t forget to import it in /imports/startup/server/index.js
!
It’s a basic rule that a publication needs to return 2 things:
\nIt is because of this rule that we aren’t using .fetch()
in our code.
A good way to think about a cursor is to think of it as if you would think of an “address”. \nIn the example above, we returned an address to all posts.
\nQ: Wait, isn’t that also what methods do ?
\nA: Yes, but by using publications we benefit from reactivity.
Q: Ok, so when should I use methods and when should i use pub/sub ?
\nA: You should use pub/sub when you want your data changes to be live. Otherwise you should stick to using methods.
We have now published the data changes, but how are the users going to know about the data that we’ve made available for them ?\nWell, that’s where subscriptions come in ! If we are to use the “friend” analogy that we have used to explain publishing,\nthe friend to which we have given access to the data will say: “Hey! I want access to the data you are offering me.”
\nYou noticed that when we created our publication, we first passed a string to it in the code called ‘posts’.\nWe must use that same string in order to subscribe to the publication.
\nGo to http://localhost:3000 and the console in your browser’s . Then, type into it:\n1
var handler = Meteor.subscribe('posts');
1 | handler.ready(); // / will return true/false if the subscription is ready |
1 | handler.stop(); // will return true/false and stop listening to changes |
The variable handler contains:\n-a subscriptionId, which is a unique identifier for the subscription
\nWhen a subscription is ready, it means that the server got your request, and it gave the specified user access to the live data.
\nHow the data is served to us is a very interesting process, but in order to dive into that process, we must first have to\nhave a good understanding of Client-Side collections.
\nNow every time something in the “address” changes, like a new element is inserted, or updated, or removed, Meteor will communicate that to you, \nbecause you subscribed to that publication, and the cursor will change, to a new address.
\nJust keep in mind: when handle.ready() returns true, if it is followed by a .find().fetch()
, the results might be empty.\nThat’s because ready() returning a true value does not mean that you got all the data. \nInstead, it means that the connections was established, and Meteor will pump the data there.
If you want to read more about how pub/sub works, click here
\nIn order to see this how fully works in a practical way, you could clone our github repository and on /posts/reactive
you can see the process.
To be able to understand what we mean when we say Pub/sub in Meteor, we need to explain to you what a pub/sub system is in the first place.
\nThe explanation is fairly straightforward: a pub/sub system is a communication system, where the “publishers” send messages \nto the “subscribers”. Which is why, in Meteor, you will meet, very frequently, the term of “reactive data”.
\nHaving this feature available in Meteor means that we can build live applications. If we go to our post example,\nand we add a new post, all the users that are subscribed to that data will see the changes made to the data in real time.
\nSo how would we explain publishing in a very simple manner ? Well, let’s assume you’re talking to a friend about your application,\n and you want to give him access to your data. In other words, you are going to make that data public for him.\n As such, publishing would be equivalent to saying “I will give you access to this data !”.
\n1 | //file /imports/api/posts/publications.js |
Now, don’t forget to import it in /imports/startup/server/index.js
!
It’s a basic rule that a publication needs to return 2 things:
\nIt is because of this rule that we aren’t using .fetch()
in our code.
A good way to think about a cursor is to think of it as if you would think of an “address”. \nIn the example above, we returned an address to all posts.
\nQ: Wait, isn’t that also what methods do ?
\nA: Yes, but by using publications we benefit from reactivity.
Q: Ok, so when should I use methods and when should i use pub/sub ?
\nA: You should use pub/sub when you want your data changes to be live. Otherwise you should stick to using methods.
We have now published the data changes, but how are the users going to know about the data that we’ve made available for them ?\nWell, that’s where subscriptions come in ! If we are to use the “friend” analogy that we have used to explain publishing,\nthe friend to which we have given access to the data will say: “Hey! I want access to the data you are offering me.”
\nYou noticed that when we created our publication, we first passed a string to it in the code called ‘posts’.\nWe must use that same string in order to subscribe to the publication.
\nGo to http://localhost:3000 and the console in your browser’s . Then, type into it:\n1
var handler = Meteor.subscribe('posts');
1 | handler.ready(); // / will return true/false if the subscription is ready |
1 | handler.stop(); // will return true/false and stop listening to changes |
The variable handler contains:\n-a subscriptionId, which is a unique identifier for the subscription
\nWhen a subscription is ready, it means that the server got your request, and it gave the specified user access to the live data.
\nHow the data is served to us is a very interesting process, but in order to dive into that process, we must first have to\nhave a good understanding of Client-Side collections.
\nNow every time something in the “address” changes, like a new element is inserted, or updated, or removed, Meteor will communicate that to you, \nbecause you subscribed to that publication, and the cursor will change, to a new address.
\nJust keep in mind: when handle.ready() returns true, if it is followed by a .find().fetch()
, the results might be empty.\nThat’s because ready() returning a true value does not mean that you got all the data. \nInstead, it means that the connections was established, and Meteor will pump the data there.
If you want to read more about how pub/sub works, click here
\nIn order to see this how fully works in a practical way, you could clone our github repository and on /posts/reactive
you can see the process.
What is a tracking system ?
\nThe tracker is one of the concepts that sets Meteor apart from other frameworks out there. As such, before we show you\nwhat it’s all about, we need to make sure you have all the packages that you need installed on your machine.\nFor that, run this in your terminal:\n1
meteor add tracker reactive-var
Now open up your browser and type this into the browser console \n(the browser console is available for you in the “developer tools” menu of your browser):\n1
2
3
4var a = new ReactiveVar('a default value');
a.get() // will display 'a default value'
a.set('some other value')
a.get() // will display 'some other value'
That does not seem to be very complicated, but you might be asking yourself “what’s the deal with this ?”. \nAllow us to explain it step by step, so you can understand it!
\nReactiveVar is a reactive-data source and because of that, you can follow its changes at runtime. \nTo be able to do that, we will be using the Tracker feature that got mentioned earlier in the tutorial:\n1
2
3
4var computation = Tracker.autorun(() => {
// this is the function that runs it
console.log(a.get())
})
Now each time you set some value to the a variable, it will get logged into the console. Let’s try it:\n1
a.set('something')
To stop the execution, use:\n1
computation.stop()
Now, if you give values to your data source, the run function’s execution will be stopped.
\nSo, basically:\nWith the Tracker, we can track changes occurring to reactive data sources, in real time. \nThat’s everything you need to know for now in order to use this feature.
\nWhat we presented for you was a brief introduction, the Tracker tool is way more complex than that. You can read more about them here.
\nAt its core, tracker is a reactive “watcher”. And it is not unique !\nHowever, this one is very well integrated with Meteor, and this brings down the integration issues you might face when \nworking on projects, which is why we are using it !
\nCreate a tracker that will stop after the 5th change to a reactive variable.
\nFind out what’s ReactiveDict and use it to track changes.
\n","site":{"data":{}},"excerpt":"","more":"What is a tracking system ?
\nThe tracker is one of the concepts that sets Meteor apart from other frameworks out there. As such, before we show you\nwhat it’s all about, we need to make sure you have all the packages that you need installed on your machine.\nFor that, run this in your terminal:\n1
meteor add tracker reactive-var
Now open up your browser and type this into the browser console \n(the browser console is available for you in the “developer tools” menu of your browser):\n1
2
3
4var a = new ReactiveVar('a default value');
a.get() // will display 'a default value'
a.set('some other value')
a.get() // will display 'some other value'
That does not seem to be very complicated, but you might be asking yourself “what’s the deal with this ?”. \nAllow us to explain it step by step, so you can understand it!
\nReactiveVar is a reactive-data source and because of that, you can follow its changes at runtime. \nTo be able to do that, we will be using the Tracker feature that got mentioned earlier in the tutorial:\n1
2
3
4var computation = Tracker.autorun(() => {
// this is the function that runs it
console.log(a.get())
})
Now each time you set some value to the a variable, it will get logged into the console. Let’s try it:\n1
a.set('something')
To stop the execution, use:\n1
computation.stop()
Now, if you give values to your data source, the run function’s execution will be stopped.
\nSo, basically:\nWith the Tracker, we can track changes occurring to reactive data sources, in real time. \nThat’s everything you need to know for now in order to use this feature.
\nWhat we presented for you was a brief introduction, the Tracker tool is way more complex than that. You can read more about them here.
\nAt its core, tracker is a reactive “watcher”. And it is not unique !\nHowever, this one is very well integrated with Meteor, and this brings down the integration issues you might face when \nworking on projects, which is why we are using it !
\nCreate a tracker that will stop after the 5th change to a reactive variable.
\nFind out what’s ReactiveDict and use it to track changes.
\n"},{"title":"Accounts","description":"How Meteor handles users","disqusPage":"Chapter 1: Accounts","_content":"\nIn this part of the tutorial we will discuss about the friendly and simple APIs for:\n\n- Creating an user\n- Login (with Password, Facebook, Google and others)\n- Forgot Password\n- Change Password\n- Reset Password\n\nIn this part we'll focus more on registering and logging in the regular way, but we will also guide you on how you can integrate it with other authentication mechanism.\n\n```\nmeteor add accounts-base accounts-password\n```\nCreate a server side method where we register an user\n\n```js\n// file: /imports/api/users/methods.js\n'user.register' (data) {\n Accounts.createUser({\n email: data.email,\n password: data.password\n });\n}\n\n// client side, in the browser try this:\nconst data = {email: 'test@test.test', password: '12345'}\nMeteor.call('user.register', email, password, (err, result) => {\n console.log(err, result) // in case you try it twice, it will throw an exception that email already exists\n});\n\n```\n\nUsers are stored in a collection. You can access this collection via `Meteor.users`. \nIt's the same kind of collection that we learned about in the past chapters.\n\nNow go to your browser's console:\n\n```js\nMeteor.loginWithPassword('test@test.test', '12345', function (err) {\n if (!err) {\n console.log('I was called because authentication was a success')\n } else {\n console.log(err);\n }\n})\n```\nYou should be logged in now!\n\n```js\n// in browser console:\nMeteor.user() // will return the current logged in user\nMeteor.userId() // will return the _id of the current logged in user\n```\n\n`Meteor.user()` is a reactive data source, so if you use it in a Tracker, then you will benefit from it's reactivity.\n\nAnother thing you may notice is how `emails` key is structured:\n```js\n[\n {\n address: 'meteor@cultofcoders.com',\n verified: true|false\n }\n]\n```\n\nThis may seem a bit complicated, but they decided to stick with this, maybe because they wanted to easily satisfy the people\nwho want multiple email addresses on their account. To get the email of your user, you would have to do:\n```js\nMeteor.user().emails[0].address\n```\n\nBut don't worry about this now, when we'll learn how to make this easy, so you won't have to type this everywhere you need an user's email.\n\nYou think '12345' is not a very secure password, and you are correct, let's change it:\n\n```js\n// browser's console\nAccounts.changePassword('12345', 'My1337L333Tpasswurt%', function (err) {\n if (!err) {\n console.log('Change password was a success!')\n } else {\n console.log(err);\n }\n})\n```\n\nVery nice, now let's try a logout:\n\n```js\n//browser's console\nMeteor.logout(function (err) {\n if (!err) {\n console.log('Logout was a success!')\n } else {\n console.log(err);\n }\n});\n// now Meteor.user() and Meteor.userId() will be null\n```\n\nNext time you login, you'll login with your new password.\n\nBut wait, your new password is so complex, you already forgot it.\n\n```js\nAccounts.forgotPassword({ email: 'donut@lover.com' })\n```\n\nNow check your terminal, where you started Meteor, you should see the email you got, you should have a link like:\n```\nhttp://localhost:3000/#/reset-password/eNqDzCvx0F3OA6B0dzmx4i6kLs4-veJ36j3X2Rhxui7\n```\n\nThe last part is your token.\n\n```js\nAccounts.resetPassword('eNqDzCvx0F3OA6B0dzmx4i6kLs4-veJ36j3X2Rhxui7', 'NewPassword123', function (err) {\n if (!err) {\n console.log('Password reset was a success!')\n } else {\n console.log(err);\n }\n})\n```\n\nIf you want to read more about this package, you can find documentation for it [here](https://docs.meteor.com/api/accounts.html)\n\n# Important\nIn order to see this in real action, you can access on [github](www.github.com) and clone the project.","source":"chapters/1/users.md","raw":"---\ntitle: Accounts\ndescription: How Meteor handles users\ndisqusPage: 'Chapter 1: Accounts'\n---\n\nIn this part of the tutorial we will discuss about the friendly and simple APIs for:\n\n- Creating an user\n- Login (with Password, Facebook, Google and others)\n- Forgot Password\n- Change Password\n- Reset Password\n\nIn this part we'll focus more on registering and logging in the regular way, but we will also guide you on how you can integrate it with other authentication mechanism.\n\n```\nmeteor add accounts-base accounts-password\n```\nCreate a server side method where we register an user\n\n```js\n// file: /imports/api/users/methods.js\n'user.register' (data) {\n Accounts.createUser({\n email: data.email,\n password: data.password\n });\n}\n\n// client side, in the browser try this:\nconst data = {email: 'test@test.test', password: '12345'}\nMeteor.call('user.register', email, password, (err, result) => {\n console.log(err, result) // in case you try it twice, it will throw an exception that email already exists\n});\n\n```\n\nUsers are stored in a collection. You can access this collection via `Meteor.users`. \nIt's the same kind of collection that we learned about in the past chapters.\n\nNow go to your browser's console:\n\n```js\nMeteor.loginWithPassword('test@test.test', '12345', function (err) {\n if (!err) {\n console.log('I was called because authentication was a success')\n } else {\n console.log(err);\n }\n})\n```\nYou should be logged in now!\n\n```js\n// in browser console:\nMeteor.user() // will return the current logged in user\nMeteor.userId() // will return the _id of the current logged in user\n```\n\n`Meteor.user()` is a reactive data source, so if you use it in a Tracker, then you will benefit from it's reactivity.\n\nAnother thing you may notice is how `emails` key is structured:\n```js\n[\n {\n address: 'meteor@cultofcoders.com',\n verified: true|false\n }\n]\n```\n\nThis may seem a bit complicated, but they decided to stick with this, maybe because they wanted to easily satisfy the people\nwho want multiple email addresses on their account. To get the email of your user, you would have to do:\n```js\nMeteor.user().emails[0].address\n```\n\nBut don't worry about this now, when we'll learn how to make this easy, so you won't have to type this everywhere you need an user's email.\n\nYou think '12345' is not a very secure password, and you are correct, let's change it:\n\n```js\n// browser's console\nAccounts.changePassword('12345', 'My1337L333Tpasswurt%', function (err) {\n if (!err) {\n console.log('Change password was a success!')\n } else {\n console.log(err);\n }\n})\n```\n\nVery nice, now let's try a logout:\n\n```js\n//browser's console\nMeteor.logout(function (err) {\n if (!err) {\n console.log('Logout was a success!')\n } else {\n console.log(err);\n }\n});\n// now Meteor.user() and Meteor.userId() will be null\n```\n\nNext time you login, you'll login with your new password.\n\nBut wait, your new password is so complex, you already forgot it.\n\n```js\nAccounts.forgotPassword({ email: 'donut@lover.com' })\n```\n\nNow check your terminal, where you started Meteor, you should see the email you got, you should have a link like:\n```\nhttp://localhost:3000/#/reset-password/eNqDzCvx0F3OA6B0dzmx4i6kLs4-veJ36j3X2Rhxui7\n```\n\nThe last part is your token.\n\n```js\nAccounts.resetPassword('eNqDzCvx0F3OA6B0dzmx4i6kLs4-veJ36j3X2Rhxui7', 'NewPassword123', function (err) {\n if (!err) {\n console.log('Password reset was a success!')\n } else {\n console.log(err);\n }\n})\n```\n\nIf you want to read more about this package, you can find documentation for it [here](https://docs.meteor.com/api/accounts.html)\n\n# Important\nIn order to see this in real action, you can access on [github](www.github.com) and clone the project.","date":"2018-03-08T15:53:49.084Z","updated":"2018-03-08T15:53:49.084Z","path":"chapters/1/users.html","_id":"ciushr1a10001lrjxef1x4k0v","comments":1,"layout":"page","content":"In this part of the tutorial we will discuss about the friendly and simple APIs for:
\nIn this part we’ll focus more on registering and logging in the regular way, but we will also guide you on how you can integrate it with other authentication mechanism.
\n1 | meteor add accounts-base accounts-password |
Create a server side method where we register an user
\n1 | // file: /imports/api/users/methods.js |
Users are stored in a collection. You can access this collection via Meteor.users
. \nIt’s the same kind of collection that we learned about in the past chapters.
Now go to your browser’s console:
\n1 | Meteor.loginWithPassword('test@test.test', '12345', function (err) { |
You should be logged in now!
\n1 | // in browser console: |
Meteor.user()
is a reactive data source, so if you use it in a Tracker, then you will benefit from it’s reactivity.
Another thing you may notice is how emails
key is structured:\n1
2
3
4
5
6[
{
address: 'meteor@cultofcoders.com',
verified: true|false
}
]
This may seem a bit complicated, but they decided to stick with this, maybe because they wanted to easily satisfy the people\nwho want multiple email addresses on their account. To get the email of your user, you would have to do:\n1
Meteor.user().emails[0].address
But don’t worry about this now, when we’ll learn how to make this easy, so you won’t have to type this everywhere you need an user’s email.
\nYou think ‘12345’ is not a very secure password, and you are correct, let’s change it:
\n1 | // browser's console |
Very nice, now let’s try a logout:
\n1 | //browser's console |
Next time you login, you’ll login with your new password.
\nBut wait, your new password is so complex, you already forgot it.
\n1 | Accounts.forgotPassword({ email: 'donut@lover.com' }) |
Now check your terminal, where you started Meteor, you should see the email you got, you should have a link like:\n1
http://localhost:3000/
The last part is your token.
\n1 | Accounts.resetPassword('eNqDzCvx0F3OA6B0dzmx4i6kLs4-veJ36j3X2Rhxui7', 'NewPassword123', function (err) { |
If you want to read more about this package, you can find documentation for it here
\nIn order to see this in real action, you can access on github and clone the project.
\n","site":{"data":{}},"excerpt":"","more":"In this part of the tutorial we will discuss about the friendly and simple APIs for:
\nIn this part we’ll focus more on registering and logging in the regular way, but we will also guide you on how you can integrate it with other authentication mechanism.
\n1 | meteor add accounts-base accounts-password |
Create a server side method where we register an user
\n1 | // file: /imports/api/users/methods.js |
Users are stored in a collection. You can access this collection via Meteor.users
. \nIt’s the same kind of collection that we learned about in the past chapters.
Now go to your browser’s console:
\n1 | Meteor.loginWithPassword('test@test.test', '12345', function (err) { |
You should be logged in now!
\n1 | // in browser console: |
Meteor.user()
is a reactive data source, so if you use it in a Tracker, then you will benefit from it’s reactivity.
Another thing you may notice is how emails
key is structured:\n1
2
3
4
5
6[
{
address: 'meteor@cultofcoders.com',
verified: true|false
}
]
This may seem a bit complicated, but they decided to stick with this, maybe because they wanted to easily satisfy the people\nwho want multiple email addresses on their account. To get the email of your user, you would have to do:\n1
Meteor.user().emails[0].address
But don’t worry about this now, when we’ll learn how to make this easy, so you won’t have to type this everywhere you need an user’s email.
\nYou think ‘12345’ is not a very secure password, and you are correct, let’s change it:
\n1 | // browser's console |
Very nice, now let’s try a logout:
\n1 | //browser's console |
Next time you login, you’ll login with your new password.
\nBut wait, your new password is so complex, you already forgot it.
\n1 | Accounts.forgotPassword({ email: 'donut@lover.com' }) |
Now check your terminal, where you started Meteor, you should see the email you got, you should have a link like:\n1
http://localhost:3000/
The last part is your token.
\n1 | Accounts.resetPassword('eNqDzCvx0F3OA6B0dzmx4i6kLs4-veJ36j3X2Rhxui7', 'NewPassword123', function (err) { |
If you want to read more about this package, you can find documentation for it here
\nIn order to see this in real action, you can access on github and clone the project.
\n"},{"title":"Security","description":"Let's talk Security","disqusPage":"Chapter 1: Security","_content":"\nSecurity is a very important aspect of any application. When your code-base grows,\nyou need to more and more careful on how you handle this. We'll first show how we can\nsecure our methods and publications, then we will get into some tips and tricks meant to teach you how to handle \nthe security for an evergrowing code base.\n\n## Securing Methods & Publicaions\n\nSo, remember the Methods ?\n\n```js\nMeteor.methods({\n 'post.create'(post) {\n Posts.insert(post);\n },\n})\n```\n\nIn every method & publication we can access `this.userId`, this will return either `null`, meaning the user is not authenticated, or a string with the actual userId, something like `8qLGFKSn6szJkzsyG`\n\n## Managing Roles\n\nBased on the userId you have the ability to check if the user is logged in, maybe you have multiple roles in the system,\n\nWe recommend installing the infamous package, [alanning:roles](https://atmospherejs.com/alanning/roles):\n\n```\nmeteor add alanning:roles\n```\n\n\n## Security Module\n\nCentralize security in a module:\n\n```js\n// file: /imports/api/security.js\n// example of a module for security\nimport { Roles } from 'meteor/alanning:roles';\n\nexport default class Security {\n static checkRole(userId, role) {\n if (!this.hasRole(userId, role)) {\n throw new Meteor.Error('not-authorized');\n }\n }\n\n static hasRole(userId, role) {\n return Roles.userIsInRole(userId, role);\n }\n\n static checkLoggedIn(userId) {\n if (!userId) {\n throw new Meteor.Error('not-authorized', 'You are not authorized');\n };\n }\n \n // add other business logic checks here that you use throughout the app\n // something like: isUserAllowedToSeeDocument()\n // always keep decoupling your code if this class gets huge.\n}\n```\n\nPretty straight forward right ? The reason we do it like this, by centralizing security in one place,\nis to remove boilerplate code inside our methods and keep separation of concerns. You can do it however you want it, there is no right or wrong way to do it,\ndepends on your use-case, but we believe that it is easier to maintain, and newly onboarded developers were writing secure\ncode right from the start!\n\nSimple usage in methods:\n\n```js\nimport {Meteor} from 'meteor/meteor'\nimport {Posts} from '/db';\nimport Security from '/imports/api/security';\n\nMeteor.methods({\n 'post.create'(post) {\n Security.checkLoggedIn(this.userId);\n post.userId = this.userId;\n Posts.insert(post);\n },\n\n 'post.list' () {\n return Posts.find().fetch();\n },\n\n 'post.edit' (_id, postData) {\n Posts.update({_id: _id, userId: this.userId}, {\n $set: {\n title: postData.title,\n description: postData.description\n }\n });\n },\n\n 'post.remove' (_id){\n Posts.remove({_id: _id, userId: this.userId});\n },\n\n 'post.get' (_id) {\n return Posts.findOne(_id);\n }\n});\n```\n\nSimple usage in publications:\n\n```js\n\nimport Security from '/imports/api/security.js';\nimport {Meteor} from \"meteor/meteor\";\n\nMeteor.publish('posts', function () {\n return Posts.find({userId: this.userId});\n})\n```\n\nThat's it. With this knowledge you can build more secured apps!","source":"chapters/1/security.md","raw":"---\ntitle: Security\ndescription: Let's talk Security\ndisqusPage: 'Chapter 1: Security'\n---\n\nSecurity is a very important aspect of any application. When your code-base grows,\nyou need to more and more careful on how you handle this. We'll first show how we can\nsecure our methods and publications, then we will get into some tips and tricks meant to teach you how to handle \nthe security for an evergrowing code base.\n\n## Securing Methods & Publicaions\n\nSo, remember the Methods ?\n\n```js\nMeteor.methods({\n 'post.create'(post) {\n Posts.insert(post);\n },\n})\n```\n\nIn every method & publication we can access `this.userId`, this will return either `null`, meaning the user is not authenticated, or a string with the actual userId, something like `8qLGFKSn6szJkzsyG`\n\n## Managing Roles\n\nBased on the userId you have the ability to check if the user is logged in, maybe you have multiple roles in the system,\n\nWe recommend installing the infamous package, [alanning:roles](https://atmospherejs.com/alanning/roles):\n\n```\nmeteor add alanning:roles\n```\n\n\n## Security Module\n\nCentralize security in a module:\n\n```js\n// file: /imports/api/security.js\n// example of a module for security\nimport { Roles } from 'meteor/alanning:roles';\n\nexport default class Security {\n static checkRole(userId, role) {\n if (!this.hasRole(userId, role)) {\n throw new Meteor.Error('not-authorized');\n }\n }\n\n static hasRole(userId, role) {\n return Roles.userIsInRole(userId, role);\n }\n\n static checkLoggedIn(userId) {\n if (!userId) {\n throw new Meteor.Error('not-authorized', 'You are not authorized');\n };\n }\n \n // add other business logic checks here that you use throughout the app\n // something like: isUserAllowedToSeeDocument()\n // always keep decoupling your code if this class gets huge.\n}\n```\n\nPretty straight forward right ? The reason we do it like this, by centralizing security in one place,\nis to remove boilerplate code inside our methods and keep separation of concerns. You can do it however you want it, there is no right or wrong way to do it,\ndepends on your use-case, but we believe that it is easier to maintain, and newly onboarded developers were writing secure\ncode right from the start!\n\nSimple usage in methods:\n\n```js\nimport {Meteor} from 'meteor/meteor'\nimport {Posts} from '/db';\nimport Security from '/imports/api/security';\n\nMeteor.methods({\n 'post.create'(post) {\n Security.checkLoggedIn(this.userId);\n post.userId = this.userId;\n Posts.insert(post);\n },\n\n 'post.list' () {\n return Posts.find().fetch();\n },\n\n 'post.edit' (_id, postData) {\n Posts.update({_id: _id, userId: this.userId}, {\n $set: {\n title: postData.title,\n description: postData.description\n }\n });\n },\n\n 'post.remove' (_id){\n Posts.remove({_id: _id, userId: this.userId});\n },\n\n 'post.get' (_id) {\n return Posts.findOne(_id);\n }\n});\n```\n\nSimple usage in publications:\n\n```js\n\nimport Security from '/imports/api/security.js';\nimport {Meteor} from \"meteor/meteor\";\n\nMeteor.publish('posts', function () {\n return Posts.find({userId: this.userId});\n})\n```\n\nThat's it. With this knowledge you can build more secured apps!","date":"2018-03-08T15:53:49.084Z","updated":"2018-03-08T15:53:49.084Z","path":"chapters/1/security.html","_id":"ciuuujzzk00001gjxm98tz609","comments":1,"layout":"page","content":"Security is a very important aspect of any application. When your code-base grows,\nyou need to more and more careful on how you handle this. We’ll first show how we can\nsecure our methods and publications, then we will get into some tips and tricks meant to teach you how to handle \nthe security for an evergrowing code base.
\nSo, remember the Methods ?
\n1 | Meteor.methods({ |
In every method & publication we can access this.userId
, this will return either null
, meaning the user is not authenticated, or a string with the actual userId, something like 8qLGFKSn6szJkzsyG
Based on the userId you have the ability to check if the user is logged in, maybe you have multiple roles in the system,
\nWe recommend installing the infamous package, alanning:roles:
\n1 | meteor add alanning:roles |
Centralize security in a module:
\n1 | // file: /imports/api/security.js |
Pretty straight forward right ? The reason we do it like this, by centralizing security in one place,\nis to remove boilerplate code inside our methods and keep separation of concerns. You can do it however you want it, there is no right or wrong way to do it,\ndepends on your use-case, but we believe that it is easier to maintain, and newly onboarded developers were writing secure\ncode right from the start!
\nSimple usage in methods:
\n1 | import {Meteor} from 'meteor/meteor' |
Simple usage in publications:
\n1 |
|
That’s it. With this knowledge you can build more secured apps!
\n","site":{"data":{}},"excerpt":"","more":"Security is a very important aspect of any application. When your code-base grows,\nyou need to more and more careful on how you handle this. We’ll first show how we can\nsecure our methods and publications, then we will get into some tips and tricks meant to teach you how to handle \nthe security for an evergrowing code base.
\nSo, remember the Methods ?
\n1 | Meteor.methods({ |
In every method & publication we can access this.userId
, this will return either null
, meaning the user is not authenticated, or a string with the actual userId, something like 8qLGFKSn6szJkzsyG
Based on the userId you have the ability to check if the user is logged in, maybe you have multiple roles in the system,
\nWe recommend installing the infamous package, alanning:roles:
\n1 | meteor add alanning:roles |
Centralize security in a module:
\n1 | // file: /imports/api/security.js |
Pretty straight forward right ? The reason we do it like this, by centralizing security in one place,\nis to remove boilerplate code inside our methods and keep separation of concerns. You can do it however you want it, there is no right or wrong way to do it,\ndepends on your use-case, but we believe that it is easier to maintain, and newly onboarded developers were writing secure\ncode right from the start!
\nSimple usage in methods:
\n1 | import {Meteor} from 'meteor/meteor' |
Simple usage in publications:
\n1 |
|
That’s it. With this knowledge you can build more secured apps!
\n"},{"title":"Meteor Tips & Tricks","description":"Some interesting tips & tricks that Meteor has to offer.","disqusPage":"Chapter 1: Meteor Tips & Tricks","_content":"\n## Environment Variables\n\nMeteor uses these variables to know which MongoDB database should it connect to, how it should send emails, and many more:\n\n- MONGO_URL : you don't have to have this by default, but if you connect to another database here is where you would put it\n- MAIL_URL : the smtp:// to your email, we'll show you in a bit how easy it is to set it up.\n- METEOR_PROFILE : if set to 1, you'll see how much time meteor spends on the building/rebuilding process\n- ROOT_URL : the real path of Meteor, default is http://localhost:3000\n\nTo specify these variables you should do the following:\n\n```\nROOT_URL=\"http://localhost:3000\" MAIL_URL=\"smtp://gmail.com:25\" meteor run\n```\n\n## Run Meteor Easy\n\nInside your Meteor folder you have a file \"package.json\", that package keeps track of what npm packages you use, and some other\ncool stuff. So for example, if you would want to start an app with diff settings like MAIL_URL, etc, you would do something like this:\n```json\n{\n ...\n \"scripts\": {\n \"start\": \"MONGO_URL=mongodb://localhost:27017/meteor-tuts meteor run --settings .deploy/local.json --port 3000\",\n \"deploy\": \"We'll get into that in another chapter ;)\"\n }\n}\n```\n\n```\n// in your terminal:\nnpm run start\n```\n\n## Meteor.wrapAsync\n\nYou will use this to be able to do async operations in your methods. Let's say you use S3, or an npm library, that is not in sync,\n it requires you to specify a callback. So try this:\n \n```js\nMeteor.methods({\n 'something_async' () {\n coolLibrary.coolFunction((err, res) => {\n // gets here after some time.\n })\n }\n})\n```\n\nYou may have a very weird error saying that code cannot run outside \"Fibers\". Don't want to get into details on that, but here's how you would do it:\n\n```js\nMeteor.methods({\n 'something_async': function () {\n const run = Meteor.wrapAsync(coolLibrary.coolFunction, coolLibrary);\n // the second argument is to provide context to the function, \n // if that function uses \"this\" inside it, then it will fail without the context specified.\n \n try {\n const results = run(\"some-argument\");\n return results;\n // if the callback accepts err then res (standard), then result will be put in sync into results.\n } catch (e) {\n // if an exception occurs, that exception will be caught here\n // and you can treat it by dispatching a Meteor.Error\n }\n }\n})\n```\n\n## Timers\n\nYou may already be familiar, with setTimeout, setInterval from JavaScript, well, Meteor has them too,\nbut they will run inside fibers. For example:\n\n```js\nMeteor.methods({\n 'something_async' () {\n Meteor.setInterval(() => {\n console.log('tick');\n }, 1000);\n }\n})\n```\n\nAfter you have called the method, you will get a 'tick' in your console, every 1 second. You will not be able to stop this, \nunless you restart or implement a handler that stops it, so be careful!\n\n## Email\n\nRemember the emails we received in console when we were talking about Users ? Well, a while back, they used this module:\nIf you don't specify MAIL_URL, all Emails that you send, will go to your console. Pretty cool, right?\n\nIf you want an email, we recommend: http://www.mailgun.com/ <- Free for < 10,000 per month\n\n```\n// we use %40 to represent @ because the username, because they need to be URI encoded\nMAIL_URL=\"smtp://postmaster%40yourdomain.com:b23f5872166c187ad5b5f1abece071b2@smtp.mailgun.org:25\" meteor run\n```\n\n```js\n// Most Basic Usage\nimport { Email } from 'meteor/email';\n\nEmail.send({\n to: 'you@meteor-tuts.com',\n from: 'no-reply@meteor-tuts.com',\n subject: \"I'm an email\",\n html: 'Hello!
'\n});\n```\n\nRead more: http://docs.meteor.com/api/email.html\n\n## Meteor.defer\n\nSometimes you want to do something, and then notify the user by email. However, if you use SMTP, sometimes\nit takes between 50ms to 1s for the mail to be fully sent, and will give the user the impression that your Meteor app is slow. \nThis is why you should use this function:\n\n```js\nMeteor.methods({\n 'action_plus_email': function () {\n // do something\n \n Meteor.defer(() => {\n Email.send(...)\n })\n \n return 'hello there, user';\n }\n})\n```\n\nMeteor.defer(fn) is same as Meteor.setTimeout(fn, 0), basically it will do a \"background\" job in a fiber.\n\n## HTTP\n\nWant to use an external REST API ? No problem for Meteor, it has a super simple HTTP package built-in.\n\nhttp://docs.meteor.com/api/http.html\n\n```js\nMeteor.methods({\n 'an_api_call': function () {\n const data = HTTP.get('https://jsonplaceholder.typicode.com/posts/1')\n \n console.log(data);\n \n return data;\n }\n})\n```\n\n## Assets\n\nhttp://docs.meteor.com/api/assets.html\n\nGo ahead, put something in \"/private/some_folder/test.txt\":\n\n```\n// meteor shell\nAssets.getText('/some_folder/test.txt')\n```\n\nYou would use this when, for example, you have a business, logic-packed csv, or .xls file.\nOr you may have a JSON with car models and makes. \nThe idea is that you can have any type of file, even binary, that you can privately use on the server.\n\n## Meteor Settings\n\nhttps://docs.meteor.com/api/core.html#Meteor-settings\n\n```json\n// file: .deploy/local.json\n{\n \"public\": {\n \"visible\": \"Something that the client can see\"\n },\n \"private\": {\n \"API_KEY\": \"XXX\"\n }\n}\n```\n\nYou can access the settings from the client-side:\n```\nMeteor.settings.public.visible\n```\n\nYou can access all settings from the server-side:\n```\nMeteor.settings.private.API_KEY\n```\n","source":"chapters/1/meteorSnacks.md","raw":"---\ntitle: Meteor Tips & Tricks\ndescription: Some interesting tips & tricks that Meteor has to offer.\ndisqusPage: 'Chapter 1: Meteor Tips & Tricks'\n---\n\n## Environment Variables\n\nMeteor uses these variables to know which MongoDB database should it connect to, how it should send emails, and many more:\n\n- MONGO_URL : you don't have to have this by default, but if you connect to another database here is where you would put it\n- MAIL_URL : the smtp:// to your email, we'll show you in a bit how easy it is to set it up.\n- METEOR_PROFILE : if set to 1, you'll see how much time meteor spends on the building/rebuilding process\n- ROOT_URL : the real path of Meteor, default is http://localhost:3000\n\nTo specify these variables you should do the following:\n\n```\nROOT_URL=\"http://localhost:3000\" MAIL_URL=\"smtp://gmail.com:25\" meteor run\n```\n\n## Run Meteor Easy\n\nInside your Meteor folder you have a file \"package.json\", that package keeps track of what npm packages you use, and some other\ncool stuff. So for example, if you would want to start an app with diff settings like MAIL_URL, etc, you would do something like this:\n```json\n{\n ...\n \"scripts\": {\n \"start\": \"MONGO_URL=mongodb://localhost:27017/meteor-tuts meteor run --settings .deploy/local.json --port 3000\",\n \"deploy\": \"We'll get into that in another chapter ;)\"\n }\n}\n```\n\n```\n// in your terminal:\nnpm run start\n```\n\n## Meteor.wrapAsync\n\nYou will use this to be able to do async operations in your methods. Let's say you use S3, or an npm library, that is not in sync,\n it requires you to specify a callback. So try this:\n \n```js\nMeteor.methods({\n 'something_async' () {\n coolLibrary.coolFunction((err, res) => {\n // gets here after some time.\n })\n }\n})\n```\n\nYou may have a very weird error saying that code cannot run outside \"Fibers\". Don't want to get into details on that, but here's how you would do it:\n\n```js\nMeteor.methods({\n 'something_async': function () {\n const run = Meteor.wrapAsync(coolLibrary.coolFunction, coolLibrary);\n // the second argument is to provide context to the function, \n // if that function uses \"this\" inside it, then it will fail without the context specified.\n \n try {\n const results = run(\"some-argument\");\n return results;\n // if the callback accepts err then res (standard), then result will be put in sync into results.\n } catch (e) {\n // if an exception occurs, that exception will be caught here\n // and you can treat it by dispatching a Meteor.Error\n }\n }\n})\n```\n\n## Timers\n\nYou may already be familiar, with setTimeout, setInterval from JavaScript, well, Meteor has them too,\nbut they will run inside fibers. For example:\n\n```js\nMeteor.methods({\n 'something_async' () {\n Meteor.setInterval(() => {\n console.log('tick');\n }, 1000);\n }\n})\n```\n\nAfter you have called the method, you will get a 'tick' in your console, every 1 second. You will not be able to stop this, \nunless you restart or implement a handler that stops it, so be careful!\n\n## Email\n\nRemember the emails we received in console when we were talking about Users ? Well, a while back, they used this module:\nIf you don't specify MAIL_URL, all Emails that you send, will go to your console. Pretty cool, right?\n\nIf you want an email, we recommend: http://www.mailgun.com/ <- Free for < 10,000 per month\n\n```\n// we use %40 to represent @ because the username, because they need to be URI encoded\nMAIL_URL=\"smtp://postmaster%40yourdomain.com:b23f5872166c187ad5b5f1abece071b2@smtp.mailgun.org:25\" meteor run\n```\n\n```js\n// Most Basic Usage\nimport { Email } from 'meteor/email';\n\nEmail.send({\n to: 'you@meteor-tuts.com',\n from: 'no-reply@meteor-tuts.com',\n subject: \"I'm an email\",\n html: 'Hello!
'\n});\n```\n\nRead more: http://docs.meteor.com/api/email.html\n\n## Meteor.defer\n\nSometimes you want to do something, and then notify the user by email. However, if you use SMTP, sometimes\nit takes between 50ms to 1s for the mail to be fully sent, and will give the user the impression that your Meteor app is slow. \nThis is why you should use this function:\n\n```js\nMeteor.methods({\n 'action_plus_email': function () {\n // do something\n \n Meteor.defer(() => {\n Email.send(...)\n })\n \n return 'hello there, user';\n }\n})\n```\n\nMeteor.defer(fn) is same as Meteor.setTimeout(fn, 0), basically it will do a \"background\" job in a fiber.\n\n## HTTP\n\nWant to use an external REST API ? No problem for Meteor, it has a super simple HTTP package built-in.\n\nhttp://docs.meteor.com/api/http.html\n\n```js\nMeteor.methods({\n 'an_api_call': function () {\n const data = HTTP.get('https://jsonplaceholder.typicode.com/posts/1')\n \n console.log(data);\n \n return data;\n }\n})\n```\n\n## Assets\n\nhttp://docs.meteor.com/api/assets.html\n\nGo ahead, put something in \"/private/some_folder/test.txt\":\n\n```\n// meteor shell\nAssets.getText('/some_folder/test.txt')\n```\n\nYou would use this when, for example, you have a business, logic-packed csv, or .xls file.\nOr you may have a JSON with car models and makes. \nThe idea is that you can have any type of file, even binary, that you can privately use on the server.\n\n## Meteor Settings\n\nhttps://docs.meteor.com/api/core.html#Meteor-settings\n\n```json\n// file: .deploy/local.json\n{\n \"public\": {\n \"visible\": \"Something that the client can see\"\n },\n \"private\": {\n \"API_KEY\": \"XXX\"\n }\n}\n```\n\nYou can access the settings from the client-side:\n```\nMeteor.settings.public.visible\n```\n\nYou can access all settings from the server-side:\n```\nMeteor.settings.private.API_KEY\n```\n","date":"2018-03-08T15:53:49.081Z","updated":"2018-03-08T15:53:49.081Z","path":"chapters/1/meteorSnacks.html","_id":"ciuuvhqrw0000a0jx8hoyytyy","comments":1,"layout":"page","content":"Meteor uses these variables to know which MongoDB database should it connect to, how it should send emails, and many more:
\nTo specify these variables you should do the following:
\n1 | ROOT_URL=\"http://localhost:3000\" MAIL_URL=\"smtp://gmail.com:25\" meteor run |
Inside your Meteor folder you have a file “package.json”, that package keeps track of what npm packages you use, and some other\ncool stuff. So for example, if you would want to start an app with diff settings like MAIL_URL, etc, you would do something like this:\n1
2
3
4
5
6
7{
...
\"scripts\": {
\"start\": \"MONGO_URL=mongodb://localhost:27017/meteor-tuts meteor run --settings .deploy/local.json --port 3000\",
\"deploy\": \"We'll get into that in another chapter ;)\"
}
}
1 | // in your terminal: |
You will use this to be able to do async operations in your methods. Let’s say you use S3, or an npm library, that is not in sync,\n it requires you to specify a callback. So try this:
\n1 | Meteor.methods({ |
You may have a very weird error saying that code cannot run outside “Fibers”. Don’t want to get into details on that, but here’s how you would do it:
\n1 | Meteor.methods({ |
You may already be familiar, with setTimeout, setInterval from JavaScript, well, Meteor has them too,\nbut they will run inside fibers. For example:
\n1 | Meteor.methods({ |
After you have called the method, you will get a ‘tick’ in your console, every 1 second. You will not be able to stop this, \nunless you restart or implement a handler that stops it, so be careful!
\nRemember the emails we received in console when we were talking about Users ? Well, a while back, they used this module:\nIf you don’t specify MAIL_URL, all Emails that you send, will go to your console. Pretty cool, right?
\nIf you want an email, we recommend: http://www.mailgun.com/ <- Free for < 10,000 per month
\n1 | // we use %40 to represent @ because the username, because they need to be URI encoded |
1 | // Most Basic Usage |
Read more: http://docs.meteor.com/api/email.html
\nSometimes you want to do something, and then notify the user by email. However, if you use SMTP, sometimes\nit takes between 50ms to 1s for the mail to be fully sent, and will give the user the impression that your Meteor app is slow. \nThis is why you should use this function:
\n1 | Meteor.methods({ |
Meteor.defer(fn) is same as Meteor.setTimeout(fn, 0), basically it will do a “background” job in a fiber.
\nWant to use an external REST API ? No problem for Meteor, it has a super simple HTTP package built-in.
\nhttp://docs.meteor.com/api/http.html
\n1 | Meteor.methods({ |
http://docs.meteor.com/api/assets.html
\nGo ahead, put something in “/private/some_folder/test.txt”:
\n1 | // meteor shell |
You would use this when, for example, you have a business, logic-packed csv, or .xls file.\nOr you may have a JSON with car models and makes. \nThe idea is that you can have any type of file, even binary, that you can privately use on the server.
\nhttps://docs.meteor.com/api/core.html#Meteor-settings
\n1 | // file: .deploy/local.json |
You can access the settings from the client-side:\n1
Meteor.settings.public.visible
You can access all settings from the server-side:\n1
Meteor.settings.private.API_KEY
Meteor uses these variables to know which MongoDB database should it connect to, how it should send emails, and many more:
\nTo specify these variables you should do the following:
\n1 | ROOT_URL=\"http://localhost:3000\" MAIL_URL=\"smtp://gmail.com:25\" meteor run |
Inside your Meteor folder you have a file “package.json”, that package keeps track of what npm packages you use, and some other\ncool stuff. So for example, if you would want to start an app with diff settings like MAIL_URL, etc, you would do something like this:\n1
2
3
4
5
6
7{
...
\"scripts\": {
\"start\": \"MONGO_URL=mongodb://localhost:27017/meteor-tuts meteor run --settings .deploy/local.json --port 3000\",
\"deploy\": \"We'll get into that in another chapter ;)\"
}
}
1 | // in your terminal: |
You will use this to be able to do async operations in your methods. Let’s say you use S3, or an npm library, that is not in sync,\n it requires you to specify a callback. So try this:
\n1 | Meteor.methods({ |
You may have a very weird error saying that code cannot run outside “Fibers”. Don’t want to get into details on that, but here’s how you would do it:
\n1 | Meteor.methods({ |
You may already be familiar, with setTimeout, setInterval from JavaScript, well, Meteor has them too,\nbut they will run inside fibers. For example:
\n1 | Meteor.methods({ |
After you have called the method, you will get a ‘tick’ in your console, every 1 second. You will not be able to stop this, \nunless you restart or implement a handler that stops it, so be careful!
\nRemember the emails we received in console when we were talking about Users ? Well, a while back, they used this module:\nIf you don’t specify MAIL_URL, all Emails that you send, will go to your console. Pretty cool, right?
\nIf you want an email, we recommend: http://www.mailgun.com/ <- Free for < 10,000 per month
\n1 | // we use %40 to represent @ because the username, because they need to be URI encoded |
1 | // Most Basic Usage |
Read more: http://docs.meteor.com/api/email.html
\nSometimes you want to do something, and then notify the user by email. However, if you use SMTP, sometimes\nit takes between 50ms to 1s for the mail to be fully sent, and will give the user the impression that your Meteor app is slow. \nThis is why you should use this function:
\n1 | Meteor.methods({ |
Meteor.defer(fn) is same as Meteor.setTimeout(fn, 0), basically it will do a “background” job in a fiber.
\nWant to use an external REST API ? No problem for Meteor, it has a super simple HTTP package built-in.
\nhttp://docs.meteor.com/api/http.html
\n1 | Meteor.methods({ |
http://docs.meteor.com/api/assets.html
\nGo ahead, put something in “/private/some_folder/test.txt”:
\n1 | // meteor shell |
You would use this when, for example, you have a business, logic-packed csv, or .xls file.\nOr you may have a JSON with car models and makes. \nThe idea is that you can have any type of file, even binary, that you can privately use on the server.
\nhttps://docs.meteor.com/api/core.html#Meteor-settings
\n1 | // file: .deploy/local.json |
You can access the settings from the client-side:\n1
Meteor.settings.public.visible
You can access all settings from the server-side:\n1
Meteor.settings.private.API_KEY
In the previous chapter we learned how to harness Meteor’s powers. But we didn’t see anything actually happening in the UI (The View Layer).
\nMeteor is a data-system more than anything else, ofcourse it gives you some snacks to help you\ncode faster: The User System, Emails, much more. But the View Layer it’s up to you.
\nDon’t get this the wrong way, Meteor can be integrated with any View Layer/Template Engine/UI Framework you want to use.
\nWhen Meteor started there was no stable solution for building reactive views. They had to invent a way.\nThis is how Blaze was born. http://blazejs.org/. In Q4 2015, they decided to drop\nsupport for Blaze, not because it wasn’t good, but because there were other teams that focused only on the\nview layer. You cannot expect one company to do everything. So they began decoupling Blaze from Meteor.
\nEven if Blaze is one of the most friendliest View Layers you’ll ever meet, they gave it to the community\nto take care of it, decoupling it from Meteor. This does not mean you should not use Blaze. Blaze is rock-solid and battle-tested.
\nFrom a personal opinionated experience, we noticed that for mobile builds, Blaze is inferior to React, in terms\nof speed and stability. Especially for older mobile phones.
\nTimes changed, new View Layers appeared that support reactivity such as:
\nIn terms of styling the UI, we are going to use CSS and to make our life easier, we can opt for a pre-processor:
\nWe could also use a front-end framework like:
\nEach has advantages and disadvantages. So question now is what to choose?
\nOur current recommendation is: React
\nReasons for this choice:
\nHowever, at this stage, (Q4 2016) Vue.JS is looking very promising and gaining a lot of momentum. \nWho wins? It’s a race. It’s the survival of the fittest. Time will decide. For now, pick a technology\nand master it.
\nKeep in mind, many companies out there still use software written 50 years ago in COBOL. Why ? Because they work and to the job.
\nOur current recommendation is to use SASS (SCSS) and no framework. Probably you are asking why we are not opting for a framework. The reason is that a framework like boostrap (one of the most used frameworks) comes with jquery dependecies which are not recommended to use with React. To read more why it is not recommended to use jquery, click here
\n","site":{"data":{}},"excerpt":"","more":"In the previous chapter we learned how to harness Meteor’s powers. But we didn’t see anything actually happening in the UI (The View Layer).
\nMeteor is a data-system more than anything else, ofcourse it gives you some snacks to help you\ncode faster: The User System, Emails, much more. But the View Layer it’s up to you.
\nDon’t get this the wrong way, Meteor can be integrated with any View Layer/Template Engine/UI Framework you want to use.
\nWhen Meteor started there was no stable solution for building reactive views. They had to invent a way.\nThis is how Blaze was born. http://blazejs.org/. In Q4 2015, they decided to drop\nsupport for Blaze, not because it wasn’t good, but because there were other teams that focused only on the\nview layer. You cannot expect one company to do everything. So they began decoupling Blaze from Meteor.
\nEven if Blaze is one of the most friendliest View Layers you’ll ever meet, they gave it to the community\nto take care of it, decoupling it from Meteor. This does not mean you should not use Blaze. Blaze is rock-solid and battle-tested.
\nFrom a personal opinionated experience, we noticed that for mobile builds, Blaze is inferior to React, in terms\nof speed and stability. Especially for older mobile phones.
\nTimes changed, new View Layers appeared that support reactivity such as:
\nIn terms of styling the UI, we are going to use CSS and to make our life easier, we can opt for a pre-processor:
\nWe could also use a front-end framework like:
\nEach has advantages and disadvantages. So question now is what to choose?
\nOur current recommendation is: React
\nReasons for this choice:
\nHowever, at this stage, (Q4 2016) Vue.JS is looking very promising and gaining a lot of momentum. \nWho wins? It’s a race. It’s the survival of the fittest. Time will decide. For now, pick a technology\nand master it.
\nKeep in mind, many companies out there still use software written 50 years ago in COBOL. Why ? Because they work and to the job.
\nOur current recommendation is to use SASS (SCSS) and no framework. Probably you are asking why we are not opting for a framework. The reason is that a framework like boostrap (one of the most used frameworks) comes with jquery dependecies which are not recommended to use with React. To read more why it is not recommended to use jquery, click here
\n"},{"title":"Chapter 1 - Conclusion","description":"Here we are, at the end of this chapter.","disqusPage":"Chapter 1: Conclusion","_content":"\nSo, even if we barely scratched the surface of the many good things Meteor has to offer, the journey has begun.\n\nYou have how acquired new knowledge on how to use Meteor's powers for your application:\n\n- How to store your data in the database via Collections\n- How to use Methods to perform server side actions and return responses to the client\n- Use Meteor's reactive system to see live updates\n- How to secure your methods/publications for a robust application\n\n\nFurther reading:\n\n- https://guide.meteor.com/ \n- https://themeteorchef.com/\n\n\nFeedback is not just welcomed, it's expected. Please share your experience with this tutorial to help us improve \nit for the next readers!","source":"chapters/1/conclusion.md","raw":"---\ntitle: Chapter 1 - Conclusion\ndescription: Here we are, at the end of this chapter.\ndisqusPage: 'Chapter 1: Conclusion'\n---\n\nSo, even if we barely scratched the surface of the many good things Meteor has to offer, the journey has begun.\n\nYou have how acquired new knowledge on how to use Meteor's powers for your application:\n\n- How to store your data in the database via Collections\n- How to use Methods to perform server side actions and return responses to the client\n- Use Meteor's reactive system to see live updates\n- How to secure your methods/publications for a robust application\n\n\nFurther reading:\n\n- https://guide.meteor.com/ \n- https://themeteorchef.com/\n\n\nFeedback is not just welcomed, it's expected. Please share your experience with this tutorial to help us improve \nit for the next readers!","date":"2018-03-08T15:53:49.079Z","updated":"2018-03-08T15:53:49.079Z","path":"chapters/1/conclusion.html","_id":"civ9g0npk0003emjxii61m771","comments":1,"layout":"page","content":"So, even if we barely scratched the surface of the many good things Meteor has to offer, the journey has begun.
\nYou have how acquired new knowledge on how to use Meteor’s powers for your application:
\nFurther reading:
\n\nFeedback is not just welcomed, it’s expected. Please share your experience with this tutorial to help us improve \nit for the next readers!
\n","site":{"data":{}},"excerpt":"","more":"So, even if we barely scratched the surface of the many good things Meteor has to offer, the journey has begun.
\nYou have how acquired new knowledge on how to use Meteor’s powers for your application:
\nFurther reading:
\n\nFeedback is not just welcomed, it’s expected. Please share your experience with this tutorial to help us improve \nit for the next readers!
\n"},{"title":"Enterprise Level","description":"Getting Meteor to Enterprise Level","disqusPage":"Chapter 3: Intro","_content":"\n## Welcome\n\nIn this chapter we are going to talk about what it takes to write a solid back-end and how we solve common issues in a very elegant manner.\n\nWe are not going to work on how to structure the front-end part. We are going to address topics such as: how do we structure the code, how do we test it, how do we lint it, and all sorts of elements\nthat make an app great behind the scenes and simply a joy to work with.\n\n## Why?\n\nWe at [Cult of Coders](https://www.cultofcoders.com) believe in the future of Meteor, making things easy for development is the key of success. But even if you have a powerful tool like Meteor in your hands, you can still make many mistakes and spend time learning from them.\nWe made these mistakes so you don't have to. \n\nSo why are we sharing them for free? When we could easily create a book and make some good bucks? Because money is not our objective. A thriving community is more valuable to us.\n\nThe main reason is that we love and support the community, the next is by engaging the community in this, we are able to gain more insights and to improve our knowledge.\n \nThat being said, we hope you enjoy this chapter and that it will open your mind.\n\nMay the code be with you.\n\n## Beginning the adventure\n\nWe will begin with a story, a young developer joining a team of Meteor developers, and he receives his first task:\n\n`\nAs a user, I want to create a post.\nAfter creation, I want to send an email to the admin so he can approve it.\nAfter approval, find the users that are interested in this post by their interests, and notify them.\n`\n\nFrom the client I would craft a form, and do something like a method call, but since our focus is backend, we won't get too much on the frontend\nside of things.\n\n```js\nMeteor.call('posts.create', {\n title: 'I wanna learn Meteor!',\n tags: ['philosophy'],\n})\n```\n\nIn the server you start coding:\n```js\nimport Posts from '/some/where/collection.js';\n\nMeteor.methods({\n 'posts.create'(post) {\n Posts.insert(post);\n \n Email.send({\n to: 'admin@app.com',\n from: 'notifications@app.com',\n subject: 'New Post',\n html: `\n You can access it here: ${Meteor.absoluteUrl(`/posts/${post._id}`)}\n `\n })\n },\n 'posts.approve'(postId) {\n const post = Posts.findOne(postId);\n Posts.update(postId, {\n $set: {\n isApproved: true\n }\n });\n \n const users = Meteor.users.find({\n interests: {$in: post.tags}\n }).fetch();\n \n users.forEach(user => {\n Email.send({\n to: user.emails[0].address,\n from: 'notifications@app.com',\n subject: 'New Post With Your Interest',\n html: `\n You can access it here: ${Meteor.absoluteUrl(`/posts/${post._id}`)}\n `\n })\n })\n },\n})\n```\n\nThis looks like a simple way of doing things, the code is relatively clean, and it does its job.\n\nBut, then the *Bigshot Code Reviewer* comes to you and says the following:\n\n1. You need to validate the user when creating a post\n2. What prevents the user from setting `isApproved: true` in the post object ? Never trust the client!\n3. What if I want to change `notifications@app.com` from one place.\n4. Post approval is not secured, you need an Admin role to do that\n5. What if the `/posts/:_id` route changes I want to be able to change it from one place\n6. What if I have multiple admins and want to notify them all ?\n7. The tags need to be validated as well, we don't allow all possible tags\n\nBecause you are a good developer you listen to all of his requests and you start coding, then it will look something like this:\n\n```js\nimport Posts from '/some/where/collection.js';\n\nconst FROM = Meteor.settings.emails.from; // You decide to put your \"from\" in settings, which is an ok approach.\nconst TAGS = ['psychology', 'philosophy'];\n\nfunction generatePostUrl(_id) {\n return Meteor.absoluteUrl(`/posts/${_id}`);\n}\n\nMeteor.methods({\n 'posts.create'(post) {\n if (!this.userId) {\n throw new Meteor.Error('not-allowed');\n }\n \n if (post.isApproved) {\n delete post['isApproved'];\n }\n \n if (post.tags) {\n let cleanedTags = [];\n post.tags.forEach(tag => {\n if (_.find(TAGS, tag)) {\n cleanedTags.push(tag);\n }\n })\n \n post.tags = cleanedTags;\n }\n \n Posts.insert(post);\n \n const admins = Meteor.users.find({\n roles: {$in: ['ADMIN']}\n }).fetch();\n \n admins.forEach(admin => {\n Email.send({\n to: admin.emails[0].address,\n from: FROM,\n subject: 'New Post',\n html: `\n You can access it here: ${generatePostUrl(post._id)}\n `\n })\n })\n },\n 'posts.approve'(postId) {\n if (!Roles.userIsInRole(this.userId, 'ADMIN')) {\n throw new Meteor.Error('not-allowed');\n }\n \n const post = Posts.findOne(postId);\n \n if (!post) {\n throw new Meteor.Error('not-found');\n }\n \n Posts.update(postId, {\n $set: {\n isApproved: true\n }\n });\n \n const users = Meteor.users.find({\n interests: {$in: post.tags}\n }).fetch();\n \n users.forEach(user => {\n Email.send({\n to: user.emails[0].address,\n from: FROM,\n subject: 'New Post With Your Interest',\n html: `\n You can access it here: ${generatePostUrl(post._id)}\n `\n })\n })\n },\n})\n```\n\n## Implementation\n\nBy this time your code has grown a lot, and you send it for review being optimistic... but our **Bigshot Code Reviewer** comes with a new set of requests:\n\n1. I need to be able to validate these tags at User level as well\n2. I want to have Emails centralized somewhere so I can have a nice layout\n3. When you find users you fetch a lot of data you don't need!\n4. ... \n\nSo what's the problem here? You want to do your job, and receiving so many comments makes you feel like you don't know jack. Then, after you implement them you realize\nthat your code grows and grows and if another developer wants to read your methods he will have a hard time because it's simply too much. What if later on you want to send some Push Notifications,\nor other stuff, a method can grow to 200 lines? Unacceptable.\n\n```bash\nQ: Is there any way in this lonesome world?\nA: Yes. Use services.\n```\n\nLet's begin first with our Emailing service:\n```js\n// file: '/some/where/services/EmailService.js'\nconst FROM = Meteor.settings.emails.from;\n\nfunction generatePostUrl(_id) {\n return Meteor.absoluteUrl(`/posts/${_id}`);\n}\n\nclass EmailService {\n sendPostForApproval(userId, postId) {\n Email.send({\n to: this._getEmailForUser(userId),\n from: FROM,\n subject: 'New Post',\n html: `\n You can access it here: ${generatePostUrl(post._id)}\n `\n })\n }\n \n sendPostForInterest(userId, postId) {\n Email.send({\n to: this._getEmailForUser(userId),\n from: FROM,\n subject: 'New Post With Your Interest',\n html: `\n You can access it here: ${generatePostUrl(post._id)}\n `\n })\n }\n \n _getEmailForUser(userId) {\n const user = Meteor.users.findOne(userId, {\n fields: {emails: 1}\n })\n \n if (!user) {\n throw new Meteor.Error('not-found');\n }\n \n return user.emails[0].address;\n }\n}\n\nexport default EmailService;\n```\n \nWe already decouple mail sending in a nice manner, and we aren't fetching from database things we don't need. Nice!\n \n```js\n// file: '/some/where/services/PostService.js'\nconst TAGS = ['psychology', 'philosophy'];\n\nclass PostService {\n static createPost(userId, post) {\n // the services is not your security layer, so we shouldn't do the check here\n this._validateCreationPost(post)\n post.userId = userId;\n \n const postId = Posts.insert(post);\n this.notifyAdminForApprovalOfPost(post);\n \n return postId;\n }\n \n static approvePost(postId) {\n const post = this._getPost(postId);\n Posts.update(postId, {\n $set: {isApproved: true}\n });\n \n this.notifyUsersOfPosts(post);\n }\n \n static notifyAdminForApprovalOfPost(post) {\n const admins = Meteor.users.find({\n roles: {$in: ['ADMIN']}\n }, {\n fields: {_id: 1}\n }).fetch();\n \n admins.forEach(admin => {\n EmailService.sendPostForApproval(admin._id, post._id)\n });\n }\n \n static notifyUsersOfPosts(post) {\n const users = Meteor.users.find({\n interests: {$in: post.tags}\n }, {\n fields: {_id: 1}\n }).fetch();\n \n users.forEach(user => {\n EmailService.sendPostForInterest(user._id, post._id);\n })\n }\n \n static _getPost(postId) {\n const post = Posts.findOne(postId);\n \n if (!post) {\n throw new Meteor.Error('not-found');\n }\n \n return post;\n }\n \n static _validateCreationPost(post) {\n if (post.isApproved) {\n delete post['isApproved'];\n }\n \n if (post.tags) {\n let cleanedTags = [];\n post.tags.forEach(tag => {\n if (_.find(TAGS, tag)) {\n cleanedTags.push(tag);\n }\n })\n \n post.tags = cleanedTags;\n }\n }\n}\n\nexport default PostService;\n```\n\nOk, nice, now as you can see the code can be read as poetry. By decoupling functions you understand what they do without seeing the code, this brings verbosity and makes the code a pleasure to work with.\nSo, how will our methods look after these changes?\n\n```js\nMeteor.methods({\n 'posts.create'(post) {\n if (!this.userId) {\n throw new Meteor.Error('not-allowed');\n }\n \n return PostService.createPost(post);\n },\n 'posts.approve'(postId) {\n if (!Roles.userIsInRole(this.userId, 'ADMIN')) {\n throw new Meteor.Error('not-allowed');\n }\n \n return PostService.approvePost(postId);\n }\n})\n```\n\nWow! So clean and so sexy! Much more readable. But are we there yet? Is this how the code should look?\nNot by far. \n\nBut we made some good progress already, good job!\nWe understood that separation of logic, makes the code manageable, easy to understand, and easier to test.\n\nSo why do we say *not by far* ?\n\nWell, the **Bigshot** will still see some problems:\n1. If I want to write a test for PostService but not send emails how would I do that ?\n2. Tags need to be centralized somewhere and apply same validation when User updates interests.\n3. What if the user adds some extra fields to post object ? How do I stop that ?\n4. What if that absolute path for seeing posts needs also to be decoupled and used somewhere ?\n5. Emails still don't have a nice re-usable layout\n6. PostService should be about Posts, not about notifying others, it has too much logic.\n7. ...\n\nOk, how do we close this **Bigshot**'s mouth? We continue learning the principles behind quality code.\n\nLet the adventure begin! But first, you must understand some basic stuff regarding programming in javascript:\n\nThis is the best resource I've found:\nhttps://github.com/ryanmcdermott/clean-code-javascript \n\nIt's based on Robert Martin's Clean Code book but tailored for our love, JavaScript.\n\nPlease don't treat it as just another link, you must absorb the teachings there, and make sure that until you master them,\nyou read them daily or at least weekly for 2 months.\n\nEven I read it from time to time, to refresh my memory.\n\n`After all this time? Always.`","source":"chapters/3/intro.md","raw":"---\ntitle: 'Enterprise Level'\ndescription: Getting Meteor to Enterprise Level\ndisqusPage: 'Chapter 3: Intro'\n---\n\n## Welcome\n\nIn this chapter we are going to talk about what it takes to write a solid back-end and how we solve common issues in a very elegant manner.\n\nWe are not going to work on how to structure the front-end part. We are going to address topics such as: how do we structure the code, how do we test it, how do we lint it, and all sorts of elements\nthat make an app great behind the scenes and simply a joy to work with.\n\n## Why?\n\nWe at [Cult of Coders](https://www.cultofcoders.com) believe in the future of Meteor, making things easy for development is the key of success. But even if you have a powerful tool like Meteor in your hands, you can still make many mistakes and spend time learning from them.\nWe made these mistakes so you don't have to. \n\nSo why are we sharing them for free? When we could easily create a book and make some good bucks? Because money is not our objective. A thriving community is more valuable to us.\n\nThe main reason is that we love and support the community, the next is by engaging the community in this, we are able to gain more insights and to improve our knowledge.\n \nThat being said, we hope you enjoy this chapter and that it will open your mind.\n\nMay the code be with you.\n\n## Beginning the adventure\n\nWe will begin with a story, a young developer joining a team of Meteor developers, and he receives his first task:\n\n`\nAs a user, I want to create a post.\nAfter creation, I want to send an email to the admin so he can approve it.\nAfter approval, find the users that are interested in this post by their interests, and notify them.\n`\n\nFrom the client I would craft a form, and do something like a method call, but since our focus is backend, we won't get too much on the frontend\nside of things.\n\n```js\nMeteor.call('posts.create', {\n title: 'I wanna learn Meteor!',\n tags: ['philosophy'],\n})\n```\n\nIn the server you start coding:\n```js\nimport Posts from '/some/where/collection.js';\n\nMeteor.methods({\n 'posts.create'(post) {\n Posts.insert(post);\n \n Email.send({\n to: 'admin@app.com',\n from: 'notifications@app.com',\n subject: 'New Post',\n html: `\n You can access it here: ${Meteor.absoluteUrl(`/posts/${post._id}`)}\n `\n })\n },\n 'posts.approve'(postId) {\n const post = Posts.findOne(postId);\n Posts.update(postId, {\n $set: {\n isApproved: true\n }\n });\n \n const users = Meteor.users.find({\n interests: {$in: post.tags}\n }).fetch();\n \n users.forEach(user => {\n Email.send({\n to: user.emails[0].address,\n from: 'notifications@app.com',\n subject: 'New Post With Your Interest',\n html: `\n You can access it here: ${Meteor.absoluteUrl(`/posts/${post._id}`)}\n `\n })\n })\n },\n})\n```\n\nThis looks like a simple way of doing things, the code is relatively clean, and it does its job.\n\nBut, then the *Bigshot Code Reviewer* comes to you and says the following:\n\n1. You need to validate the user when creating a post\n2. What prevents the user from setting `isApproved: true` in the post object ? Never trust the client!\n3. What if I want to change `notifications@app.com` from one place.\n4. Post approval is not secured, you need an Admin role to do that\n5. What if the `/posts/:_id` route changes I want to be able to change it from one place\n6. What if I have multiple admins and want to notify them all ?\n7. The tags need to be validated as well, we don't allow all possible tags\n\nBecause you are a good developer you listen to all of his requests and you start coding, then it will look something like this:\n\n```js\nimport Posts from '/some/where/collection.js';\n\nconst FROM = Meteor.settings.emails.from; // You decide to put your \"from\" in settings, which is an ok approach.\nconst TAGS = ['psychology', 'philosophy'];\n\nfunction generatePostUrl(_id) {\n return Meteor.absoluteUrl(`/posts/${_id}`);\n}\n\nMeteor.methods({\n 'posts.create'(post) {\n if (!this.userId) {\n throw new Meteor.Error('not-allowed');\n }\n \n if (post.isApproved) {\n delete post['isApproved'];\n }\n \n if (post.tags) {\n let cleanedTags = [];\n post.tags.forEach(tag => {\n if (_.find(TAGS, tag)) {\n cleanedTags.push(tag);\n }\n })\n \n post.tags = cleanedTags;\n }\n \n Posts.insert(post);\n \n const admins = Meteor.users.find({\n roles: {$in: ['ADMIN']}\n }).fetch();\n \n admins.forEach(admin => {\n Email.send({\n to: admin.emails[0].address,\n from: FROM,\n subject: 'New Post',\n html: `\n You can access it here: ${generatePostUrl(post._id)}\n `\n })\n })\n },\n 'posts.approve'(postId) {\n if (!Roles.userIsInRole(this.userId, 'ADMIN')) {\n throw new Meteor.Error('not-allowed');\n }\n \n const post = Posts.findOne(postId);\n \n if (!post) {\n throw new Meteor.Error('not-found');\n }\n \n Posts.update(postId, {\n $set: {\n isApproved: true\n }\n });\n \n const users = Meteor.users.find({\n interests: {$in: post.tags}\n }).fetch();\n \n users.forEach(user => {\n Email.send({\n to: user.emails[0].address,\n from: FROM,\n subject: 'New Post With Your Interest',\n html: `\n You can access it here: ${generatePostUrl(post._id)}\n `\n })\n })\n },\n})\n```\n\n## Implementation\n\nBy this time your code has grown a lot, and you send it for review being optimistic... but our **Bigshot Code Reviewer** comes with a new set of requests:\n\n1. I need to be able to validate these tags at User level as well\n2. I want to have Emails centralized somewhere so I can have a nice layout\n3. When you find users you fetch a lot of data you don't need!\n4. ... \n\nSo what's the problem here? You want to do your job, and receiving so many comments makes you feel like you don't know jack. Then, after you implement them you realize\nthat your code grows and grows and if another developer wants to read your methods he will have a hard time because it's simply too much. What if later on you want to send some Push Notifications,\nor other stuff, a method can grow to 200 lines? Unacceptable.\n\n```bash\nQ: Is there any way in this lonesome world?\nA: Yes. Use services.\n```\n\nLet's begin first with our Emailing service:\n```js\n// file: '/some/where/services/EmailService.js'\nconst FROM = Meteor.settings.emails.from;\n\nfunction generatePostUrl(_id) {\n return Meteor.absoluteUrl(`/posts/${_id}`);\n}\n\nclass EmailService {\n sendPostForApproval(userId, postId) {\n Email.send({\n to: this._getEmailForUser(userId),\n from: FROM,\n subject: 'New Post',\n html: `\n You can access it here: ${generatePostUrl(post._id)}\n `\n })\n }\n \n sendPostForInterest(userId, postId) {\n Email.send({\n to: this._getEmailForUser(userId),\n from: FROM,\n subject: 'New Post With Your Interest',\n html: `\n You can access it here: ${generatePostUrl(post._id)}\n `\n })\n }\n \n _getEmailForUser(userId) {\n const user = Meteor.users.findOne(userId, {\n fields: {emails: 1}\n })\n \n if (!user) {\n throw new Meteor.Error('not-found');\n }\n \n return user.emails[0].address;\n }\n}\n\nexport default EmailService;\n```\n \nWe already decouple mail sending in a nice manner, and we aren't fetching from database things we don't need. Nice!\n \n```js\n// file: '/some/where/services/PostService.js'\nconst TAGS = ['psychology', 'philosophy'];\n\nclass PostService {\n static createPost(userId, post) {\n // the services is not your security layer, so we shouldn't do the check here\n this._validateCreationPost(post)\n post.userId = userId;\n \n const postId = Posts.insert(post);\n this.notifyAdminForApprovalOfPost(post);\n \n return postId;\n }\n \n static approvePost(postId) {\n const post = this._getPost(postId);\n Posts.update(postId, {\n $set: {isApproved: true}\n });\n \n this.notifyUsersOfPosts(post);\n }\n \n static notifyAdminForApprovalOfPost(post) {\n const admins = Meteor.users.find({\n roles: {$in: ['ADMIN']}\n }, {\n fields: {_id: 1}\n }).fetch();\n \n admins.forEach(admin => {\n EmailService.sendPostForApproval(admin._id, post._id)\n });\n }\n \n static notifyUsersOfPosts(post) {\n const users = Meteor.users.find({\n interests: {$in: post.tags}\n }, {\n fields: {_id: 1}\n }).fetch();\n \n users.forEach(user => {\n EmailService.sendPostForInterest(user._id, post._id);\n })\n }\n \n static _getPost(postId) {\n const post = Posts.findOne(postId);\n \n if (!post) {\n throw new Meteor.Error('not-found');\n }\n \n return post;\n }\n \n static _validateCreationPost(post) {\n if (post.isApproved) {\n delete post['isApproved'];\n }\n \n if (post.tags) {\n let cleanedTags = [];\n post.tags.forEach(tag => {\n if (_.find(TAGS, tag)) {\n cleanedTags.push(tag);\n }\n })\n \n post.tags = cleanedTags;\n }\n }\n}\n\nexport default PostService;\n```\n\nOk, nice, now as you can see the code can be read as poetry. By decoupling functions you understand what they do without seeing the code, this brings verbosity and makes the code a pleasure to work with.\nSo, how will our methods look after these changes?\n\n```js\nMeteor.methods({\n 'posts.create'(post) {\n if (!this.userId) {\n throw new Meteor.Error('not-allowed');\n }\n \n return PostService.createPost(post);\n },\n 'posts.approve'(postId) {\n if (!Roles.userIsInRole(this.userId, 'ADMIN')) {\n throw new Meteor.Error('not-allowed');\n }\n \n return PostService.approvePost(postId);\n }\n})\n```\n\nWow! So clean and so sexy! Much more readable. But are we there yet? Is this how the code should look?\nNot by far. \n\nBut we made some good progress already, good job!\nWe understood that separation of logic, makes the code manageable, easy to understand, and easier to test.\n\nSo why do we say *not by far* ?\n\nWell, the **Bigshot** will still see some problems:\n1. If I want to write a test for PostService but not send emails how would I do that ?\n2. Tags need to be centralized somewhere and apply same validation when User updates interests.\n3. What if the user adds some extra fields to post object ? How do I stop that ?\n4. What if that absolute path for seeing posts needs also to be decoupled and used somewhere ?\n5. Emails still don't have a nice re-usable layout\n6. PostService should be about Posts, not about notifying others, it has too much logic.\n7. ...\n\nOk, how do we close this **Bigshot**'s mouth? We continue learning the principles behind quality code.\n\nLet the adventure begin! But first, you must understand some basic stuff regarding programming in javascript:\n\nThis is the best resource I've found:\nhttps://github.com/ryanmcdermott/clean-code-javascript \n\nIt's based on Robert Martin's Clean Code book but tailored for our love, JavaScript.\n\nPlease don't treat it as just another link, you must absorb the teachings there, and make sure that until you master them,\nyou read them daily or at least weekly for 2 months.\n\nEven I read it from time to time, to refresh my memory.\n\n`After all this time? Always.`","date":"2017-11-10T12:45:17.017Z","updated":"2017-11-10T12:45:17.017Z","path":"chapters/3/intro.html","_id":"cj8zm74a20000qmu55ra4wato","comments":1,"layout":"page","content":"In this chapter we are going to talk about what it takes to write a solid back-end and how we solve common issues in a very elegant manner.
\nWe are not going to work on how to structure the front-end part. We are going to address topics such as: how do we structure the code, how do we test it, how do we lint it, and all sorts of elements\nthat make an app great behind the scenes and simply a joy to work with.
\nWe at Cult of Coders believe in the future of Meteor, making things easy for development is the key of success. But even if you have a powerful tool like Meteor in your hands, you can still make many mistakes and spend time learning from them.\nWe made these mistakes so you don’t have to.
\nSo why are we sharing them for free? When we could easily create a book and make some good bucks? Because money is not our objective. A thriving community is more valuable to us.
\nThe main reason is that we love and support the community, the next is by engaging the community in this, we are able to gain more insights and to improve our knowledge.
\nThat being said, we hope you enjoy this chapter and that it will open your mind.
\nMay the code be with you.
\nWe will begin with a story, a young developer joining a team of Meteor developers, and he receives his first task:
\nAs a user, I want to create a post.\nAfter creation, I want to send an email to the admin so he can approve it.\nAfter approval, find the users that are interested in this post by their interests, and notify them.
From the client I would craft a form, and do something like a method call, but since our focus is backend, we won’t get too much on the frontend\nside of things.
\n
|
|
In the server you start coding:\n
This looks like a simple way of doing things, the code is relatively clean, and it does its job.
\nBut, then the Bigshot Code Reviewer comes to you and says the following:
\nisApproved: true
in the post object ? Never trust the client!notifications@app.com
from one place./posts/:_id
route changes I want to be able to change it from one placeBecause you are a good developer you listen to all of his requests and you start coding, then it will look something like this:
\n
|
|
By this time your code has grown a lot, and you send it for review being optimistic… but our Bigshot Code Reviewer comes with a new set of requests:
\nSo what’s the problem here? You want to do your job, and receiving so many comments makes you feel like you don’t know jack. Then, after you implement them you realize\nthat your code grows and grows and if another developer wants to read your methods he will have a hard time because it’s simply too much. What if later on you want to send some Push Notifications,\nor other stuff, a method can grow to 200 lines? Unacceptable.
\n
|
|
Let’s begin first with our Emailing service:\n
We already decouple mail sending in a nice manner, and we aren’t fetching from database things we don’t need. Nice!
\n
|
|
Ok, nice, now as you can see the code can be read as poetry. By decoupling functions you understand what they do without seeing the code, this brings verbosity and makes the code a pleasure to work with.\nSo, how will our methods look after these changes?
\n
|
|
Wow! So clean and so sexy! Much more readable. But are we there yet? Is this how the code should look?\nNot by far.
\nBut we made some good progress already, good job!\nWe understood that separation of logic, makes the code manageable, easy to understand, and easier to test.
\nSo why do we say not by far ?
\nWell, the Bigshot will still see some problems:
\nOk, how do we close this Bigshot‘s mouth? We continue learning the principles behind quality code.
\nLet the adventure begin! But first, you must understand some basic stuff regarding programming in javascript:
\nThis is the best resource I’ve found:\nhttps://github.com/ryanmcdermott/clean-code-javascript
\nIt’s based on Robert Martin’s Clean Code book but tailored for our love, JavaScript.
\nPlease don’t treat it as just another link, you must absorb the teachings there, and make sure that until you master them,\nyou read them daily or at least weekly for 2 months.
\nEven I read it from time to time, to refresh my memory.
\nAfter all this time? Always.
In this chapter we are going to talk about what it takes to write a solid back-end and how we solve common issues in a very elegant manner.
\nWe are not going to work on how to structure the front-end part. We are going to address topics such as: how do we structure the code, how do we test it, how do we lint it, and all sorts of elements\nthat make an app great behind the scenes and simply a joy to work with.
\nWe at Cult of Coders believe in the future of Meteor, making things easy for development is the key of success. But even if you have a powerful tool like Meteor in your hands, you can still make many mistakes and spend time learning from them.\nWe made these mistakes so you don’t have to.
\nSo why are we sharing them for free? When we could easily create a book and make some good bucks? Because money is not our objective. A thriving community is more valuable to us.
\nThe main reason is that we love and support the community, the next is by engaging the community in this, we are able to gain more insights and to improve our knowledge.
\nThat being said, we hope you enjoy this chapter and that it will open your mind.
\nMay the code be with you.
\nWe will begin with a story, a young developer joining a team of Meteor developers, and he receives his first task:
\nAs a user, I want to create a post.\nAfter creation, I want to send an email to the admin so he can approve it.\nAfter approval, find the users that are interested in this post by their interests, and notify them.
From the client I would craft a form, and do something like a method call, but since our focus is backend, we won’t get too much on the frontend\nside of things.
\n
|
|
In the server you start coding:\n
This looks like a simple way of doing things, the code is relatively clean, and it does its job.
\nBut, then the Bigshot Code Reviewer comes to you and says the following:
\nisApproved: true
in the post object ? Never trust the client!notifications@app.com
from one place./posts/:_id
route changes I want to be able to change it from one placeBecause you are a good developer you listen to all of his requests and you start coding, then it will look something like this:
\n
|
|
By this time your code has grown a lot, and you send it for review being optimistic… but our Bigshot Code Reviewer comes with a new set of requests:
\nSo what’s the problem here? You want to do your job, and receiving so many comments makes you feel like you don’t know jack. Then, after you implement them you realize\nthat your code grows and grows and if another developer wants to read your methods he will have a hard time because it’s simply too much. What if later on you want to send some Push Notifications,\nor other stuff, a method can grow to 200 lines? Unacceptable.
\n
|
|
Let’s begin first with our Emailing service:\n
We already decouple mail sending in a nice manner, and we aren’t fetching from database things we don’t need. Nice!
\n
|
|
Ok, nice, now as you can see the code can be read as poetry. By decoupling functions you understand what they do without seeing the code, this brings verbosity and makes the code a pleasure to work with.\nSo, how will our methods look after these changes?
\n
|
|
Wow! So clean and so sexy! Much more readable. But are we there yet? Is this how the code should look?\nNot by far.
\nBut we made some good progress already, good job!\nWe understood that separation of logic, makes the code manageable, easy to understand, and easier to test.
\nSo why do we say not by far ?
\nWell, the Bigshot will still see some problems:
\nOk, how do we close this Bigshot‘s mouth? We continue learning the principles behind quality code.
\nLet the adventure begin! But first, you must understand some basic stuff regarding programming in javascript:
\nThis is the best resource I’ve found:\nhttps://github.com/ryanmcdermott/clean-code-javascript
\nIt’s based on Robert Martin’s Clean Code book but tailored for our love, JavaScript.
\nPlease don’t treat it as just another link, you must absorb the teachings there, and make sure that until you master them,\nyou read them daily or at least weekly for 2 months.
\nEven I read it from time to time, to refresh my memory.
\nAfter all this time? Always.
We really hope that this chapter has shared some insights into how to architect your Meteor app, and what are some\nof the best practices we identified.
\nYou should be on your way now to write beautiful, decoupled code that is easy to understand.
\nNow, I have a task for you, remember that first task the user received in his intro ?
\nLet’s try and apply the principles we discussed and go ahead and create a public GitHub repository and write a project\nthat will do the following:
\nFocus on backend, not frontend. The purpose here is to architect the backend right.
\nWould you pass the Bigshot Code Reviewer‘s review?
\nLeave your link to the repository down below, and let other users review it! See if it’s truly Enterprise level.
\nYour feedback is very important. Feel free to ask questions, if they are specific to a certain part of this chapter,\ngo to that page and leave a comment there. Thank you!
\n","site":{"data":{}},"excerpt":"","more":"We really hope that this chapter has shared some insights into how to architect your Meteor app, and what are some\nof the best practices we identified.
\nYou should be on your way now to write beautiful, decoupled code that is easy to understand.
\nNow, I have a task for you, remember that first task the user received in his intro ?
\nLet’s try and apply the principles we discussed and go ahead and create a public GitHub repository and write a project\nthat will do the following:
\nFocus on backend, not frontend. The purpose here is to architect the backend right.
\nWould you pass the Bigshot Code Reviewer‘s review?
\nLeave your link to the repository down below, and let other users review it! See if it’s truly Enterprise level.
\nYour feedback is very important. Feel free to ask questions, if they are specific to a certain part of this chapter,\ngo to that page and leave a comment there. Thank you!
\n"},{"title":"React","description":"Integrating React with Meteor","disqusPage":"Chapter 2: React","_content":"\n## Introduction\n\nThis chapter shows you how to integrate React into Meteor with absolute ease. But be warned! This is not a React tutorial, in order\nto fully understand what's going on here check the following resources:\n\n- https://camjackson.net/post/9-things-every-reactjs-beginner-should-know\n- https://blog.risingstack.com/the-react-way-getting-started-tutorial/\n- https://egghead.io/courses/react-fundamentals\n- http://jaketrent.com/post/smart-dumb-components-react/\n\n## Setting Up\n```bash\nmeteor add cultofcoders:meteor-react-routing\nmeteor npm install --save react react-mounter react-dom \n```\n\n## Architecture\n\nAs we discussed in the first chapter, we want to build modular apps and not rely on auto loading.\nWe will store all our react components in the `/imports/ui` folder.\n\nRouting is a general concept. A router decides what to show to the user, and because we regard it\nas a very important piece in our app, we shall put it in `/imports/routing`\n\n## Routing\n\nPlease follow the README.md from: \nhttps://github.com/cult-of-coders/meteor-react-routing\n\nAfter you have setup your files, created `/imports/routing/router.js`, `/imports/routing/index.js`, and you included the routing in your client/startup folder,\ncreated your `/imports/ui/App.jsx` file, and now it's time to see this baby in action!\n\n```js\n// file: /imports/ui/Home.jsx\nimport React from 'react';\n\nexport default class Home extends React.Component {\n render() {\n returnThis chapter shows you how to integrate React into Meteor with absolute ease. But be warned! This is not a React tutorial, in order\nto fully understand what’s going on here check the following resources:
\n
|
|
As we discussed in the first chapter, we want to build modular apps and not rely on auto loading.\nWe will store all our react components in the /imports/ui
folder.
Routing is a general concept. A router decides what to show to the user, and because we regard it\nas a very important piece in our app, we shall put it in /imports/routing
Please follow the README.md from: \nhttps://github.com/cult-of-coders/meteor-react-routing
\nAfter you have setup your files, created /imports/routing/router.js
, /imports/routing/index.js
, and you included the routing in your client/startup folder,\ncreated your /imports/ui/App.jsx
file, and now it’s time to see this baby in action!
|
|
|
|
Now access it: http://localhost:3000/
\nBy default, if you read the tutorials on React, you realized that React is already reactive, by doing this.setState
it will\nrerender efficiently your Component.
Let’s see how we would call a method and show a response in our component:
\n
|
|
Try it out, see if it works.
\nFor publications we need Meteor’s reactivity, the Tracker, that we discussed about it in Chapter 1. However,\nfortunately we don’t have to reinvent the wheel for it, because Meteor has created a package that allows us\nto easily do it:
\nhttps://guide.meteor.com/react.html#using-createContainer
\n
|
|
|
|
Go ahead, try adding a new donut from the Meteor’s shell. You’ll see it updated live.
\nThis is it. It’s just this simple. You now have the knowledge of integrating Meteor & React. Now, we need to\nmaster it.
\nIn the homework you’ll most likely be dealing with forms, here are some starting points:
\nBut you can use everything you want.
\nThere is a package that leverages work for you:\nhttps://github.com/meteor-utilities/react-list-container
\nCreate a route /login
and a Login.jsx
file. In it you will have a form. When the form is submitted, \nit will call Meteor.loginWithPassword
and redirect him to “/“ Hint 1
Only allow logged in users to view the donuts list, it is very private. Hint
\nCreate a registration, use email and password for now.
\nAs a logged in user, present a form and createa donut via the Meteor.call(). You can do it in a separate route, or\nwhere you list the donuts. Whatever you choose, create another component. Always think in components.
\nIn the donuts list, add a checkbox, when you click it it will only show the donuts with price < 200. Make sure filter\nis in another component, when something changes, your subscription should change. This means you may need a container\nover on top of your container!
\nImplement a pagination strategy (You can use: https://www.npmjs.com/package/react-paginator). Where you display\nall the donuts, 3 items per page. Quick tip: You may need to create another method that retrieves the count for all donuts.
\nThis one is a bit tricky. But not impossible, if you want a true reactive pagination (which in most cases you don’t) but you want \nit know because you’re an over-achiever, check this meteor package for reactive counting:
\n\n","site":{"data":{}},"excerpt":"","more":"This chapter shows you how to integrate React into Meteor with absolute ease. But be warned! This is not a React tutorial, in order\nto fully understand what’s going on here check the following resources:
\n
|
|
As we discussed in the first chapter, we want to build modular apps and not rely on auto loading.\nWe will store all our react components in the /imports/ui
folder.
Routing is a general concept. A router decides what to show to the user, and because we regard it\nas a very important piece in our app, we shall put it in /imports/routing
Please follow the README.md from: \nhttps://github.com/cult-of-coders/meteor-react-routing
\nAfter you have setup your files, created /imports/routing/router.js
, /imports/routing/index.js
, and you included the routing in your client/startup folder,\ncreated your /imports/ui/App.jsx
file, and now it’s time to see this baby in action!
|
|
|
|
Now access it: http://localhost:3000/
\nBy default, if you read the tutorials on React, you realized that React is already reactive, by doing this.setState
it will\nrerender efficiently your Component.
Let’s see how we would call a method and show a response in our component:
\n
|
|
Try it out, see if it works.
\nFor publications we need Meteor’s reactivity, the Tracker, that we discussed about it in Chapter 1. However,\nfortunately we don’t have to reinvent the wheel for it, because Meteor has created a package that allows us\nto easily do it:
\nhttps://guide.meteor.com/react.html#using-createContainer
\n
|
|
|
|
Go ahead, try adding a new donut from the Meteor’s shell. You’ll see it updated live.
\nThis is it. It’s just this simple. You now have the knowledge of integrating Meteor & React. Now, we need to\nmaster it.
\nIn the homework you’ll most likely be dealing with forms, here are some starting points:
\nBut you can use everything you want.
\nThere is a package that leverages work for you:\nhttps://github.com/meteor-utilities/react-list-container
\nCreate a route /login
and a Login.jsx
file. In it you will have a form. When the form is submitted, \nit will call Meteor.loginWithPassword
and redirect him to “/“ Hint 1
Only allow logged in users to view the donuts list, it is very private. Hint
\nCreate a registration, use email and password for now.
\nAs a logged in user, present a form and createa donut via the Meteor.call(). You can do it in a separate route, or\nwhere you list the donuts. Whatever you choose, create another component. Always think in components.
\nIn the donuts list, add a checkbox, when you click it it will only show the donuts with price < 200. Make sure filter\nis in another component, when something changes, your subscription should change. This means you may need a container\nover on top of your container!
\nImplement a pagination strategy (You can use: https://www.npmjs.com/package/react-paginator). Where you display\nall the donuts, 3 items per page. Quick tip: You may need to create another method that retrieves the count for all donuts.
\nThis one is a bit tricky. But not impossible, if you want a true reactive pagination (which in most cases you don’t) but you want \nit know because you’re an over-achiever, check this meteor package for reactive counting:
\n\n"},{"title":"Emails","description":"How to work with emails","disqusPage":"Chapter 3: Emails","_content":"\nMost of the apps send out emails, but what is the best way to do it ?\n\nWell, I don't know about the best, but let's explore this solution.\n\nWe want emails to:\n- Be able to be found easily in a centralized place. It's very important they are not spread all over the app so we have control over them.\n- Be able to have a nice layout for everyone of them\n- Be easy to develop and easy to maintain\n\nSo, if we want to keep them centralized, it's clear that we must use [Events](/chapters/3/events.html). Therefore,\nwhenever we want to send an email we need to dispatch an event.\n\nSo, how do we render our emails ? React baby!\n\nSo we want to create a service that sends an email based on a React template. Let's see how it would look like:\n\n```js\n// file: /imports/api/emails/send.js\nimport {Email} from 'meteor/email';\nimport {Meteor} from 'meteor/meteor';\nimport React from 'react';\nimport ReactDOM from 'react-dom/server';\n\nconst debug = Meteor.isDevelopment;\n// const debug = false;\n\n/**\n * React Email Sender\n */\nexport default function (mailConfig, Component, props) {\n const element = React.createElement(Component, props);\n \n let options = _.extend({}, {\n from: 'notifications@app.com', // or get it from Meteor.settings\n html: ReactDOM.renderToString(element)\n }, mailConfig);\n\n if (debug) {\n console.log(options);\n } else {\n // so it won't lag your methods\n Meteor.defer(() => {\n Email.send(options);\n })\n }\n}\n```\n\nCool I think it's self descriptive what this function does, here is how it would look in a real-life scenario:\n\n```bash\n// file: /imports/api/emails/templates/NewItemCreated.jsx\nimport React from 'react';\nimport PropTypes from 'prop-types';\n\nconst NewItem = ({item}) {\n return (\n{headerText} | \n
{children} | \n
{headerText} | \n
{children} | \n
Most of the apps send out emails, but what is the best way to do it ?
\nWell, I don’t know about the best, but let’s explore this solution.
\nWe want emails to:
\nSo, if we want to keep them centralized, it’s clear that we must use Events. Therefore,\nwhenever we want to send an email we need to dispatch an event.
\nSo, how do we render our emails ? React baby!
\nSo we want to create a service that sends an email based on a React template. Let’s see how it would look like:
\n
|
|
Cool I think it’s self descriptive what this function does, here is how it would look in a real-life scenario:
\n
|
|
And to actually send it:
\n
|
|
Now if you want to create a layout enveloping your template it’s quite easy, you would do something like:
\n
|
|
And because we want to keep the send email as flexible as possible we will use the layout inside our templates:
\n
|
|
Both Layout
and ItemNewEmail
can change in anyway you like, depending on your context. \nOf course you can have multiple layouts, or no layout, it’s up to the email template to decide, not the send function, don’t forget this.
Isn’t this easy and clean ?
\n","site":{"data":{}},"excerpt":"","more":"Most of the apps send out emails, but what is the best way to do it ?
\nWell, I don’t know about the best, but let’s explore this solution.
\nWe want emails to:
\nSo, if we want to keep them centralized, it’s clear that we must use Events. Therefore,\nwhenever we want to send an email we need to dispatch an event.
\nSo, how do we render our emails ? React baby!
\nSo we want to create a service that sends an email based on a React template. Let’s see how it would look like:
\n
|
|
Cool I think it’s self descriptive what this function does, here is how it would look in a real-life scenario:
\n
|
|
And to actually send it:
\n
|
|
Now if you want to create a layout enveloping your template it’s quite easy, you would do something like:
\n
|
|
And because we want to keep the send email as flexible as possible we will use the layout inside our templates:
\n
|
|
Both Layout
and ItemNewEmail
can change in anyway you like, depending on your context. \nOf course you can have multiple layouts, or no layout, it’s up to the email template to decide, not the send function, don’t forget this.
Isn’t this easy and clean ?
\n"},{"title":"Events","description":"Observe and react to changes in a decoupled manner","disqusPage":"Chapter 3: Events","_content":"\n## The Problem\n\nLet's start with the problem. Something happens in your system, like: you add a new item for sale.\n\nWhen this happens you need to do:\n- Notify people that may be interested in your sale\n- Send an email to the Admin\n- Charge the person 0.02$ for the fact he posted a new item for sale on your platform\n\nSo far so good, you know that you need to have units of logic (services) for this, so after some coding hours (or minutes!) you come up with these functions:\n\n```js\nnotifyInterestedPeopleInSale(itemId);\nnotifyAdmins(itemId);\nprocessCharging(itemId);\n```\n\nNow you go to your `ItemService` and have something like:\n```js\nstatic createItem(data) {\n const itemId = Items.insert(data);\n \n notifyInterestedPeopleInSale(itemId);\n notifyAdmins(itemId);\n processCharging(itemId);\n}\n```\n\nAnd it seems that you are happy with this. It looks modular and decoupled. However it's not ok because:\n- **It knows too much** about other services\n- **It does too much**, it's purpose is to merely create an item, that's it.\n- **It depends on too many modules**, if by any chance you decide to remove admin notifications you need to see wherever you use it and remove it.\n\nBesides that, the name we have is not very verbose, what if we change it?\n```js\ncreatePostAndNotifyAdminsAndInterestedPeopleInSaleThenProcessCharging(data);\n```\n\n## The Solution\n\nOk we can't work with something like that, name too long, and we break the single responsability principle.\nIs there any hope for us ?\nCan we have good code when we have a lot of functionality ?\n\nOfcourse, let's rock and roll with the Observer pattern. The observer pattern is so simple to understand:\n- When X happens I want to do Y\n- Ability to say X happens\n- Ability to do Y when X happens\n\nIn code translation:\n```\nmeteor npm i -S event-emitter\n```\n\nPS: In Node 8 (Meteor 1.6), it is native: https://nodejs.org/api/events.html \n\n```js\n// file: /imports/api/events.js\nimport EventEmitter from 'event-emitter';\n\nconst Emitter = new EventEmitter();\n\nconst Events = {\n ITEM_CREATED: 'item_created'\n};\n\nexport { Emitter, Events }\n```\n\nNow we need to say to the system that an item has been created:\n```js\nimport {Emitter, Events} from '/imports/api/events';\n\nfunction createItem(userId, data) {\n const itemId = Items.insert(data);\n \n Emitter.emit(Events.ITEM_CREATED, {itemId, userId});\n}\n```\n\nNow, notifications and payment are two modules that aren't necessarily related, they don't really need to know about each other.\nThis is why our listeners, should be close to their code:\n\n```js\n// file: /imports/api/notifications/listeners.js\nimport {Emitter, Events} from '/imports/api/events';\n\nEmitter.on(Events.ITEM_CREATED, function({itemId}) { \n notifyInterestedPeopleInSale(itemId);\n notifyAdmins(itemId);\n})\n```\n```js\n// file: /imports/api/payments/listeners.js\nimport {Emitter, Events} from '/imports/api/events';\n\nEmitter.on(Events.ITEM_CREATED, function({itemId}) { \n processCharging(itemId);\n})\n```\n\nWhat do we exactly gain by using this strategy ?\n- You can plug-in additional functionality by listening to events\n- If item creation is done from multiple places, if you want to remove/add functionality you do it in one place\n- It's easy to find what listeners you have if you respect the pattern, simply searching for `Events.ITEM_CREATED` in your project finds you everything you need (listeners, emitters)\n- The code is independent and doesn't know about other functionality, which is the way it should be\n\n\nWatch the pattern: `MODEL_ACTION: 'model_action'`. Begin all your event names with the subject of interest, and if the event name\nalso needs to contain the one who did an action, like the admin for example: `MODEL_ACTION_BY_ADMIN`\n\nBe very careful with verb tenses, if it's present, then it's before the item creation, if it's past-tense it's after that action has been made:\n\n```js\nEmitter.emit(Events.ITEM_CREATE, {item});\nconst _id = Items.insert(item);\nEmitter.emit(Events.ITEM_CREATED, {_id, item});\n```\n\n\n\n**Bottom line**\nWhen we are doing non-related logic inside the service, just dispatch an event and hook in a listener. You are doing yourself a big favor!\n\n## Event Params\n\nWhen you emit an event, send an object as a single parameter, instead of multiple parameters or other types.\nThis gives verbosity to the code.\n\nA common mistake is the fact that when you dispatch an event, you tend to send data,\nwhich would help the listener, especially if it's your first one.\n\nFor example, you dispatch something like:\n```js\nEmitter.emit(Events.ITEM_CREATED, {\n itemPrice: X,\n})\n```\n\nBecause you want this data to be used in the payment processor. However, this is a bad way of sending events, events should be dispatched with object or objectId of their interest and other data\nrelated to the Event itself.\n\nBasically the rule is: when you dispatch events, imagine that you don't know about who is going to use them. There aren't any strict rules here.\n\nWhen item is created I want to send:\n- Item identification\n- Who added this item\n\nIt's not bad to send the full item, it's up to you, my personal preference is to send ids as much as I can, but there\nare ofcourse scenarios where I'd rather send the full `item` object, because I know a lot of listeners will need it.\n\nDoesn't matter if you do 3 additional finds in your listeners. Most of the times when you do the finds, you will use [Grapher](http://grapher.cultofcoders.com),\nand fetch only the fields you need. \n\nAren't Events just great ?\n\n## When to use\n\nYou can use them in most situations but I strongly recommend using them when:\n- You have to create notifications of any kind (push, app, emails)\n- You have a service that knows too much \n- You want to have something easily pluggable and upluggable in your system\n\n## Validation & Maintenance\n\nHow do I enforce validation for events parameters ?\n\nAgain, you have to keep things simple, but if your code-base grows a lot, this will be needed,\nespecially if events are dispatched from multiple places. You need to be sure that the events receive valid parameters.\n\nJust a simple way to architect this:\n`/imports/api/events.js` -> `/imports/api/events/index.js`\n\n```js\n// file: /imports/api/events/events.js\n\nconst Events = {\n ITEM_CREATED: {\n key: 'item_created',\n schema: {\n itemId: {type: String}\n }\n }\n};\n\nexport default Events;\n```\n\n```js\n// file: /imports/api/events/events.js\nimport SimpleSchema from 'simpl-schema';\nimport _ from 'underscore';\n\nconst Events = {\n ITEM_CREATED: {\n key: 'item_created',\n schema: {\n itemId: {type: String}\n }\n }\n};\n\n// put this in a separate module, it's for demo here\n// we create SimpleSchema validator function for each event with schema \n_.each(Events, (value, key) => {\n if (!Events[key].schema) {\n return;\n }\n \n Events[key]['validator'] = new SimpleSchema(Events[key].schema).validator();\n});\n\n// we also need the old simple Events style so we can easily use it in our app\n// so we don't have to do Events.ITEM_CREATED.key when dispatching or listening\nlet SimpleEventsMap = {};\n_.each(Events, (value, key) => {\n SimpleEventsMap[key] = value.key;\n});\n\nexport default SimpleEventsMap;\nexport {Events};\n```\n\n```js\n// file: /imports/api/events/extendForValidation.js\nimport {Events} from './events';\n\nexport default (emitter) => {\n const oldEmit = emitter.emit;\n const newEmit = function (event, data) {\n if (!Events[event]) {\n return;\n }\n \n if (Events[event].validator) {\n Events[event].validator.validate(data);\n }\n \n oldEmit(event, data);\n };\n \n emitter.emit = newEmit.bind(emitter);\n \n return emitter;\n}\n```\n\n```js\n// file: /imports/api/events/index.js\nimport Events from './events';\nimport EventEmitter from 'event-emitter';\nimport extendForValidation from './extendForValidation';\n\nconst Emitter = extendForValidation(\n new EventEmitter()\n);\n\nexport default Emitter;\n\nexport {Emitter, Events};\n```\n\n## Testing\n\nEvent listeners **must** delegate their job to services directly, they are proxies. Event Listeners should not contain any logic. Create unit-tests for services,\nand then you can run an integration test easily.\n\nIf you do want to test them, use the strategy in the [**Services - Dependency Injection**](/chapters/3/services.html#Dependency-Injection) and make your listener a class,\nand inject the services it uses.\n","source":"chapters/3/events.md","raw":"---\ntitle: 'Events'\ndescription: Observe and react to changes in a decoupled manner \ndisqusPage: 'Chapter 3: Events'\n---\n\n## The Problem\n\nLet's start with the problem. Something happens in your system, like: you add a new item for sale.\n\nWhen this happens you need to do:\n- Notify people that may be interested in your sale\n- Send an email to the Admin\n- Charge the person 0.02$ for the fact he posted a new item for sale on your platform\n\nSo far so good, you know that you need to have units of logic (services) for this, so after some coding hours (or minutes!) you come up with these functions:\n\n```js\nnotifyInterestedPeopleInSale(itemId);\nnotifyAdmins(itemId);\nprocessCharging(itemId);\n```\n\nNow you go to your `ItemService` and have something like:\n```js\nstatic createItem(data) {\n const itemId = Items.insert(data);\n \n notifyInterestedPeopleInSale(itemId);\n notifyAdmins(itemId);\n processCharging(itemId);\n}\n```\n\nAnd it seems that you are happy with this. It looks modular and decoupled. However it's not ok because:\n- **It knows too much** about other services\n- **It does too much**, it's purpose is to merely create an item, that's it.\n- **It depends on too many modules**, if by any chance you decide to remove admin notifications you need to see wherever you use it and remove it.\n\nBesides that, the name we have is not very verbose, what if we change it?\n```js\ncreatePostAndNotifyAdminsAndInterestedPeopleInSaleThenProcessCharging(data);\n```\n\n## The Solution\n\nOk we can't work with something like that, name too long, and we break the single responsability principle.\nIs there any hope for us ?\nCan we have good code when we have a lot of functionality ?\n\nOfcourse, let's rock and roll with the Observer pattern. The observer pattern is so simple to understand:\n- When X happens I want to do Y\n- Ability to say X happens\n- Ability to do Y when X happens\n\nIn code translation:\n```\nmeteor npm i -S event-emitter\n```\n\nPS: In Node 8 (Meteor 1.6), it is native: https://nodejs.org/api/events.html \n\n```js\n// file: /imports/api/events.js\nimport EventEmitter from 'event-emitter';\n\nconst Emitter = new EventEmitter();\n\nconst Events = {\n ITEM_CREATED: 'item_created'\n};\n\nexport { Emitter, Events }\n```\n\nNow we need to say to the system that an item has been created:\n```js\nimport {Emitter, Events} from '/imports/api/events';\n\nfunction createItem(userId, data) {\n const itemId = Items.insert(data);\n \n Emitter.emit(Events.ITEM_CREATED, {itemId, userId});\n}\n```\n\nNow, notifications and payment are two modules that aren't necessarily related, they don't really need to know about each other.\nThis is why our listeners, should be close to their code:\n\n```js\n// file: /imports/api/notifications/listeners.js\nimport {Emitter, Events} from '/imports/api/events';\n\nEmitter.on(Events.ITEM_CREATED, function({itemId}) { \n notifyInterestedPeopleInSale(itemId);\n notifyAdmins(itemId);\n})\n```\n```js\n// file: /imports/api/payments/listeners.js\nimport {Emitter, Events} from '/imports/api/events';\n\nEmitter.on(Events.ITEM_CREATED, function({itemId}) { \n processCharging(itemId);\n})\n```\n\nWhat do we exactly gain by using this strategy ?\n- You can plug-in additional functionality by listening to events\n- If item creation is done from multiple places, if you want to remove/add functionality you do it in one place\n- It's easy to find what listeners you have if you respect the pattern, simply searching for `Events.ITEM_CREATED` in your project finds you everything you need (listeners, emitters)\n- The code is independent and doesn't know about other functionality, which is the way it should be\n\n\nWatch the pattern: `MODEL_ACTION: 'model_action'`. Begin all your event names with the subject of interest, and if the event name\nalso needs to contain the one who did an action, like the admin for example: `MODEL_ACTION_BY_ADMIN`\n\nBe very careful with verb tenses, if it's present, then it's before the item creation, if it's past-tense it's after that action has been made:\n\n```js\nEmitter.emit(Events.ITEM_CREATE, {item});\nconst _id = Items.insert(item);\nEmitter.emit(Events.ITEM_CREATED, {_id, item});\n```\n\n\n\n**Bottom line**\nWhen we are doing non-related logic inside the service, just dispatch an event and hook in a listener. You are doing yourself a big favor!\n\n## Event Params\n\nWhen you emit an event, send an object as a single parameter, instead of multiple parameters or other types.\nThis gives verbosity to the code.\n\nA common mistake is the fact that when you dispatch an event, you tend to send data,\nwhich would help the listener, especially if it's your first one.\n\nFor example, you dispatch something like:\n```js\nEmitter.emit(Events.ITEM_CREATED, {\n itemPrice: X,\n})\n```\n\nBecause you want this data to be used in the payment processor. However, this is a bad way of sending events, events should be dispatched with object or objectId of their interest and other data\nrelated to the Event itself.\n\nBasically the rule is: when you dispatch events, imagine that you don't know about who is going to use them. There aren't any strict rules here.\n\nWhen item is created I want to send:\n- Item identification\n- Who added this item\n\nIt's not bad to send the full item, it's up to you, my personal preference is to send ids as much as I can, but there\nare ofcourse scenarios where I'd rather send the full `item` object, because I know a lot of listeners will need it.\n\nDoesn't matter if you do 3 additional finds in your listeners. Most of the times when you do the finds, you will use [Grapher](http://grapher.cultofcoders.com),\nand fetch only the fields you need. \n\nAren't Events just great ?\n\n## When to use\n\nYou can use them in most situations but I strongly recommend using them when:\n- You have to create notifications of any kind (push, app, emails)\n- You have a service that knows too much \n- You want to have something easily pluggable and upluggable in your system\n\n## Validation & Maintenance\n\nHow do I enforce validation for events parameters ?\n\nAgain, you have to keep things simple, but if your code-base grows a lot, this will be needed,\nespecially if events are dispatched from multiple places. You need to be sure that the events receive valid parameters.\n\nJust a simple way to architect this:\n`/imports/api/events.js` -> `/imports/api/events/index.js`\n\n```js\n// file: /imports/api/events/events.js\n\nconst Events = {\n ITEM_CREATED: {\n key: 'item_created',\n schema: {\n itemId: {type: String}\n }\n }\n};\n\nexport default Events;\n```\n\n```js\n// file: /imports/api/events/events.js\nimport SimpleSchema from 'simpl-schema';\nimport _ from 'underscore';\n\nconst Events = {\n ITEM_CREATED: {\n key: 'item_created',\n schema: {\n itemId: {type: String}\n }\n }\n};\n\n// put this in a separate module, it's for demo here\n// we create SimpleSchema validator function for each event with schema \n_.each(Events, (value, key) => {\n if (!Events[key].schema) {\n return;\n }\n \n Events[key]['validator'] = new SimpleSchema(Events[key].schema).validator();\n});\n\n// we also need the old simple Events style so we can easily use it in our app\n// so we don't have to do Events.ITEM_CREATED.key when dispatching or listening\nlet SimpleEventsMap = {};\n_.each(Events, (value, key) => {\n SimpleEventsMap[key] = value.key;\n});\n\nexport default SimpleEventsMap;\nexport {Events};\n```\n\n```js\n// file: /imports/api/events/extendForValidation.js\nimport {Events} from './events';\n\nexport default (emitter) => {\n const oldEmit = emitter.emit;\n const newEmit = function (event, data) {\n if (!Events[event]) {\n return;\n }\n \n if (Events[event].validator) {\n Events[event].validator.validate(data);\n }\n \n oldEmit(event, data);\n };\n \n emitter.emit = newEmit.bind(emitter);\n \n return emitter;\n}\n```\n\n```js\n// file: /imports/api/events/index.js\nimport Events from './events';\nimport EventEmitter from 'event-emitter';\nimport extendForValidation from './extendForValidation';\n\nconst Emitter = extendForValidation(\n new EventEmitter()\n);\n\nexport default Emitter;\n\nexport {Emitter, Events};\n```\n\n## Testing\n\nEvent listeners **must** delegate their job to services directly, they are proxies. Event Listeners should not contain any logic. Create unit-tests for services,\nand then you can run an integration test easily.\n\nIf you do want to test them, use the strategy in the [**Services - Dependency Injection**](/chapters/3/services.html#Dependency-Injection) and make your listener a class,\nand inject the services it uses.\n","date":"2017-11-10T12:45:17.013Z","updated":"2017-11-10T12:45:17.013Z","path":"chapters/3/events.html","comments":1,"layout":"page","_id":"cj9twby7y00029ajxr3fb3m7a","content":"Let’s start with the problem. Something happens in your system, like: you add a new item for sale.
\nWhen this happens you need to do:
\nSo far so good, you know that you need to have units of logic (services) for this, so after some coding hours (or minutes!) you come up with these functions:
\n
|
|
Now you go to your ItemService
and have something like:\n
And it seems that you are happy with this. It looks modular and decoupled. However it’s not ok because:
\nBesides that, the name we have is not very verbose, what if we change it?\n
Ok we can’t work with something like that, name too long, and we break the single responsability principle.\nIs there any hope for us ?\nCan we have good code when we have a lot of functionality ?
\nOfcourse, let’s rock and roll with the Observer pattern. The observer pattern is so simple to understand:
\nIn code translation:\n
PS: In Node 8 (Meteor 1.6), it is native: https://nodejs.org/api/events.html
\n
|
|
Now we need to say to the system that an item has been created:\n
Now, notifications and payment are two modules that aren’t necessarily related, they don’t really need to know about each other.\nThis is why our listeners, should be close to their code:
\n
|
|
|
|
What do we exactly gain by using this strategy ?
\nEvents.ITEM_CREATED
in your project finds you everything you need (listeners, emitters)Watch the pattern: MODEL_ACTION: 'model_action'
. Begin all your event names with the subject of interest, and if the event name\nalso needs to contain the one who did an action, like the admin for example: MODEL_ACTION_BY_ADMIN
Be very careful with verb tenses, if it’s present, then it’s before the item creation, if it’s past-tense it’s after that action has been made:
\n
|
|
Bottom line\nWhen we are doing non-related logic inside the service, just dispatch an event and hook in a listener. You are doing yourself a big favor!
\nWhen you emit an event, send an object as a single parameter, instead of multiple parameters or other types.\nThis gives verbosity to the code.
\nA common mistake is the fact that when you dispatch an event, you tend to send data,\nwhich would help the listener, especially if it’s your first one.
\nFor example, you dispatch something like:\n
Because you want this data to be used in the payment processor. However, this is a bad way of sending events, events should be dispatched with object or objectId of their interest and other data\nrelated to the Event itself.
\nBasically the rule is: when you dispatch events, imagine that you don’t know about who is going to use them. There aren’t any strict rules here.
\nWhen item is created I want to send:
\nIt’s not bad to send the full item, it’s up to you, my personal preference is to send ids as much as I can, but there\nare ofcourse scenarios where I’d rather send the full item
object, because I know a lot of listeners will need it.
Doesn’t matter if you do 3 additional finds in your listeners. Most of the times when you do the finds, you will use Grapher,\nand fetch only the fields you need.
\nAren’t Events just great ?
\nYou can use them in most situations but I strongly recommend using them when:
\nHow do I enforce validation for events parameters ?
\nAgain, you have to keep things simple, but if your code-base grows a lot, this will be needed,\nespecially if events are dispatched from multiple places. You need to be sure that the events receive valid parameters.
\nJust a simple way to architect this:\n/imports/api/events.js
-> /imports/api/events/index.js
|
|
|
|
|
|
|
|
Event listeners must delegate their job to services directly, they are proxies. Event Listeners should not contain any logic. Create unit-tests for services,\nand then you can run an integration test easily.
\nIf you do want to test them, use the strategy in the Services - Dependency Injection and make your listener a class,\nand inject the services it uses.
\n","site":{"data":{}},"excerpt":"","more":"Let’s start with the problem. Something happens in your system, like: you add a new item for sale.
\nWhen this happens you need to do:
\nSo far so good, you know that you need to have units of logic (services) for this, so after some coding hours (or minutes!) you come up with these functions:
\n
|
|
Now you go to your ItemService
and have something like:\n
And it seems that you are happy with this. It looks modular and decoupled. However it’s not ok because:
\nBesides that, the name we have is not very verbose, what if we change it?\n
Ok we can’t work with something like that, name too long, and we break the single responsability principle.\nIs there any hope for us ?\nCan we have good code when we have a lot of functionality ?
\nOfcourse, let’s rock and roll with the Observer pattern. The observer pattern is so simple to understand:
\nIn code translation:\n
PS: In Node 8 (Meteor 1.6), it is native: https://nodejs.org/api/events.html
\n
|
|
Now we need to say to the system that an item has been created:\n
Now, notifications and payment are two modules that aren’t necessarily related, they don’t really need to know about each other.\nThis is why our listeners, should be close to their code:
\n
|
|
|
|
What do we exactly gain by using this strategy ?
\nEvents.ITEM_CREATED
in your project finds you everything you need (listeners, emitters)Watch the pattern: MODEL_ACTION: 'model_action'
. Begin all your event names with the subject of interest, and if the event name\nalso needs to contain the one who did an action, like the admin for example: MODEL_ACTION_BY_ADMIN
Be very careful with verb tenses, if it’s present, then it’s before the item creation, if it’s past-tense it’s after that action has been made:
\n
|
|
Bottom line\nWhen we are doing non-related logic inside the service, just dispatch an event and hook in a listener. You are doing yourself a big favor!
\nWhen you emit an event, send an object as a single parameter, instead of multiple parameters or other types.\nThis gives verbosity to the code.
\nA common mistake is the fact that when you dispatch an event, you tend to send data,\nwhich would help the listener, especially if it’s your first one.
\nFor example, you dispatch something like:\n
Because you want this data to be used in the payment processor. However, this is a bad way of sending events, events should be dispatched with object or objectId of their interest and other data\nrelated to the Event itself.
\nBasically the rule is: when you dispatch events, imagine that you don’t know about who is going to use them. There aren’t any strict rules here.
\nWhen item is created I want to send:
\nIt’s not bad to send the full item, it’s up to you, my personal preference is to send ids as much as I can, but there\nare ofcourse scenarios where I’d rather send the full item
object, because I know a lot of listeners will need it.
Doesn’t matter if you do 3 additional finds in your listeners. Most of the times when you do the finds, you will use Grapher,\nand fetch only the fields you need.
\nAren’t Events just great ?
\nYou can use them in most situations but I strongly recommend using them when:
\nHow do I enforce validation for events parameters ?
\nAgain, you have to keep things simple, but if your code-base grows a lot, this will be needed,\nespecially if events are dispatched from multiple places. You need to be sure that the events receive valid parameters.
\nJust a simple way to architect this:\n/imports/api/events.js
-> /imports/api/events/index.js
|
|
|
|
|
|
|
|
Event listeners must delegate their job to services directly, they are proxies. Event Listeners should not contain any logic. Create unit-tests for services,\nand then you can run an integration test easily.
\nIf you do want to test them, use the strategy in the Services - Dependency Injection and make your listener a class,\nand inject the services it uses.
\n"},{"title":"Linting","description":"Keeping your code standards high","disqusPage":"Chapter 3: Linting","_content":"\n## Why?\n\nBecause it takes you 2 minutes to set up, and the code quality respects the standards. Also you make it easier for code-reviewers, to spend time\non thinking about what you did, rather than your coding standards mistakes.\n\nThe thing is we need to automate this process, so we need to run the linting *before we commit*.\nAnd not allow a commits with bad code, this will force you to write beautiful code.\n\n## Install\n\nAlso, try integrating it with your IDE, for WebStorm look here: https://www.jetbrains.com/help/webstorm/eslint.html \n\n```js\nmeteor npm i --save-dev babel-eslint eslint eslint-plugin-import eslint-plugin-meteor eslint-plugin-react eslint-import-resolver-meteor lint-staged pre-commit\n```\n\n## Config \n\nThis is an opionated configuration.\n\nCreate `.eslintrc.js` file inside your project root:\n```\nmodule.exports = {\n \"parser\": \"babel-eslint\",\n \"parserOptions\": {\n \"allowImportExportEverywhere\": true,\n \"ecmaFeatures\": {\n \"jsx\": true\n }\n },\n \"env\": {\n \"es6\": true,\n \"browser\": true,\n \"node\": true,\n },\n \"plugins\": [\n \"meteor\",\n \"react\"\n ],\n \"extends\": [\"eslint:recommended\", \"plugin:meteor/recommended\", \"plugin:react/recommended\"],\n \"settings\": {\n \"import/resolver\": \"meteor\"\n },\n \"rules\": {\n \"react/jsx-filename-extension\": [1, {\n \"extensions\": [\".jsx\"]\n }],\n \"react/jsx-no-bind\": [2, {\n \"ignoreRefs\": false,\n \"allowArrowFunctions\": false,\n \"allowFunctions\": false,\n \"allowBind\": false\n }],\n \"max-len\": [0, {code: 100}],\n \"import/no-absolute-path\": [0],\n \"meteor/audit-argument-checks\": [0],\n \"indent\": [\"error\", 4],\n \"switch-colon-spacing\": [0],\n \"no-invalid-this\": [0],\n \"new-cap\": [1],\n \"no-trailing-spaces\": [2, {\n skipBlankLines: true\n }],\n },\n \"overrides\": {\n files: \"*.js,*.jsx\",\n }\n};\n```\n\nIn your `package.json` file add the following:\n\n```\n{\n ...\n \"scripts\": {\n \"start\": \"meteor run --settings settings.json\",\n \"test\": \"meteor test --driver-package practicalmeteor:mocha --settings settings.json\",\n \"lint\": \"eslint . --ext .jsx --fix\"\n },\n \"lint-staged\": {\n \"*.js\": \"eslint --fix\",\n \"*.jsx\": \"eslint --fix\"\n },\n \"pre-commit\": \"lint-staged\"\n}\n```\n\nNice, now if you want to commit, it will try to lint your code first. And if any errors happen, it will not allow you to do so.\n\nIf by any chance you are in a rush and you have a production bug and the whole world is burning! Try running:\n```\ngit commit -m \"xxx\" -n\n```\n\nUsing `-n` will bypass the linting and it will allow you to commit directly.","source":"chapters/3/linting.md","raw":"---\ntitle: 'Linting'\ndescription: Keeping your code standards high\ndisqusPage: 'Chapter 3: Linting'\n---\n\n## Why?\n\nBecause it takes you 2 minutes to set up, and the code quality respects the standards. Also you make it easier for code-reviewers, to spend time\non thinking about what you did, rather than your coding standards mistakes.\n\nThe thing is we need to automate this process, so we need to run the linting *before we commit*.\nAnd not allow a commits with bad code, this will force you to write beautiful code.\n\n## Install\n\nAlso, try integrating it with your IDE, for WebStorm look here: https://www.jetbrains.com/help/webstorm/eslint.html \n\n```js\nmeteor npm i --save-dev babel-eslint eslint eslint-plugin-import eslint-plugin-meteor eslint-plugin-react eslint-import-resolver-meteor lint-staged pre-commit\n```\n\n## Config \n\nThis is an opionated configuration.\n\nCreate `.eslintrc.js` file inside your project root:\n```\nmodule.exports = {\n \"parser\": \"babel-eslint\",\n \"parserOptions\": {\n \"allowImportExportEverywhere\": true,\n \"ecmaFeatures\": {\n \"jsx\": true\n }\n },\n \"env\": {\n \"es6\": true,\n \"browser\": true,\n \"node\": true,\n },\n \"plugins\": [\n \"meteor\",\n \"react\"\n ],\n \"extends\": [\"eslint:recommended\", \"plugin:meteor/recommended\", \"plugin:react/recommended\"],\n \"settings\": {\n \"import/resolver\": \"meteor\"\n },\n \"rules\": {\n \"react/jsx-filename-extension\": [1, {\n \"extensions\": [\".jsx\"]\n }],\n \"react/jsx-no-bind\": [2, {\n \"ignoreRefs\": false,\n \"allowArrowFunctions\": false,\n \"allowFunctions\": false,\n \"allowBind\": false\n }],\n \"max-len\": [0, {code: 100}],\n \"import/no-absolute-path\": [0],\n \"meteor/audit-argument-checks\": [0],\n \"indent\": [\"error\", 4],\n \"switch-colon-spacing\": [0],\n \"no-invalid-this\": [0],\n \"new-cap\": [1],\n \"no-trailing-spaces\": [2, {\n skipBlankLines: true\n }],\n },\n \"overrides\": {\n files: \"*.js,*.jsx\",\n }\n};\n```\n\nIn your `package.json` file add the following:\n\n```\n{\n ...\n \"scripts\": {\n \"start\": \"meteor run --settings settings.json\",\n \"test\": \"meteor test --driver-package practicalmeteor:mocha --settings settings.json\",\n \"lint\": \"eslint . --ext .jsx --fix\"\n },\n \"lint-staged\": {\n \"*.js\": \"eslint --fix\",\n \"*.jsx\": \"eslint --fix\"\n },\n \"pre-commit\": \"lint-staged\"\n}\n```\n\nNice, now if you want to commit, it will try to lint your code first. And if any errors happen, it will not allow you to do so.\n\nIf by any chance you are in a rush and you have a production bug and the whole world is burning! Try running:\n```\ngit commit -m \"xxx\" -n\n```\n\nUsing `-n` will bypass the linting and it will allow you to commit directly.","date":"2018-03-05T20:41:32.068Z","updated":"2018-03-05T20:41:32.068Z","path":"chapters/3/linting.html","_id":"cj9twby7z00039ajx69ms9km1","comments":1,"layout":"page","content":"Because it takes you 2 minutes to set up, and the code quality respects the standards. Also you make it easier for code-reviewers, to spend time\non thinking about what you did, rather than your coding standards mistakes.
\nThe thing is we need to automate this process, so we need to run the linting before we commit.\nAnd not allow a commits with bad code, this will force you to write beautiful code.
\nAlso, try integrating it with your IDE, for WebStorm look here: https://www.jetbrains.com/help/webstorm/eslint.html
\n1 | meteor npm i --save-dev babel-eslint eslint eslint-plugin-import eslint-plugin-meteor eslint-plugin-react eslint-import-resolver-meteor lint-staged pre-commit |
This is an opionated configuration.
\nCreate .eslintrc.js
file inside your project root:\n1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46module.exports = {
\"parser\": \"babel-eslint\",
\"parserOptions\": {
\"allowImportExportEverywhere\": true,
\"ecmaFeatures\": {
\"jsx\": true
}
},
\"env\": {
\"es6\": true,
\"browser\": true,
\"node\": true,
},
\"plugins\": [
\"meteor\",
\"react\"
],
\"extends\": [\"eslint:recommended\", \"plugin:meteor/recommended\", \"plugin:react/recommended\"],
\"settings\": {
\"import/resolver\": \"meteor\"
},
\"rules\": {
\"react/jsx-filename-extension\": [1, {
\"extensions\": [\".jsx\"]
}],
\"react/jsx-no-bind\": [2, {
\"ignoreRefs\": false,
\"allowArrowFunctions\": false,
\"allowFunctions\": false,
\"allowBind\": false
}],
\"max-len\": [0, {code: 100}],
\"import/no-absolute-path\": [0],
\"meteor/audit-argument-checks\": [0],
\"indent\": [\"error\", 4],
\"switch-colon-spacing\": [0],
\"no-invalid-this\": [0],
\"new-cap\": [1],
\"no-trailing-spaces\": [2, {
skipBlankLines: true
}],
},
\"overrides\": {
files: \"*.js,*.jsx\",
}
};
In your package.json
file add the following:
1 | { |
Nice, now if you want to commit, it will try to lint your code first. And if any errors happen, it will not allow you to do so.
\nIf by any chance you are in a rush and you have a production bug and the whole world is burning! Try running:\n1
git commit -m \"xxx\" -n
Using -n
will bypass the linting and it will allow you to commit directly.
Because it takes you 2 minutes to set up, and the code quality respects the standards. Also you make it easier for code-reviewers, to spend time\non thinking about what you did, rather than your coding standards mistakes.
\nThe thing is we need to automate this process, so we need to run the linting before we commit.\nAnd not allow a commits with bad code, this will force you to write beautiful code.
\nAlso, try integrating it with your IDE, for WebStorm look here: https://www.jetbrains.com/help/webstorm/eslint.html
\n1 | meteor npm i --save-dev babel-eslint eslint eslint-plugin-import eslint-plugin-meteor eslint-plugin-react eslint-import-resolver-meteor lint-staged pre-commit |
This is an opionated configuration.
\nCreate .eslintrc.js
file inside your project root:\n1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46module.exports = {
\"parser\": \"babel-eslint\",
\"parserOptions\": {
\"allowImportExportEverywhere\": true,
\"ecmaFeatures\": {
\"jsx\": true
}
},
\"env\": {
\"es6\": true,
\"browser\": true,
\"node\": true,
},
\"plugins\": [
\"meteor\",
\"react\"
],
\"extends\": [\"eslint:recommended\", \"plugin:meteor/recommended\", \"plugin:react/recommended\"],
\"settings\": {
\"import/resolver\": \"meteor\"
},
\"rules\": {
\"react/jsx-filename-extension\": [1, {
\"extensions\": [\".jsx\"]
}],
\"react/jsx-no-bind\": [2, {
\"ignoreRefs\": false,
\"allowArrowFunctions\": false,
\"allowFunctions\": false,
\"allowBind\": false
}],
\"max-len\": [0, {code: 100}],
\"import/no-absolute-path\": [0],
\"meteor/audit-argument-checks\": [0],
\"indent\": [\"error\", 4],
\"switch-colon-spacing\": [0],
\"no-invalid-this\": [0],
\"new-cap\": [1],
\"no-trailing-spaces\": [2, {
skipBlankLines: true
}],
},
\"overrides\": {
files: \"*.js,*.jsx\",
}
};
In your package.json
file add the following:
1 | { |
Nice, now if you want to commit, it will try to lint your code first. And if any errors happen, it will not allow you to do so.
\nIf by any chance you are in a rush and you have a production bug and the whole world is burning! Try running:\n1
git commit -m \"xxx\" -n
Using -n
will bypass the linting and it will allow you to commit directly.
Usually, you tend to put logic in your Meteor methods which is a very very bad terrible thing, because Meteor methods act as server-side routes, they act as the C (Controller) in MVC, \nthe C does not contain logic, the C is a boss that delegates work to M (Services aka Models)
\nIt is also bad because methods are a communication layer between the client and the server, they shouldn’t store business logic, or logic of any kind, imagine them as proxies that communicate with your services.
\nKeep your methods inside a /server
folder, this will allow Meteor to only refresh the server, without refreshing your browser,\nif you make a change inside a method.
Take this scenario for example:
\nBy coupling server-only logic under /server
folder you will benefit a faster development speed.
So, I suggest the following design:
\n
|
|
|
|
|
|
Use {module}.{submodule[1..n]}?.{action}
\nI recommend lowercase and plural form and camelCase for multi-words. Whatever you choose, just stick to it!
Examples:
\n
|
|
Whenever you do a method call I recommend sending an object, rather than multiple arguments.
\nFor example instead of:\n
|
|
The reason is simple: easier inspection on the server and in the websocket frames. Plus, it’s easier to validate. You validate against one object.
\nIf your method is heavily used, ok, I agree use arguments because they cost less, but I highly doubt you will need this. Don’t micro-optimize.
\nA method should do the following:
\nWe can do #1 by using check
but I’d rather use SimpleSchema:\n
For #2, always use a Security service, which you either store at top level in /imports/api/security.js
or locally in posts
module folder,\nor you could use both, why not?
|
|
These security checks must throw a Meteor.Error
if the check does not pass.
For #3, you know it, we use a service.
\nSometimes you need that when you do an action which updates a local collection (meaning you observe reactive data) and\nyou have a form that does an insert, you want it to immediately appear on your view. (not wait for the publication to send it to you)
\nThe best way to do this is to use ValidatedMethod: https://github.com/meteor/validated-method
\nStore them inside: /imports/api/{module}/optimistic/methodName.js
\nImport them all in: /imports/api/{module}/optimistic/methods.js
\nAnd if you have imports/api/{module}/server/index.js
make sure to import ../optimistic
inside /imports/api/{module}/server/methods.js
In most cases you won’t use this, but it’s a very nice feature when you need to.
\nThere will be scenarios where all your methods defined need a logged in user, or a user with a certain role, it can become tedious and repetitive, that \ninside every method you do this check, you need to do it once and keep your code DRY.
\n
|
|
And where you define your methods:\n
These few lines of code can have great impact on readability and elegance of your code. Feel free to use them!
\n","site":{"data":{}},"excerpt":"","more":"Usually, you tend to put logic in your Meteor methods which is a very very bad terrible thing, because Meteor methods act as server-side routes, they act as the C (Controller) in MVC, \nthe C does not contain logic, the C is a boss that delegates work to M (Services aka Models)
\nIt is also bad because methods are a communication layer between the client and the server, they shouldn’t store business logic, or logic of any kind, imagine them as proxies that communicate with your services.
\nKeep your methods inside a /server
folder, this will allow Meteor to only refresh the server, without refreshing your browser,\nif you make a change inside a method.
Take this scenario for example:
\nBy coupling server-only logic under /server
folder you will benefit a faster development speed.
So, I suggest the following design:
\n
|
|
|
|
|
|
Use {module}.{submodule[1..n]}?.{action}
\nI recommend lowercase and plural form and camelCase for multi-words. Whatever you choose, just stick to it!
Examples:
\n
|
|
Whenever you do a method call I recommend sending an object, rather than multiple arguments.
\nFor example instead of:\n
|
|
The reason is simple: easier inspection on the server and in the websocket frames. Plus, it’s easier to validate. You validate against one object.
\nIf your method is heavily used, ok, I agree use arguments because they cost less, but I highly doubt you will need this. Don’t micro-optimize.
\nA method should do the following:
\nWe can do #1 by using check
but I’d rather use SimpleSchema:\n
For #2, always use a Security service, which you either store at top level in /imports/api/security.js
or locally in posts
module folder,\nor you could use both, why not?
|
|
These security checks must throw a Meteor.Error
if the check does not pass.
For #3, you know it, we use a service.
\nSometimes you need that when you do an action which updates a local collection (meaning you observe reactive data) and\nyou have a form that does an insert, you want it to immediately appear on your view. (not wait for the publication to send it to you)
\nThe best way to do this is to use ValidatedMethod: https://github.com/meteor/validated-method
\nStore them inside: /imports/api/{module}/optimistic/methodName.js
\nImport them all in: /imports/api/{module}/optimistic/methods.js
\nAnd if you have imports/api/{module}/server/index.js
make sure to import ../optimistic
inside /imports/api/{module}/server/methods.js
In most cases you won’t use this, but it’s a very nice feature when you need to.
\nThere will be scenarios where all your methods defined need a logged in user, or a user with a certain role, it can become tedious and repetitive, that \ninside every method you do this check, you need to do it once and keep your code DRY.
\n
|
|
And where you define your methods:\n
These few lines of code can have great impact on readability and elegance of your code. Feel free to use them!
\n"},{"title":"Fixtures","description":"Generate dummy data.","disqusPage":"Chapter 3: Fixtures","_content":"\n## Why?\n\nThe reason for writing fixtures is that once you setup your project, you fill it with relevant data, like users of different types and roles, posts, items, comments, whatever.\n\nThis removes the reason of having a shared database, and it makes onboarding new developers so easy.\n\n## Structure\n\nWe will store our fixtures inside `/imports/fixtures` because this way you will not forget about them. They are an essential part of development.\n\n```js\n// file: /imports/fixtures/index.js\nimport createUsers from './createUsers';\nimport createPostsForUsers from './createPostsForUsers';\n\nconst runFixtures = function () {\n const shouldRun = Meteor.users.find().count() == 0;\n \n if (shouldRun) {\n createUsers();\n createPostsForUsers();\n }\n}\n\nif (!Meteor.isDevelopment) {\n runFixtures();\n}\n```\n\nKeep a configuration so you can use it for load testing later on:\n\n```js\n// file: /imports/fixtures/config.js\nexport default {\n USERS_COUNT: 10,\n POSTS_PER_USER: 2,\n}\n```\n\nAnd make sure you use the config in every fixture module you've got!\n\nIf it helps you for generation you can also use: https://github.com/versolearning/meteor-factory/ \n\n## Caution\n\nIf you have hooks that send emails or do API calls make sure you disable them when running your fixtures.\n\nKeep your fixtures in sync with your schemas, so whenever you update it, make sure to update your fixtures as well.\n\nAlways work on adding fixtures as you add different data.\n\nKeep fixtures small and be careful when nesting them, you don't want to insert 100,000 documents because of a big nested loop.","source":"chapters/3/fixtures.md","raw":"---\ntitle: 'Fixtures'\ndescription: Generate dummy data. \ndisqusPage: 'Chapter 3: Fixtures'\n---\n\n## Why?\n\nThe reason for writing fixtures is that once you setup your project, you fill it with relevant data, like users of different types and roles, posts, items, comments, whatever.\n\nThis removes the reason of having a shared database, and it makes onboarding new developers so easy.\n\n## Structure\n\nWe will store our fixtures inside `/imports/fixtures` because this way you will not forget about them. They are an essential part of development.\n\n```js\n// file: /imports/fixtures/index.js\nimport createUsers from './createUsers';\nimport createPostsForUsers from './createPostsForUsers';\n\nconst runFixtures = function () {\n const shouldRun = Meteor.users.find().count() == 0;\n \n if (shouldRun) {\n createUsers();\n createPostsForUsers();\n }\n}\n\nif (!Meteor.isDevelopment) {\n runFixtures();\n}\n```\n\nKeep a configuration so you can use it for load testing later on:\n\n```js\n// file: /imports/fixtures/config.js\nexport default {\n USERS_COUNT: 10,\n POSTS_PER_USER: 2,\n}\n```\n\nAnd make sure you use the config in every fixture module you've got!\n\nIf it helps you for generation you can also use: https://github.com/versolearning/meteor-factory/ \n\n## Caution\n\nIf you have hooks that send emails or do API calls make sure you disable them when running your fixtures.\n\nKeep your fixtures in sync with your schemas, so whenever you update it, make sure to update your fixtures as well.\n\nAlways work on adding fixtures as you add different data.\n\nKeep fixtures small and be careful when nesting them, you don't want to insert 100,000 documents because of a big nested loop.","date":"2017-11-10T12:45:17.017Z","updated":"2017-11-10T12:45:17.017Z","path":"chapters/3/fixtures.html","comments":1,"layout":"page","_id":"cj9twby8000059ajx6fl24066","content":"The reason for writing fixtures is that once you setup your project, you fill it with relevant data, like users of different types and roles, posts, items, comments, whatever.
\nThis removes the reason of having a shared database, and it makes onboarding new developers so easy.
\nWe will store our fixtures inside /imports/fixtures
because this way you will not forget about them. They are an essential part of development.
|
|
Keep a configuration so you can use it for load testing later on:
\n
|
|
And make sure you use the config in every fixture module you’ve got!
\nIf it helps you for generation you can also use: https://github.com/versolearning/meteor-factory/
\nIf you have hooks that send emails or do API calls make sure you disable them when running your fixtures.
\nKeep your fixtures in sync with your schemas, so whenever you update it, make sure to update your fixtures as well.
\nAlways work on adding fixtures as you add different data.
\nKeep fixtures small and be careful when nesting them, you don’t want to insert 100,000 documents because of a big nested loop.
\n","site":{"data":{}},"excerpt":"","more":"The reason for writing fixtures is that once you setup your project, you fill it with relevant data, like users of different types and roles, posts, items, comments, whatever.
\nThis removes the reason of having a shared database, and it makes onboarding new developers so easy.
\nWe will store our fixtures inside /imports/fixtures
because this way you will not forget about them. They are an essential part of development.
|
|
Keep a configuration so you can use it for load testing later on:
\n
|
|
And make sure you use the config in every fixture module you’ve got!
\nIf it helps you for generation you can also use: https://github.com/versolearning/meteor-factory/
\nIf you have hooks that send emails or do API calls make sure you disable them when running your fixtures.
\nKeep your fixtures in sync with your schemas, so whenever you update it, make sure to update your fixtures as well.
\nAlways work on adding fixtures as you add different data.
\nKeep fixtures small and be careful when nesting them, you don’t want to insert 100,000 documents because of a big nested loop.
\n"},{"title":"Microservices","description":"Microservices - Decouple & Scale","disqusPage":"Chapter 3: Microservices","_content":"\n## What?\nThis does not relate to \"services\" term we used previously, when we regarded it as an unit of logic.\n\n`The term \"Microservice Architecture\" has sprung up over the last few years to describe a particular way of designing software applications as suites of independently deployable services.`\n\nIndepenently deployable, so basically it means that we need to have multiple meteor apps, but how to manage and allow them to re-use code ?\nIt's quite easy.\n\n\n## Why?\n\nFor example you have an app, and you have to manage the users and control the content. Doing it in the same app may be a wrong approach, either your bundle size will increase, you may\nuse modules that you don't need. And to use dynamic imports everywhere may be too much. Instead just separate the concerns, and move your admin app into a separate microservice.\n\n## Solution\n\nFirst of all, understand the fact that we can have multiple Meteor apps inside the same git repository:\n\n```js\nmkdir myapp\ngit init .\nmeteor create --bare admin\nmeteor create --bare web\n```\n\nThat's it, you began your microservice architecture. Now if you want to re-use code, we use symlinks. Sorry Windows users, for you it will not work.\n```\ncd admin/imports\nln -s ../../web/imports app\n```\n\nNow look at this example:\n```js\n// file: ./admin/imports/api/posts/server/methods.js\nimport {Meteor} from 'meteor/meteor';\nimport {Posts} from '/imports/web/imports/db';\n\nMeteor.methods({\n 'posts.approve'({postId}) {\n // ...\n }\n})\n```\n\nThat's it. If you plan on only using the db models, and not interact with other services, than just do:\n\n```\ncd admin/imports\nln -s ../../web/imports/db db\n```\n\nand use it naturally:\n\n```js\n// file: ./admin/imports/api/posts/methods.js\nimport {Meteor} from 'meteor/meteor';\nimport {Posts} from '/imports/db';\n\nMeteor.methods({\n 'posts.approve'({postId}) {\n // ...\n }\n})\n```\n\nSo nice!\nNote that if you use WebStorm, make sure to mark both \"admin\" and \"web\" folders as Resource Root (By right clicking on the folder), so you have autocomplete on absolute imports properly.\n\n## Local Database\n\nIn order for this to work nicely, you would have to configure them to open on different ports and connect to the same database.\n\nNote that when you start a meteor app, it opens it's own mongodb on PORT+1 (3001 in most cases).\n\nBut if we want the two microservices to work together, either install a docker mongodb locally, either [install mongodb directly](https://docs.mongodb.com/manual/installation/).\n\n```js\n// file: app/package.json\n \"scripts\": {\n \"start\": \"MONGO_URL=mongodb://localhost:27017/web meteor run --port 3000\",\n },\n```\n```js\n// file: admin/package.json\n \"scripts\": {\n \"start\": \"MONGO_URL=mongodb://localhost:27017/web meteor run --port 3050\",\n },\n```\n\nNow you can start them both:\n```\ncd admin\nnpm start\n```\n\n```\ncd web\nnpm start\n```\n\n## Deployment\n\nEach individual microservice will have it's own `.deploy` folder inside it with the proper configuration of deployment, depending on which tool you use to do so.\nThe production bundle will be properly generated.\n\n## A better strategy\n\nYou will most likely head into this issue, especially if you decide to microservicify your already existing app:\n\nFor example:\n```js\n// file: /web/imports/api/posts/services/PostService.js\nimport PaymentService from '/imports/api/payments/services/PaymentService.js';\n// ...\n```\n\nThis will fail if you want to use the PostService inside `admin` microservice! Because doesn't have that file in that path. \n\nThe initial solution would be to use `imports PaymentService from '../../payments/services/PaymentService'`\n\nAnother great solution is to begin thinking about it as a [reusable module](/chapters/3/reusable-modules.html). Which you can store in `/modules` folder directly.\nAnd create the following symlinks:\n```bash\n/web/imports/modules -- /modules\n/admin/imports/modules -- /modules\n```\n\nIf your module depends on things outside it, simply create a config inside it, and inject what you need inside it, keep it simple or make it as complex as you wish.\n```js\n// file: /modules/chat/config.js\nexport default {}\n```\n\nBut there will be cases where you can't make everything completely independent, as they interact with many other system components,\nthis would require a change in strategy:\n\nWe have our `App Logic Layer` our `Persistence Layer` and our `Event System`\n\nLet's work with the following architecture:\n\n```bash\n/core/services\n/core/events.js\n/core/security.js\n/core/db\n```\n\nNow let's symlink it:\n```bash\n/web/imports/core -- /core\n/admin/imports/core -- /core\n```\n\nSample:\n```js\n// file: /web/imports/api/posts/server/methods.js\n\nimport PostService from '/imports/core/services/posts/PostService.js';\n\nMeteor.methods({})\n```\n\nI personally don't recommend you using absolute imports any longer inside `/core`, even though you can do it if it starts with `/imports/core`.\n\n## Conclusion\n\nThis \"better\" strategy would require you to do some refactoring to ensure it works properly provided you have an existing app already. However, if you don't and you know \nyou will have microservices, start with this approach right from the beginning.\n\n \n \n\n\n","source":"chapters/3/microservices.md","raw":"---\ntitle: 'Microservices'\ndescription: Microservices - Decouple & Scale\ndisqusPage: 'Chapter 3: Microservices'\n---\n\n## What?\nThis does not relate to \"services\" term we used previously, when we regarded it as an unit of logic.\n\n`The term \"Microservice Architecture\" has sprung up over the last few years to describe a particular way of designing software applications as suites of independently deployable services.`\n\nIndepenently deployable, so basically it means that we need to have multiple meteor apps, but how to manage and allow them to re-use code ?\nIt's quite easy.\n\n\n## Why?\n\nFor example you have an app, and you have to manage the users and control the content. Doing it in the same app may be a wrong approach, either your bundle size will increase, you may\nuse modules that you don't need. And to use dynamic imports everywhere may be too much. Instead just separate the concerns, and move your admin app into a separate microservice.\n\n## Solution\n\nFirst of all, understand the fact that we can have multiple Meteor apps inside the same git repository:\n\n```js\nmkdir myapp\ngit init .\nmeteor create --bare admin\nmeteor create --bare web\n```\n\nThat's it, you began your microservice architecture. Now if you want to re-use code, we use symlinks. Sorry Windows users, for you it will not work.\n```\ncd admin/imports\nln -s ../../web/imports app\n```\n\nNow look at this example:\n```js\n// file: ./admin/imports/api/posts/server/methods.js\nimport {Meteor} from 'meteor/meteor';\nimport {Posts} from '/imports/web/imports/db';\n\nMeteor.methods({\n 'posts.approve'({postId}) {\n // ...\n }\n})\n```\n\nThat's it. If you plan on only using the db models, and not interact with other services, than just do:\n\n```\ncd admin/imports\nln -s ../../web/imports/db db\n```\n\nand use it naturally:\n\n```js\n// file: ./admin/imports/api/posts/methods.js\nimport {Meteor} from 'meteor/meteor';\nimport {Posts} from '/imports/db';\n\nMeteor.methods({\n 'posts.approve'({postId}) {\n // ...\n }\n})\n```\n\nSo nice!\nNote that if you use WebStorm, make sure to mark both \"admin\" and \"web\" folders as Resource Root (By right clicking on the folder), so you have autocomplete on absolute imports properly.\n\n## Local Database\n\nIn order for this to work nicely, you would have to configure them to open on different ports and connect to the same database.\n\nNote that when you start a meteor app, it opens it's own mongodb on PORT+1 (3001 in most cases).\n\nBut if we want the two microservices to work together, either install a docker mongodb locally, either [install mongodb directly](https://docs.mongodb.com/manual/installation/).\n\n```js\n// file: app/package.json\n \"scripts\": {\n \"start\": \"MONGO_URL=mongodb://localhost:27017/web meteor run --port 3000\",\n },\n```\n```js\n// file: admin/package.json\n \"scripts\": {\n \"start\": \"MONGO_URL=mongodb://localhost:27017/web meteor run --port 3050\",\n },\n```\n\nNow you can start them both:\n```\ncd admin\nnpm start\n```\n\n```\ncd web\nnpm start\n```\n\n## Deployment\n\nEach individual microservice will have it's own `.deploy` folder inside it with the proper configuration of deployment, depending on which tool you use to do so.\nThe production bundle will be properly generated.\n\n## A better strategy\n\nYou will most likely head into this issue, especially if you decide to microservicify your already existing app:\n\nFor example:\n```js\n// file: /web/imports/api/posts/services/PostService.js\nimport PaymentService from '/imports/api/payments/services/PaymentService.js';\n// ...\n```\n\nThis will fail if you want to use the PostService inside `admin` microservice! Because doesn't have that file in that path. \n\nThe initial solution would be to use `imports PaymentService from '../../payments/services/PaymentService'`\n\nAnother great solution is to begin thinking about it as a [reusable module](/chapters/3/reusable-modules.html). Which you can store in `/modules` folder directly.\nAnd create the following symlinks:\n```bash\n/web/imports/modules -- /modules\n/admin/imports/modules -- /modules\n```\n\nIf your module depends on things outside it, simply create a config inside it, and inject what you need inside it, keep it simple or make it as complex as you wish.\n```js\n// file: /modules/chat/config.js\nexport default {}\n```\n\nBut there will be cases where you can't make everything completely independent, as they interact with many other system components,\nthis would require a change in strategy:\n\nWe have our `App Logic Layer` our `Persistence Layer` and our `Event System`\n\nLet's work with the following architecture:\n\n```bash\n/core/services\n/core/events.js\n/core/security.js\n/core/db\n```\n\nNow let's symlink it:\n```bash\n/web/imports/core -- /core\n/admin/imports/core -- /core\n```\n\nSample:\n```js\n// file: /web/imports/api/posts/server/methods.js\n\nimport PostService from '/imports/core/services/posts/PostService.js';\n\nMeteor.methods({})\n```\n\nI personally don't recommend you using absolute imports any longer inside `/core`, even though you can do it if it starts with `/imports/core`.\n\n## Conclusion\n\nThis \"better\" strategy would require you to do some refactoring to ensure it works properly provided you have an existing app already. However, if you don't and you know \nyou will have microservices, start with this approach right from the beginning.\n\n \n \n\n\n","date":"2017-11-10T12:45:17.017Z","updated":"2017-11-10T12:45:17.017Z","path":"chapters/3/microservices.html","comments":1,"layout":"page","_id":"cj9twby8000069ajxz3pixrrm","content":"This does not relate to “services” term we used previously, when we regarded it as an unit of logic.
\nThe term "Microservice Architecture" has sprung up over the last few years to describe a particular way of designing software applications as suites of independently deployable services.
Indepenently deployable, so basically it means that we need to have multiple meteor apps, but how to manage and allow them to re-use code ?\nIt’s quite easy.
\nFor example you have an app, and you have to manage the users and control the content. Doing it in the same app may be a wrong approach, either your bundle size will increase, you may\nuse modules that you don’t need. And to use dynamic imports everywhere may be too much. Instead just separate the concerns, and move your admin app into a separate microservice.
\nFirst of all, understand the fact that we can have multiple Meteor apps inside the same git repository:
\n
|
|
That’s it, you began your microservice architecture. Now if you want to re-use code, we use symlinks. Sorry Windows users, for you it will not work.\n
Now look at this example:\n
That’s it. If you plan on only using the db models, and not interact with other services, than just do:
\n
|
|
and use it naturally:
\n
|
|
So nice!\nNote that if you use WebStorm, make sure to mark both “admin” and “web” folders as Resource Root (By right clicking on the folder), so you have autocomplete on absolute imports properly.
\nIn order for this to work nicely, you would have to configure them to open on different ports and connect to the same database.
\nNote that when you start a meteor app, it opens it’s own mongodb on PORT+1 (3001 in most cases).
\nBut if we want the two microservices to work together, either install a docker mongodb locally, either install mongodb directly.
\n
|
|
|
|
Now you can start them both:\n
|
|
Each individual microservice will have it’s own .deploy
folder inside it with the proper configuration of deployment, depending on which tool you use to do so.\nThe production bundle will be properly generated.
You will most likely head into this issue, especially if you decide to microservicify your already existing app:
\nFor example:\n
This will fail if you want to use the PostService inside admin
microservice! Because doesn’t have that file in that path.
The initial solution would be to use imports PaymentService from '../../payments/services/PaymentService'
Another great solution is to begin thinking about it as a reusable module. Which you can store in /modules
folder directly.\nAnd create the following symlinks:\n
If your module depends on things outside it, simply create a config inside it, and inject what you need inside it, keep it simple or make it as complex as you wish.\n
But there will be cases where you can’t make everything completely independent, as they interact with many other system components,\nthis would require a change in strategy:
\nWe have our App Logic Layer
our Persistence Layer
and our Event System
Let’s work with the following architecture:
\n
|
|
Now let’s symlink it:\n
Sample:\n
I personally don’t recommend you using absolute imports any longer inside /core
, even though you can do it if it starts with /imports/core
.
This “better” strategy would require you to do some refactoring to ensure it works properly provided you have an existing app already. However, if you don’t and you know \nyou will have microservices, start with this approach right from the beginning.
\n","site":{"data":{}},"excerpt":"","more":"This does not relate to “services” term we used previously, when we regarded it as an unit of logic.
\nThe term "Microservice Architecture" has sprung up over the last few years to describe a particular way of designing software applications as suites of independently deployable services.
Indepenently deployable, so basically it means that we need to have multiple meteor apps, but how to manage and allow them to re-use code ?\nIt’s quite easy.
\nFor example you have an app, and you have to manage the users and control the content. Doing it in the same app may be a wrong approach, either your bundle size will increase, you may\nuse modules that you don’t need. And to use dynamic imports everywhere may be too much. Instead just separate the concerns, and move your admin app into a separate microservice.
\nFirst of all, understand the fact that we can have multiple Meteor apps inside the same git repository:
\n
|
|
That’s it, you began your microservice architecture. Now if you want to re-use code, we use symlinks. Sorry Windows users, for you it will not work.\n
Now look at this example:\n
That’s it. If you plan on only using the db models, and not interact with other services, than just do:
\n
|
|
and use it naturally:
\n
|
|
So nice!\nNote that if you use WebStorm, make sure to mark both “admin” and “web” folders as Resource Root (By right clicking on the folder), so you have autocomplete on absolute imports properly.
\nIn order for this to work nicely, you would have to configure them to open on different ports and connect to the same database.
\nNote that when you start a meteor app, it opens it’s own mongodb on PORT+1 (3001 in most cases).
\nBut if we want the two microservices to work together, either install a docker mongodb locally, either install mongodb directly.
\n
|
|
|
|
Now you can start them both:\n
|
|
Each individual microservice will have it’s own .deploy
folder inside it with the proper configuration of deployment, depending on which tool you use to do so.\nThe production bundle will be properly generated.
You will most likely head into this issue, especially if you decide to microservicify your already existing app:
\nFor example:\n
This will fail if you want to use the PostService inside admin
microservice! Because doesn’t have that file in that path.
The initial solution would be to use imports PaymentService from '../../payments/services/PaymentService'
Another great solution is to begin thinking about it as a reusable module. Which you can store in /modules
folder directly.\nAnd create the following symlinks:\n
If your module depends on things outside it, simply create a config inside it, and inject what you need inside it, keep it simple or make it as complex as you wish.\n
But there will be cases where you can’t make everything completely independent, as they interact with many other system components,\nthis would require a change in strategy:
\nWe have our App Logic Layer
our Persistence Layer
and our Event System
Let’s work with the following architecture:
\n
|
|
Now let’s symlink it:\n
Sample:\n
I personally don’t recommend you using absolute imports any longer inside /core
, even though you can do it if it starts with /imports/core
.
This “better” strategy would require you to do some refactoring to ensure it works properly provided you have an existing app already. However, if you don’t and you know \nyou will have microservices, start with this approach right from the beginning.
\n"},{"title":"Persistence Layer","description":"Using collections the right way","disqusPage":"Chapter 3: Persistence Layer","_content":"\n## What is it ?\n\nThe Persistence Layer (PL) is that \"thingie\" in your app that communicates with another API to store the data we want.\nIn our case, we communicate with MongoDB for that. Therefore, an instance of `Mongo.Collection` is our API to communicate with the Persistence Layer, therefore in our app we can regard collections as the PL.\n\nWhat if at some point in the future, you decide that a certain\ncollection should not be kept in MongoDB but rather used via an external HTTP API? \n\nYour code architecture should try to be as independent as possible from the PL, because this opens the path for scaling and decoupling later on.\n\nJust try and think this way, and whenever you want to start a new project: disregard the way you are going to store the data, focus more on the app/business logic layer.\n\nThen build your database on top of it.\n\n## Structure\n\nSince we want to explicitly separate it, I propose that instead of storing collections inside `/imports/api`, we give them their own folder `/imports/db`\n\n`/imports/api/posts/collection.js` -> `/imports/db/posts/collection.js`\n\n\n```js\n// file: /imports/db/posts/collection.js\nimport {Mongo} from 'meteor/mongo';\n\nconst Posts = new Mongo.Collection('posts');\n\nexport default Posts;\n```\n\nNow let's assume you have a new collection for comments on posts, and it's used only for posts. What do we do ?\n\nShould we store it in `/imports/db/posts/comments/collection.js` or in `/imports/db/postComments/collection.js` ?\n\nIt's a matter of preference here. I prefer the first approach, but the collection's name I use is `PostComments` not `Comments`, so\nwe know it's context. But this only applies if my collection is coupled in a way to posts, if not, then go with second approach.\n\n## Validating Documents\n\nWe need to be able to validate easily the data we save in a collection to prevent bad things from happening.\n\nFor this we are going to use [`aldeed:collection2-core`](https://github.com/aldeed/meteor-collection2-core) and [`SimpleSchema`](https://www.npmjs.com/package/simpl-schema)\n\nRead more about it here: https://github.com/aldeed/meteor-collection2-core then come back!\n\n```js\nmeteor add aldeed:collection2-core\nmeteor npm i -S simpl-schema\n```\n\nNow let's create a schema for our `Post`:\n\n```js\n// file: /imports/db/posts/schema.js\nimport SimpleSchema from 'simpl-schema';\n\nexport default new SimpleSchema({\n title: {\n type: String,\n },\n tags: {\n type: Array,\n },\n 'tags.$': {\n type: String,\n },\n isApproved: {\n type: Boolean,\n defaultValue: false,\n },\n userId: {\n type: String,\n }\n})\n```\n\nNow let's attach it:\n```js\n// file: /imports/db/posts/collection.js\nimport {Mongo} from 'meteor/mongo';\nimport PostSchema from './schema';\n\nconst Posts = new Mongo.Collection('posts');\n\nPosts.attachSchema(PostSchema);\n\nexport default Posts;\n```\n\nNice! This structure works fine. This will never allow you to insert or update a document without validating it properly.\n\nBeautiful, but now we see those \"tags\" in there, and we know from our previous chapter, that we need a way to re-use them properly,\nthis is why we introduce here to concept of Enums.\n\n\n## Enums\n\nAn Enum is a set of constants, and they should be in their own module. Let's try an example\n\n```js\n// file: /imports/db/posts/enums/tags.js\n\nconst PostTagsEnum = {\n PSYCHOLOGY: 'psychology', \n PHILOSOPHY: 'philosophy',\n}\n\n// maybe you want to have them used as human readable \nconst PostTagsLabels = {\n [PostTagsEnum.PSYCHOLOGY]: 'Psychology & Education',\n [PostTagsEnum.PHILOSOPHY]: 'Philosophy & Arts',\n}\n\nexport default PostTagsEnum;\n\nexport {\n PostTagsEnum,\n PostTagsLabels,\n}\n```\n\nThe value of the enum, represents how you internally want to store it. Does not represent how you want to display it. Keep that in mind.\n\nAnd if we go back to our schema it can look something like:\n```js\nimport _ from 'underscore'; // don't forget to do meteor npm i -S underscore\nimport SimpleSchema from 'simpl-schema';\nimport PostTagsEnum from './enums/tags';\n\nexport default new SimpleSchema({\n ...\n 'tags.$': {\n type: String,\n allowedValues: _.values(PostTagsEnum) // this returns ['psychology', 'philosophy']\n },\n ...\n})\n```\nAnother quick example to ilustrate their purpose, imagine a status of a Task:\n```js\n// /imports/db/tasks/enums/status.js\nexport default {\n ACTIVE: 'active',\n PENDING: 'pending',\n COMPLETED: 'completed'\n}\n```\n\nThe rule using enums is that when you are dealing with them, you must never use the string/value itself.\n\nIn your code, client or server, when you have to see if a status is active:\n```js\n// VERY BAD\nif (object.status === 'active') {\n // do something\n}\n```\n\n```js\nimport ObjectStatusEnum from '...';\n// OK\nif (object.status === ObjectStatusEnum.ACTIVE) {\n // do something\n}\n```\n\nKeep in mind that you can use Enums anywhere, not only for storing stuff in your db. Whenever you find there's a list of constants needed,\nyou are looking at an enum. And ofcourse you can use them client-side or server-side.\n\n## Meteor.users\n\nThis collection feels like it is from another world, it's best that we standardize it as well, and also\ngive it a schema:\n\n```js\n// file: /imports/db/users/collection.js\nimport {Meteor} from 'meteor/meteor';\nimport UserSchema from './schema';\n\nconst Users = Meteor.users;\n\nUsers.attachSchema(UserSchema);\n\nexport default Users;\n```\n\nA sample schema that works with Accounts configuration:\n```js\n// file: /imports/db/users/schema.js\nimport SimpleSchema from 'simpl-schema';\n\nexport default new SimpleSchema({\n _id: {type: String},\n username: {type: String, optional: true},\n emails: {type: Array},\n 'emails.$': {type: Object},\n 'emails.$.address': {type: String},\n 'emails.$.verified': {type: Boolean},\n createdAt: {type: Date},\n services: {type: Object, blackbox: true},\n roles: {\n type: Array,\n optional: true\n },\n \"roles.$\": {\n type: String\n },\n profile: {\n type: Object,\n optional: true\n },\n 'profile.firstName': {\n type: String,\n optional: true\n },\n 'profile.lastName': {\n type: String,\n optional: true\n },\n});\n```\n\nAnd don't forget about this nice package: https://github.com/alanning/meteor-roles. Allows you to nicely manage roles in the system.\n\n\n## Hooks\n\nYou may want to do something after an element is removed or updated or inserted from a certain collection.\n\nThe almost official way to do this is by using this awesome package: https://atmospherejs.com/matb33/collection-hooks\n \nGive it a read, you'll love it.\n\nMy recommendation here is to dispatch an event on a given hook, and store the hooks inside `/imports/db/{collection}/hooks.js`,\nand the logic for handling the hooks should be inside `/imports/api`\n\n## Behaviours\n\nMost likely, your collections will store the user who created something, or store things like when it was created, or when it was updated.\n\nSo instead of creating hooks for every collection, it's easier to define a behaviour and re-use it.\n\nPlease read about it on these links:\nhttps://github.com/zimme/meteor-collection-behaviours/\n\nFor storage, I think it's fine to just attachBehaviours where you store the collection:\n\n```js\n// file: /imports/db/posts/collection.js\n\n// ...\nPosts.attachBehaviour('timestampable');\n```\n\n## Extensions\n\nDon't be afraid to extend your collection, especially when you need something that is specific to the collection. For example, you have the `_id` of an user,\nand you want to quickly get the email:\n\n```js\n// file: /imports/db/users/extensions.js\n\nexport default {\n getEmailForUserId(userId) {\n const user = this.findOne(userId, {\n fields: {emails: 1}\n });\n \n if (!user) {\n throw new Meteor.Error('not-found');\n }\n \n return user.emails[0].address;\n }\n}\n```\n\n```js\n// file: /imports/db/users/collection.js\nimport UsersExtension from './extensions';\n\n// ...\n_.extend(Users, UsersExtension);\n// ...\n```\n\n## Helpers\n\nWith the use of this package: https://github.com/dburles/meteor-collection-helpers you can make your documents smarter if they are fetched through your collections.\n```js\n// file: /imports/db/users/helpers.js\n\nexport default {\n getEmail(userId) {\n return this.emails[0].address;\n }\n}\n```\n\n```js\n// file: /imports/db/users/collection.js\nimport UsersHelpers from './helpers';\n\n// ...\nUsers.helpers(UsersHelpers);\n// ...\n```\n\nThis will allow you to easily do:\n\n```js\nUsers.findOne(userId).getEmail()\n```\n\n## Shortcuts\n \nCreate an index for your collections:\n\n```js\n// file: /imports/db/index.js\n\nimport Posts from './posts/collection';\nimport Comments from './comments/collection';\n\nexport {\n Posts,\n Comments\n}\n```\n\nThis will allow you to use it like this:\n\n```js\nimport {Posts, Comments} from '/imports/db';\n```\n\nYou can go ahead and even put enums inside index.js, but be very careful with their naming,\nas they need to be specific: `${collection}${enumName}Enum`, example:\n- TaskStatusEnum\n- PostTagsEnum \n- etc\n\n## Relations (Grapher)\n\nHow do we work with relations in a non-relational database? We use [Grapher](http://grapher.cultofcoders.com), ofcourse.\nAnd even if we don't have relations, we still should use [Grapher](http://grapher.cultofcoders.com) because of the way it works, as you will see below:\n\nHere's how simple it is:\n\n```bash\nmeteor add cultofcoders:grapher\n```\n\n```\n// file: /imports/db/posts/links.js\nimport {Posts, Users} from '/imports/db';\n\nPosts.addLinks({\n author: {\n type: 'one',\n collection: Users,\n field: 'authorId'\n }\n})\n```\n\nWe need those links loaded, so aggregate all links importing into a single file. \nImport this file in the client-side and server-side init modules.\n\n```js\n// file: /imports/db/links.js\nimport './posts/links.js';\n```\n\nAnd when you want to get the post alongside with the user's profile for example:\n\n```js\nconst post = Posts.createQuery({\n $filters: {\n _id: postId,\n },\n title: 1,\n user: {\n profile: 1,\n }\n}).fetchOne((err, post) => {\n console.log(post.user.profile.firstName);\n});\n```\n\n[Grapher](http://grapher.cultofcoders.com) is a very complex tool and it can work with queries client-side as well. You can have reactive queries, there's simply a lot related to it,\nbut it's properly documented here: http://grapher.cultofcoders.com. \n\nBegin with the Guide, and patiently read through it. Once you read the guide you can understand the following patterns:\n\n### Creating a simple query\n\nThe recommended way of working with [Grapher](http://grapher.cultofcoders.com) is by working exclusively with NamedQueries.\nIf you would like to store and expose NamedQueries:\n\n```js\n// file: /imports/db/posts/queries/getPost.js\nimport Posts from '../collection';\n\nexport default Posts.createQuery('getPost', {\n $filter({filters, params}) {\n filters._id = params.postId;\n },\n title: 1,\n user: {\n profile: 1,\n }\n});\n```\n\nDon't be afraid to create shortcuts for all your queries in one place:\n```js\n// file: /imports/db/queries.js\nimport getPost from './posts/queries/getPost',\n\nexport {\n getPost\n}\n```\n\nNow that we created the query, we need to expose and secure it:\n\n```js\n// file: /imports/api/posts/queries/getPost.expose.js\n// if you don't expose it you can't work with it\nimport {getPost} from '/imports/db/queries'\n\ngetPost.expose({\n firewall(userId, params) {\n // you can manipulate params object here\n if (isAllowed(userId)) {\n // you can throw exceptions if he's not allowed access to this query\n }\n // the firewall can either: throw exception or modify params\n }\n})\n```\n\nAggregate it in one file and import this file on server-side only.\n```js\n// file: /imports/api/exposures.js\nimport './posts/queries/getPost.expose.js';\n```\n\nNow from the client you can easily do something like:\n```js\n// barbaric (but it's also nice)\ncreateQuery({\n getPost: {\n postId: 'XXX'\n }\n}).fetchOne((err, post) => {\n console.log(post);\n // you will also have access to post.users.name\n})\n\n// modular\nimport {getPost} from '/imports/db/queries'\n\ngetPost.clone({\n postId: 'XXX'\n}).fetchOne((err, post) => { ... })\n```\n\nBy abstracting the data retrieval layer to [Grapher](http://grapher.cultofcoders.com) and allowing it to do the linking of collections for you, you will find yourself with so much less headache.\nBecause [Grapher](http://grapher.cultofcoders.com) forces you to specify only the fields you need, you will find your apps running fast and secure right from the start.\n\n### Separating concerns\n\nYou may feel that having your query in `/imports/db` and your exposure inside `/imports/api` to be a little odd. \nThis is why you can put your query inside `/imports/api` as well. But the reason for not putting exposure inside `/imports/db` is\nbecause it contains app logic, and that layer it should be independent from it.\n\n\n\n\n\n","source":"chapters/3/persistence-layer.md","raw":"---\ntitle: 'Persistence Layer'\ndescription: Using collections the right way\ndisqusPage: 'Chapter 3: Persistence Layer'\n---\n\n## What is it ?\n\nThe Persistence Layer (PL) is that \"thingie\" in your app that communicates with another API to store the data we want.\nIn our case, we communicate with MongoDB for that. Therefore, an instance of `Mongo.Collection` is our API to communicate with the Persistence Layer, therefore in our app we can regard collections as the PL.\n\nWhat if at some point in the future, you decide that a certain\ncollection should not be kept in MongoDB but rather used via an external HTTP API? \n\nYour code architecture should try to be as independent as possible from the PL, because this opens the path for scaling and decoupling later on.\n\nJust try and think this way, and whenever you want to start a new project: disregard the way you are going to store the data, focus more on the app/business logic layer.\n\nThen build your database on top of it.\n\n## Structure\n\nSince we want to explicitly separate it, I propose that instead of storing collections inside `/imports/api`, we give them their own folder `/imports/db`\n\n`/imports/api/posts/collection.js` -> `/imports/db/posts/collection.js`\n\n\n```js\n// file: /imports/db/posts/collection.js\nimport {Mongo} from 'meteor/mongo';\n\nconst Posts = new Mongo.Collection('posts');\n\nexport default Posts;\n```\n\nNow let's assume you have a new collection for comments on posts, and it's used only for posts. What do we do ?\n\nShould we store it in `/imports/db/posts/comments/collection.js` or in `/imports/db/postComments/collection.js` ?\n\nIt's a matter of preference here. I prefer the first approach, but the collection's name I use is `PostComments` not `Comments`, so\nwe know it's context. But this only applies if my collection is coupled in a way to posts, if not, then go with second approach.\n\n## Validating Documents\n\nWe need to be able to validate easily the data we save in a collection to prevent bad things from happening.\n\nFor this we are going to use [`aldeed:collection2-core`](https://github.com/aldeed/meteor-collection2-core) and [`SimpleSchema`](https://www.npmjs.com/package/simpl-schema)\n\nRead more about it here: https://github.com/aldeed/meteor-collection2-core then come back!\n\n```js\nmeteor add aldeed:collection2-core\nmeteor npm i -S simpl-schema\n```\n\nNow let's create a schema for our `Post`:\n\n```js\n// file: /imports/db/posts/schema.js\nimport SimpleSchema from 'simpl-schema';\n\nexport default new SimpleSchema({\n title: {\n type: String,\n },\n tags: {\n type: Array,\n },\n 'tags.$': {\n type: String,\n },\n isApproved: {\n type: Boolean,\n defaultValue: false,\n },\n userId: {\n type: String,\n }\n})\n```\n\nNow let's attach it:\n```js\n// file: /imports/db/posts/collection.js\nimport {Mongo} from 'meteor/mongo';\nimport PostSchema from './schema';\n\nconst Posts = new Mongo.Collection('posts');\n\nPosts.attachSchema(PostSchema);\n\nexport default Posts;\n```\n\nNice! This structure works fine. This will never allow you to insert or update a document without validating it properly.\n\nBeautiful, but now we see those \"tags\" in there, and we know from our previous chapter, that we need a way to re-use them properly,\nthis is why we introduce here to concept of Enums.\n\n\n## Enums\n\nAn Enum is a set of constants, and they should be in their own module. Let's try an example\n\n```js\n// file: /imports/db/posts/enums/tags.js\n\nconst PostTagsEnum = {\n PSYCHOLOGY: 'psychology', \n PHILOSOPHY: 'philosophy',\n}\n\n// maybe you want to have them used as human readable \nconst PostTagsLabels = {\n [PostTagsEnum.PSYCHOLOGY]: 'Psychology & Education',\n [PostTagsEnum.PHILOSOPHY]: 'Philosophy & Arts',\n}\n\nexport default PostTagsEnum;\n\nexport {\n PostTagsEnum,\n PostTagsLabels,\n}\n```\n\nThe value of the enum, represents how you internally want to store it. Does not represent how you want to display it. Keep that in mind.\n\nAnd if we go back to our schema it can look something like:\n```js\nimport _ from 'underscore'; // don't forget to do meteor npm i -S underscore\nimport SimpleSchema from 'simpl-schema';\nimport PostTagsEnum from './enums/tags';\n\nexport default new SimpleSchema({\n ...\n 'tags.$': {\n type: String,\n allowedValues: _.values(PostTagsEnum) // this returns ['psychology', 'philosophy']\n },\n ...\n})\n```\nAnother quick example to ilustrate their purpose, imagine a status of a Task:\n```js\n// /imports/db/tasks/enums/status.js\nexport default {\n ACTIVE: 'active',\n PENDING: 'pending',\n COMPLETED: 'completed'\n}\n```\n\nThe rule using enums is that when you are dealing with them, you must never use the string/value itself.\n\nIn your code, client or server, when you have to see if a status is active:\n```js\n// VERY BAD\nif (object.status === 'active') {\n // do something\n}\n```\n\n```js\nimport ObjectStatusEnum from '...';\n// OK\nif (object.status === ObjectStatusEnum.ACTIVE) {\n // do something\n}\n```\n\nKeep in mind that you can use Enums anywhere, not only for storing stuff in your db. Whenever you find there's a list of constants needed,\nyou are looking at an enum. And ofcourse you can use them client-side or server-side.\n\n## Meteor.users\n\nThis collection feels like it is from another world, it's best that we standardize it as well, and also\ngive it a schema:\n\n```js\n// file: /imports/db/users/collection.js\nimport {Meteor} from 'meteor/meteor';\nimport UserSchema from './schema';\n\nconst Users = Meteor.users;\n\nUsers.attachSchema(UserSchema);\n\nexport default Users;\n```\n\nA sample schema that works with Accounts configuration:\n```js\n// file: /imports/db/users/schema.js\nimport SimpleSchema from 'simpl-schema';\n\nexport default new SimpleSchema({\n _id: {type: String},\n username: {type: String, optional: true},\n emails: {type: Array},\n 'emails.$': {type: Object},\n 'emails.$.address': {type: String},\n 'emails.$.verified': {type: Boolean},\n createdAt: {type: Date},\n services: {type: Object, blackbox: true},\n roles: {\n type: Array,\n optional: true\n },\n \"roles.$\": {\n type: String\n },\n profile: {\n type: Object,\n optional: true\n },\n 'profile.firstName': {\n type: String,\n optional: true\n },\n 'profile.lastName': {\n type: String,\n optional: true\n },\n});\n```\n\nAnd don't forget about this nice package: https://github.com/alanning/meteor-roles. Allows you to nicely manage roles in the system.\n\n\n## Hooks\n\nYou may want to do something after an element is removed or updated or inserted from a certain collection.\n\nThe almost official way to do this is by using this awesome package: https://atmospherejs.com/matb33/collection-hooks\n \nGive it a read, you'll love it.\n\nMy recommendation here is to dispatch an event on a given hook, and store the hooks inside `/imports/db/{collection}/hooks.js`,\nand the logic for handling the hooks should be inside `/imports/api`\n\n## Behaviours\n\nMost likely, your collections will store the user who created something, or store things like when it was created, or when it was updated.\n\nSo instead of creating hooks for every collection, it's easier to define a behaviour and re-use it.\n\nPlease read about it on these links:\nhttps://github.com/zimme/meteor-collection-behaviours/\n\nFor storage, I think it's fine to just attachBehaviours where you store the collection:\n\n```js\n// file: /imports/db/posts/collection.js\n\n// ...\nPosts.attachBehaviour('timestampable');\n```\n\n## Extensions\n\nDon't be afraid to extend your collection, especially when you need something that is specific to the collection. For example, you have the `_id` of an user,\nand you want to quickly get the email:\n\n```js\n// file: /imports/db/users/extensions.js\n\nexport default {\n getEmailForUserId(userId) {\n const user = this.findOne(userId, {\n fields: {emails: 1}\n });\n \n if (!user) {\n throw new Meteor.Error('not-found');\n }\n \n return user.emails[0].address;\n }\n}\n```\n\n```js\n// file: /imports/db/users/collection.js\nimport UsersExtension from './extensions';\n\n// ...\n_.extend(Users, UsersExtension);\n// ...\n```\n\n## Helpers\n\nWith the use of this package: https://github.com/dburles/meteor-collection-helpers you can make your documents smarter if they are fetched through your collections.\n```js\n// file: /imports/db/users/helpers.js\n\nexport default {\n getEmail(userId) {\n return this.emails[0].address;\n }\n}\n```\n\n```js\n// file: /imports/db/users/collection.js\nimport UsersHelpers from './helpers';\n\n// ...\nUsers.helpers(UsersHelpers);\n// ...\n```\n\nThis will allow you to easily do:\n\n```js\nUsers.findOne(userId).getEmail()\n```\n\n## Shortcuts\n \nCreate an index for your collections:\n\n```js\n// file: /imports/db/index.js\n\nimport Posts from './posts/collection';\nimport Comments from './comments/collection';\n\nexport {\n Posts,\n Comments\n}\n```\n\nThis will allow you to use it like this:\n\n```js\nimport {Posts, Comments} from '/imports/db';\n```\n\nYou can go ahead and even put enums inside index.js, but be very careful with their naming,\nas they need to be specific: `${collection}${enumName}Enum`, example:\n- TaskStatusEnum\n- PostTagsEnum \n- etc\n\n## Relations (Grapher)\n\nHow do we work with relations in a non-relational database? We use [Grapher](http://grapher.cultofcoders.com), ofcourse.\nAnd even if we don't have relations, we still should use [Grapher](http://grapher.cultofcoders.com) because of the way it works, as you will see below:\n\nHere's how simple it is:\n\n```bash\nmeteor add cultofcoders:grapher\n```\n\n```\n// file: /imports/db/posts/links.js\nimport {Posts, Users} from '/imports/db';\n\nPosts.addLinks({\n author: {\n type: 'one',\n collection: Users,\n field: 'authorId'\n }\n})\n```\n\nWe need those links loaded, so aggregate all links importing into a single file. \nImport this file in the client-side and server-side init modules.\n\n```js\n// file: /imports/db/links.js\nimport './posts/links.js';\n```\n\nAnd when you want to get the post alongside with the user's profile for example:\n\n```js\nconst post = Posts.createQuery({\n $filters: {\n _id: postId,\n },\n title: 1,\n user: {\n profile: 1,\n }\n}).fetchOne((err, post) => {\n console.log(post.user.profile.firstName);\n});\n```\n\n[Grapher](http://grapher.cultofcoders.com) is a very complex tool and it can work with queries client-side as well. You can have reactive queries, there's simply a lot related to it,\nbut it's properly documented here: http://grapher.cultofcoders.com. \n\nBegin with the Guide, and patiently read through it. Once you read the guide you can understand the following patterns:\n\n### Creating a simple query\n\nThe recommended way of working with [Grapher](http://grapher.cultofcoders.com) is by working exclusively with NamedQueries.\nIf you would like to store and expose NamedQueries:\n\n```js\n// file: /imports/db/posts/queries/getPost.js\nimport Posts from '../collection';\n\nexport default Posts.createQuery('getPost', {\n $filter({filters, params}) {\n filters._id = params.postId;\n },\n title: 1,\n user: {\n profile: 1,\n }\n});\n```\n\nDon't be afraid to create shortcuts for all your queries in one place:\n```js\n// file: /imports/db/queries.js\nimport getPost from './posts/queries/getPost',\n\nexport {\n getPost\n}\n```\n\nNow that we created the query, we need to expose and secure it:\n\n```js\n// file: /imports/api/posts/queries/getPost.expose.js\n// if you don't expose it you can't work with it\nimport {getPost} from '/imports/db/queries'\n\ngetPost.expose({\n firewall(userId, params) {\n // you can manipulate params object here\n if (isAllowed(userId)) {\n // you can throw exceptions if he's not allowed access to this query\n }\n // the firewall can either: throw exception or modify params\n }\n})\n```\n\nAggregate it in one file and import this file on server-side only.\n```js\n// file: /imports/api/exposures.js\nimport './posts/queries/getPost.expose.js';\n```\n\nNow from the client you can easily do something like:\n```js\n// barbaric (but it's also nice)\ncreateQuery({\n getPost: {\n postId: 'XXX'\n }\n}).fetchOne((err, post) => {\n console.log(post);\n // you will also have access to post.users.name\n})\n\n// modular\nimport {getPost} from '/imports/db/queries'\n\ngetPost.clone({\n postId: 'XXX'\n}).fetchOne((err, post) => { ... })\n```\n\nBy abstracting the data retrieval layer to [Grapher](http://grapher.cultofcoders.com) and allowing it to do the linking of collections for you, you will find yourself with so much less headache.\nBecause [Grapher](http://grapher.cultofcoders.com) forces you to specify only the fields you need, you will find your apps running fast and secure right from the start.\n\n### Separating concerns\n\nYou may feel that having your query in `/imports/db` and your exposure inside `/imports/api` to be a little odd. \nThis is why you can put your query inside `/imports/api` as well. But the reason for not putting exposure inside `/imports/db` is\nbecause it contains app logic, and that layer it should be independent from it.\n\n\n\n\n\n","date":"2017-11-10T12:45:17.017Z","updated":"2017-11-10T12:45:17.017Z","path":"chapters/3/persistence-layer.html","comments":1,"layout":"page","_id":"cj9twby8100079ajxxxcmmxjm","content":"The Persistence Layer (PL) is that “thingie” in your app that communicates with another API to store the data we want.\nIn our case, we communicate with MongoDB for that. Therefore, an instance of Mongo.Collection
is our API to communicate with the Persistence Layer, therefore in our app we can regard collections as the PL.
What if at some point in the future, you decide that a certain\ncollection should not be kept in MongoDB but rather used via an external HTTP API?
\nYour code architecture should try to be as independent as possible from the PL, because this opens the path for scaling and decoupling later on.
\nJust try and think this way, and whenever you want to start a new project: disregard the way you are going to store the data, focus more on the app/business logic layer.
\nThen build your database on top of it.
\nSince we want to explicitly separate it, I propose that instead of storing collections inside /imports/api
, we give them their own folder /imports/db
/imports/api/posts/collection.js
-> /imports/db/posts/collection.js
|
|
Now let’s assume you have a new collection for comments on posts, and it’s used only for posts. What do we do ?
\nShould we store it in /imports/db/posts/comments/collection.js
or in /imports/db/postComments/collection.js
?
It’s a matter of preference here. I prefer the first approach, but the collection’s name I use is PostComments
not Comments
, so\nwe know it’s context. But this only applies if my collection is coupled in a way to posts, if not, then go with second approach.
We need to be able to validate easily the data we save in a collection to prevent bad things from happening.
\nFor this we are going to use aldeed:collection2-core
and SimpleSchema
Read more about it here: https://github.com/aldeed/meteor-collection2-core then come back!
\n
|
|
Now let’s create a schema for our Post
:
|
|
Now let’s attach it:\n
Nice! This structure works fine. This will never allow you to insert or update a document without validating it properly.
\nBeautiful, but now we see those “tags” in there, and we know from our previous chapter, that we need a way to re-use them properly,\nthis is why we introduce here to concept of Enums.
\nAn Enum is a set of constants, and they should be in their own module. Let’s try an example
\n
|
|
The value of the enum, represents how you internally want to store it. Does not represent how you want to display it. Keep that in mind.
\nAnd if we go back to our schema it can look something like:\n
Another quick example to ilustrate their purpose, imagine a status of a Task:\n
The rule using enums is that when you are dealing with them, you must never use the string/value itself.
\nIn your code, client or server, when you have to see if a status is active:\n
|
|
Keep in mind that you can use Enums anywhere, not only for storing stuff in your db. Whenever you find there’s a list of constants needed,\nyou are looking at an enum. And ofcourse you can use them client-side or server-side.
\nThis collection feels like it is from another world, it’s best that we standardize it as well, and also\ngive it a schema:
\n
|
|
A sample schema that works with Accounts configuration:\n
And don’t forget about this nice package: https://github.com/alanning/meteor-roles. Allows you to nicely manage roles in the system.
\nYou may want to do something after an element is removed or updated or inserted from a certain collection.
\nThe almost official way to do this is by using this awesome package: https://atmospherejs.com/matb33/collection-hooks
\nGive it a read, you’ll love it.
\nMy recommendation here is to dispatch an event on a given hook, and store the hooks inside /imports/db/{collection}/hooks.js
,\nand the logic for handling the hooks should be inside /imports/api
Most likely, your collections will store the user who created something, or store things like when it was created, or when it was updated.
\nSo instead of creating hooks for every collection, it’s easier to define a behaviour and re-use it.
\nPlease read about it on these links:\nhttps://github.com/zimme/meteor-collection-behaviours/
\nFor storage, I think it’s fine to just attachBehaviours where you store the collection:
\n
|
|
Don’t be afraid to extend your collection, especially when you need something that is specific to the collection. For example, you have the _id
of an user,\nand you want to quickly get the email:
|
|
|
|
With the use of this package: https://github.com/dburles/meteor-collection-helpers you can make your documents smarter if they are fetched through your collections.\n
|
|
This will allow you to easily do:
\n
|
|
Create an index for your collections:
\n
|
|
This will allow you to use it like this:
\n
|
|
You can go ahead and even put enums inside index.js, but be very careful with their naming,\nas they need to be specific: ${collection}${enumName}Enum
, example:
How do we work with relations in a non-relational database? We use Grapher, ofcourse.\nAnd even if we don’t have relations, we still should use Grapher because of the way it works, as you will see below:
\nHere’s how simple it is:
\n
|
|
|
|
We need those links loaded, so aggregate all links importing into a single file. \nImport this file in the client-side and server-side init modules.
\n
|
|
And when you want to get the post alongside with the user’s profile for example:
\n
|
|
Grapher is a very complex tool and it can work with queries client-side as well. You can have reactive queries, there’s simply a lot related to it,\nbut it’s properly documented here: http://grapher.cultofcoders.com.
\nBegin with the Guide, and patiently read through it. Once you read the guide you can understand the following patterns:
\nThe recommended way of working with Grapher is by working exclusively with NamedQueries.\nIf you would like to store and expose NamedQueries:
\n
|
|
Don’t be afraid to create shortcuts for all your queries in one place:\n
Now that we created the query, we need to expose and secure it:
\n
|
|
Aggregate it in one file and import this file on server-side only.\n
Now from the client you can easily do something like:\n
By abstracting the data retrieval layer to Grapher and allowing it to do the linking of collections for you, you will find yourself with so much less headache.\nBecause Grapher forces you to specify only the fields you need, you will find your apps running fast and secure right from the start.
\nYou may feel that having your query in /imports/db
and your exposure inside /imports/api
to be a little odd. \nThis is why you can put your query inside /imports/api
as well. But the reason for not putting exposure inside /imports/db
is\nbecause it contains app logic, and that layer it should be independent from it.
The Persistence Layer (PL) is that “thingie” in your app that communicates with another API to store the data we want.\nIn our case, we communicate with MongoDB for that. Therefore, an instance of Mongo.Collection
is our API to communicate with the Persistence Layer, therefore in our app we can regard collections as the PL.
What if at some point in the future, you decide that a certain\ncollection should not be kept in MongoDB but rather used via an external HTTP API?
\nYour code architecture should try to be as independent as possible from the PL, because this opens the path for scaling and decoupling later on.
\nJust try and think this way, and whenever you want to start a new project: disregard the way you are going to store the data, focus more on the app/business logic layer.
\nThen build your database on top of it.
\nSince we want to explicitly separate it, I propose that instead of storing collections inside /imports/api
, we give them their own folder /imports/db
/imports/api/posts/collection.js
-> /imports/db/posts/collection.js
|
|
Now let’s assume you have a new collection for comments on posts, and it’s used only for posts. What do we do ?
\nShould we store it in /imports/db/posts/comments/collection.js
or in /imports/db/postComments/collection.js
?
It’s a matter of preference here. I prefer the first approach, but the collection’s name I use is PostComments
not Comments
, so\nwe know it’s context. But this only applies if my collection is coupled in a way to posts, if not, then go with second approach.
We need to be able to validate easily the data we save in a collection to prevent bad things from happening.
\nFor this we are going to use aldeed:collection2-core
and SimpleSchema
Read more about it here: https://github.com/aldeed/meteor-collection2-core then come back!
\n
|
|
Now let’s create a schema for our Post
:
|
|
Now let’s attach it:\n
Nice! This structure works fine. This will never allow you to insert or update a document without validating it properly.
\nBeautiful, but now we see those “tags” in there, and we know from our previous chapter, that we need a way to re-use them properly,\nthis is why we introduce here to concept of Enums.
\nAn Enum is a set of constants, and they should be in their own module. Let’s try an example
\n
|
|
The value of the enum, represents how you internally want to store it. Does not represent how you want to display it. Keep that in mind.
\nAnd if we go back to our schema it can look something like:\n
Another quick example to ilustrate their purpose, imagine a status of a Task:\n
The rule using enums is that when you are dealing with them, you must never use the string/value itself.
\nIn your code, client or server, when you have to see if a status is active:\n
|
|
Keep in mind that you can use Enums anywhere, not only for storing stuff in your db. Whenever you find there’s a list of constants needed,\nyou are looking at an enum. And ofcourse you can use them client-side or server-side.
\nThis collection feels like it is from another world, it’s best that we standardize it as well, and also\ngive it a schema:
\n
|
|
A sample schema that works with Accounts configuration:\n
And don’t forget about this nice package: https://github.com/alanning/meteor-roles. Allows you to nicely manage roles in the system.
\nYou may want to do something after an element is removed or updated or inserted from a certain collection.
\nThe almost official way to do this is by using this awesome package: https://atmospherejs.com/matb33/collection-hooks
\nGive it a read, you’ll love it.
\nMy recommendation here is to dispatch an event on a given hook, and store the hooks inside /imports/db/{collection}/hooks.js
,\nand the logic for handling the hooks should be inside /imports/api
Most likely, your collections will store the user who created something, or store things like when it was created, or when it was updated.
\nSo instead of creating hooks for every collection, it’s easier to define a behaviour and re-use it.
\nPlease read about it on these links:\nhttps://github.com/zimme/meteor-collection-behaviours/
\nFor storage, I think it’s fine to just attachBehaviours where you store the collection:
\n
|
|
Don’t be afraid to extend your collection, especially when you need something that is specific to the collection. For example, you have the _id
of an user,\nand you want to quickly get the email:
|
|
|
|
With the use of this package: https://github.com/dburles/meteor-collection-helpers you can make your documents smarter if they are fetched through your collections.\n
|
|
This will allow you to easily do:
\n
|
|
Create an index for your collections:
\n
|
|
This will allow you to use it like this:
\n
|
|
You can go ahead and even put enums inside index.js, but be very careful with their naming,\nas they need to be specific: ${collection}${enumName}Enum
, example:
How do we work with relations in a non-relational database? We use Grapher, ofcourse.\nAnd even if we don’t have relations, we still should use Grapher because of the way it works, as you will see below:
\nHere’s how simple it is:
\n
|
|
|
|
We need those links loaded, so aggregate all links importing into a single file. \nImport this file in the client-side and server-side init modules.
\n
|
|
And when you want to get the post alongside with the user’s profile for example:
\n
|
|
Grapher is a very complex tool and it can work with queries client-side as well. You can have reactive queries, there’s simply a lot related to it,\nbut it’s properly documented here: http://grapher.cultofcoders.com.
\nBegin with the Guide, and patiently read through it. Once you read the guide you can understand the following patterns:
\nThe recommended way of working with Grapher is by working exclusively with NamedQueries.\nIf you would like to store and expose NamedQueries:
\n
|
|
Don’t be afraid to create shortcuts for all your queries in one place:\n
Now that we created the query, we need to expose and secure it:
\n
|
|
Aggregate it in one file and import this file on server-side only.\n
Now from the client you can easily do something like:\n
By abstracting the data retrieval layer to Grapher and allowing it to do the linking of collections for you, you will find yourself with so much less headache.\nBecause Grapher forces you to specify only the fields you need, you will find your apps running fast and secure right from the start.
\nYou may feel that having your query in /imports/db
and your exposure inside /imports/api
to be a little odd. \nThis is why you can put your query inside /imports/api
as well. But the reason for not putting exposure inside /imports/db
is\nbecause it contains app logic, and that layer it should be independent from it.
Yes you read that right, unless you want to create a custom functionality that uses the beautiful LiveData utilities\nfrom Meteor, stop using publications, instead leverage Grapher for this.
\nIf you would like to find one post and have it react to changes you will create a NamedQuery:
\n
|
|
|
|
In the client you will use it something like this:
\n
|
|
The advantages are the following:
\nYou have to use Grapher in your Meteor app if you want to bring it to enterprise standards. Period. It’s the best way we know.
\n","site":{"data":{}},"excerpt":"","more":"Yes you read that right, unless you want to create a custom functionality that uses the beautiful LiveData utilities\nfrom Meteor, stop using publications, instead leverage Grapher for this.
\nIf you would like to find one post and have it react to changes you will create a NamedQuery:
\n
|
|
|
|
In the client you will use it something like this:
\n
|
|
The advantages are the following:
\nYou have to use Grapher in your Meteor app if you want to bring it to enterprise standards. Period. It’s the best way we know.
\n"},{"title":"Reusable Modules","description":"How to create pieces of code designed for reusability","disqusPage":"Chapter 3: Reusable Modules","_content":"\n## Why?\n\nLet's say you want to create a chat thingie, and you want to decouple it, because\nyou think that in the future you will definitely use it in some other project.\n\nYou need to treat it separately as a module:\n\nCreate a new folder `/imports/modules/chat/`\n\n```js\n// file: /imports/modules/chat/client.js\n// Do things related to client initialization (if necessary)\n\n// file: /imports/modules/chat/server.js\n// Do things related to server initialization, methods, listeners, etc (if necessary)\n\n// file: /imports/modules/chat/db\n// Apply the same principles\n\n// file: /imports/modules/chat/api\n// Apply the same principles\n\n// file: /imports/modules/chat/client\n// Store frontend stuff here.\n```\n\n## Imports\n\nInside your re-usable module, never use absolute paths, only relative paths when importing:\n\n```js\nimport {Threads, Messages} from ../../db\n```\n\n## README.md\n\nProvide a README that specifies the module this uses, so it can be nicely reused.\n\n```markdown\n// file: /imports/modules/chat/README.md\n// Store here the installation details and npm dependencies.\n// Describe in general terms how to use this module\n```\n\n## Event handling\n\nUse an independent event handler. Not the global one.\n\n```js\n// file: /imports/modules/chat/api/events.js\n\nimport EventEmitter from 'event-emitter';\n\nconst Events = {\n CHAT_NEW: 'chat.new'\n}\n\nexport default new EventEmitter();\n```\n\nIf you want to unify emitters you can do so simply:\nhttps://www.npmjs.com/package/event-emitter#unifyemitter1-emitter2-event-emitterunify\n\nBut if you want to do app specific logic when something in this module happens, I recommend you use the EventEmitter from `chat` module to listen for events.\n\n\n## A git submodule\n\nOfcourse now that you have it as a module, you can make it a git-submodule, and make it re-usable in multiple projects and maintainable.\n\nRead more here:\nhttps://git-scm.com/book/en/v2/Git-Tools-Submodules \n\n## Caution\n\nThe problem is that this app is not so independent, so you may find yourself conflicting with some npm dependencies,\nso keep that in mind. It depends on your whole structure and how you use it.\n\nYou can avoid this by properly versioning your git submodule.\n\n\n","source":"chapters/3/reusable-modules.md","raw":"---\ntitle: 'Reusable Modules'\ndescription: How to create pieces of code designed for reusability\ndisqusPage: 'Chapter 3: Reusable Modules'\n---\n\n## Why?\n\nLet's say you want to create a chat thingie, and you want to decouple it, because\nyou think that in the future you will definitely use it in some other project.\n\nYou need to treat it separately as a module:\n\nCreate a new folder `/imports/modules/chat/`\n\n```js\n// file: /imports/modules/chat/client.js\n// Do things related to client initialization (if necessary)\n\n// file: /imports/modules/chat/server.js\n// Do things related to server initialization, methods, listeners, etc (if necessary)\n\n// file: /imports/modules/chat/db\n// Apply the same principles\n\n// file: /imports/modules/chat/api\n// Apply the same principles\n\n// file: /imports/modules/chat/client\n// Store frontend stuff here.\n```\n\n## Imports\n\nInside your re-usable module, never use absolute paths, only relative paths when importing:\n\n```js\nimport {Threads, Messages} from ../../db\n```\n\n## README.md\n\nProvide a README that specifies the module this uses, so it can be nicely reused.\n\n```markdown\n// file: /imports/modules/chat/README.md\n// Store here the installation details and npm dependencies.\n// Describe in general terms how to use this module\n```\n\n## Event handling\n\nUse an independent event handler. Not the global one.\n\n```js\n// file: /imports/modules/chat/api/events.js\n\nimport EventEmitter from 'event-emitter';\n\nconst Events = {\n CHAT_NEW: 'chat.new'\n}\n\nexport default new EventEmitter();\n```\n\nIf you want to unify emitters you can do so simply:\nhttps://www.npmjs.com/package/event-emitter#unifyemitter1-emitter2-event-emitterunify\n\nBut if you want to do app specific logic when something in this module happens, I recommend you use the EventEmitter from `chat` module to listen for events.\n\n\n## A git submodule\n\nOfcourse now that you have it as a module, you can make it a git-submodule, and make it re-usable in multiple projects and maintainable.\n\nRead more here:\nhttps://git-scm.com/book/en/v2/Git-Tools-Submodules \n\n## Caution\n\nThe problem is that this app is not so independent, so you may find yourself conflicting with some npm dependencies,\nso keep that in mind. It depends on your whole structure and how you use it.\n\nYou can avoid this by properly versioning your git submodule.\n\n\n","date":"2017-11-10T12:45:17.021Z","updated":"2017-11-10T12:45:17.021Z","path":"chapters/3/reusable-modules.html","comments":1,"layout":"page","_id":"cj9twby8100099ajxz10zcdre","content":"Let’s say you want to create a chat thingie, and you want to decouple it, because\nyou think that in the future you will definitely use it in some other project.
\nYou need to treat it separately as a module:
\nCreate a new folder /imports/modules/chat/
|
|
Inside your re-usable module, never use absolute paths, only relative paths when importing:
\n
|
|
Provide a README that specifies the module this uses, so it can be nicely reused.
\n
|
|
Use an independent event handler. Not the global one.
\n
|
|
If you want to unify emitters you can do so simply:\nhttps://www.npmjs.com/package/event-emitter#unifyemitter1-emitter2-event-emitterunify
\nBut if you want to do app specific logic when something in this module happens, I recommend you use the EventEmitter from chat
module to listen for events.
Ofcourse now that you have it as a module, you can make it a git-submodule, and make it re-usable in multiple projects and maintainable.
\nRead more here:\nhttps://git-scm.com/book/en/v2/Git-Tools-Submodules
\nThe problem is that this app is not so independent, so you may find yourself conflicting with some npm dependencies,\nso keep that in mind. It depends on your whole structure and how you use it.
\nYou can avoid this by properly versioning your git submodule.
\n","site":{"data":{}},"excerpt":"","more":"Let’s say you want to create a chat thingie, and you want to decouple it, because\nyou think that in the future you will definitely use it in some other project.
\nYou need to treat it separately as a module:
\nCreate a new folder /imports/modules/chat/
|
|
Inside your re-usable module, never use absolute paths, only relative paths when importing:
\n
|
|
Provide a README that specifies the module this uses, so it can be nicely reused.
\n
|
|
Use an independent event handler. Not the global one.
\n
|
|
If you want to unify emitters you can do so simply:\nhttps://www.npmjs.com/package/event-emitter#unifyemitter1-emitter2-event-emitterunify
\nBut if you want to do app specific logic when something in this module happens, I recommend you use the EventEmitter from chat
module to listen for events.
Ofcourse now that you have it as a module, you can make it a git-submodule, and make it re-usable in multiple projects and maintainable.
\nRead more here:\nhttps://git-scm.com/book/en/v2/Git-Tools-Submodules
\nThe problem is that this app is not so independent, so you may find yourself conflicting with some npm dependencies,\nso keep that in mind. It depends on your whole structure and how you use it.
\nYou can avoid this by properly versioning your git submodule.
\n"},{"title":"Chapter 2 - Conclusion","description":"Conclusion","disqusPage":"Chapter 2: Conclusion","_content":"\nYou made it through Chapter 2, good job :)\n\nSo we uncovered how to integrate the View Layer into Meteor, how can we write clean CSS code and tools for optimization. What's next ?\nWell, you need to do your homework first, because it will help you form\nnew neural synapses that will enable you to remember and code faster.\n\nNext chapter will be dedicated to how we can use all the knowledge from Chapter 1 & Chapter 2,\nin order to create large scale applications, not your simple To Do list, we are talking about\nHealthcare, Government Apps, Multi-tenant Apps, Role-Based Access Control, CRMs, the usual enterprise type of applications that may require a bigger team,\nand a lot of coding. \n\nWe'll understand how to keep it under-control, how to use testing and how\nto continously integrate and ofcourse: how to deploy!\n\nIt is going to be a big one.\n\n","source":"chapters/2/conclusion.md","raw":"---\ntitle: 'Chapter 2 - Conclusion'\ndescription: Conclusion\ndisqusPage: 'Chapter 2: Conclusion'\n---\n\nYou made it through Chapter 2, good job :)\n\nSo we uncovered how to integrate the View Layer into Meteor, how can we write clean CSS code and tools for optimization. What's next ?\nWell, you need to do your homework first, because it will help you form\nnew neural synapses that will enable you to remember and code faster.\n\nNext chapter will be dedicated to how we can use all the knowledge from Chapter 1 & Chapter 2,\nin order to create large scale applications, not your simple To Do list, we are talking about\nHealthcare, Government Apps, Multi-tenant Apps, Role-Based Access Control, CRMs, the usual enterprise type of applications that may require a bigger team,\nand a lot of coding. \n\nWe'll understand how to keep it under-control, how to use testing and how\nto continously integrate and ofcourse: how to deploy!\n\nIt is going to be a big one.\n\n","date":"2018-03-05T20:41:32.064Z","updated":"2018-03-05T20:41:32.064Z","path":"chapters/2/conclusion.html","_id":"cj9twby82000a9ajx63q6f51i","comments":1,"layout":"page","content":"You made it through Chapter 2, good job :)
\nSo we uncovered how to integrate the View Layer into Meteor, how can we write clean CSS code and tools for optimization. What’s next ?\nWell, you need to do your homework first, because it will help you form\nnew neural synapses that will enable you to remember and code faster.
\nNext chapter will be dedicated to how we can use all the knowledge from Chapter 1 & Chapter 2,\nin order to create large scale applications, not your simple To Do list, we are talking about\nHealthcare, Government Apps, Multi-tenant Apps, Role-Based Access Control, CRMs, the usual enterprise type of applications that may require a bigger team,\nand a lot of coding.
\nWe’ll understand how to keep it under-control, how to use testing and how\nto continously integrate and ofcourse: how to deploy!
\nIt is going to be a big one.
\n","site":{"data":{}},"excerpt":"","more":"You made it through Chapter 2, good job :)
\nSo we uncovered how to integrate the View Layer into Meteor, how can we write clean CSS code and tools for optimization. What’s next ?\nWell, you need to do your homework first, because it will help you form\nnew neural synapses that will enable you to remember and code faster.
\nNext chapter will be dedicated to how we can use all the knowledge from Chapter 1 & Chapter 2,\nin order to create large scale applications, not your simple To Do list, we are talking about\nHealthcare, Government Apps, Multi-tenant Apps, Role-Based Access Control, CRMs, the usual enterprise type of applications that may require a bigger team,\nand a lot of coding.
\nWe’ll understand how to keep it under-control, how to use testing and how\nto continously integrate and ofcourse: how to deploy!
\nIt is going to be a big one.
\n"},{"title":"Services","description":"Using services to decouple your code","disqusPage":"Chapter 3: Services","_content":"\n## What is a service? \nA service is a unit of work (a function) or a group of very related functionality (a class)\n\nUsing services is linked to the [Single responsibility principle](https://en.wikipedia.org/wiki/Single_responsibility_principle)\n\n```simple-text\nQ: So when do we need to use services?\nA: Everytime!\nQ: Where do we use services?\nA: Everywhere!\n```\n\nOk we're done here, everything is clear now, right?\n\nJokes aside, you should treat every functionality in your app as a service, for example:\nYou want to send an email, and ofcourse, you know how to do this, you use *Email.send*. Well, *Email* is actually a service, but now how you send it in your app ?\n\nLet's say you create a Post, and you need to notify the Admin to approve it.\nWhere do you store that unit of logic ? Inside a Meteor method ? Inside a function ? Inside a class ?\n\nThe answer is: **NOT inside the Meteor method**\n\nUsually, you tend to couple logic in your Meteor methods which is a very very bad terrible thing, because Meteor methods act as server-side routes, they act as the *C* (Controller) in *MVC*, \nthe *C* does not contain logic, the *C* is a boss that delegates work to *M* (Services aka Models)\n\nIt is also bad because methods are a communication layer between the client and the server, they shouldn't store business logic, or logic of any kind, imagine them as proxies that communicate with your services.\n\nLet's start focusing about services in depth, and understand some nice principles about\ncrafting them in a nice way.\n\nDon't forget to read: https://github.com/ryanmcdermott/clean-code-javascript \n\n## Sample\n```js\nclass ItemService {\n static createItem() {\n // put logic here for item creation\n }\n \n static updateItem(id, data) {\n const item = this._getItem(id)\n // do something with it\n }\n \n static _getItem(itemId) {\n // returns the item from database or throws an exception\n }\n}\n\nexport default ItemService;\n```\n\nIn most cases, you want your services to be a class with static methods, and not an instance of that class `new ItemService()`, however\nwe'll see below why in some cases using the instance makes more sense.\n\n## Structuring\n\nBy default we are going to put them inside:\n`/imports/api/{module}/services`\n\nUsually {module} represents the collection name it handles it, but this is not always the case, it can represent something that is handling multiple collections to achieve its goal.\n\nIf your services need more decoupling feel free to nest them:\n`/imports/api/{module}/services/{submodule}`\n\nIf you are writing HTTP APIs separate the Service of fetching/persisting data and put it in `/imports/db/{module}` and\nthe one that does include the app/business logic keep it in `/imports/api/{module}`. The one inside `/imports/api` should instantiate the one from `/imports/db` with the correct API keys (most likely retrieved from `Meteor.settings`)\n\nName your services with uppercase if classes (ItemService), or lowercase if functions (doSomething).\nIf your service is a class, suffix it with service, if it's a function, make the sure the filename is a verb.\n\nInside a function service module, you can create additional functions, but you must export only one, if you need to\nexport multiple functions, it will become a service class with static methods.\n\n## Creating Services\n\nGo API first. Don't try to think about the logic, try to think about how you are going to use it. This is why [**TDD**](https://technologyconversations.com/2013/12/20/test-driven-development-tdd-example-walkthrough/) works so well, because\nit lets you think about the API and you also write a test, and passing tests means you finished implementation.\n\nSo, instead of patterns, try to think about how you would use it, and just write a test for it first. You will notice that your development speed will increase dramatically (after first tries). No kidding!\n\n#### Helpful questions:\n1. What would be the cleanest, easiest way to use this Service?\n2. How can I make it so it's easier to understand by others?\n3. What comments can I leave so the next developer that comes in understands this?\n4. Does my service have a single responsability?\n5. Is there any functionality in my service that is outside its scope so I can decouple it?\n\n## Conventions\n\nAgain, don't forget to read: https://github.com/ryanmcdermott/clean-code-javascript. I'm not joking, read it! \n\n#### Functions should be actions or interrogations\n\nPlease favor longer names for small scope, lower names for large scopes.\n\n```js\n// BAD\nobject.author()\nobject.veryBad()\nobject.closing()\nobject.mkSmtGdAbtIt()\n```\n\n```js\n// GOOD\nobject.getAuthor()\nobject.isGood()\nobject.close()\nobject.makeSomethingGoodAboutIt()\n```\n\n#### Variable names should be substantives or interrogations\n\n```js\n// BAD\nlet making;\nlet made;\n```\n\n```js\n// GOOD\nlet isMaking;\nlet isMade;\n```\n\n#### Functions should be small\n\nA function should not be larger than 10 lines. Honestly, if you have that, then it must be refactored. Exceptions being, large switch cases, Pure Components and render() functions from React.\n\n#### Indent with 4 spaces\n\nWith JS and with JSX.\nWhy 4? Because it forces you to decouple and makes your spaghetti much more visible and annoying!\nPlus it feels that the code is more airy and readable.\n\nWhat about callback hell and stuff ?\n1. Use promises, or the sexy async/await syntax\n2. Decouple your callbacks\n\nThis is opinionated, there is no wrong way to indent, there are other factors far more important.\n\n#### Classes hide in large functions\n\nThere are cases where you have a single function as service, and that function begins to grow,\nand you find that decoupling it in multiple functions gets tedious because you will need to pass\nmany variables along, and you realize that those variables are strings or numbers and can't be properly modified,\nso you need to return them, and even if your intentions are pure, your code will become\nmore messy.\n\nThe rule is simple: when it's difficult to decouple a function, create a class. Because inside a class, each method\nhas access to `this` context, so you no longer need to pass variables along.\n\n#### Don't extend classes\n\nAnother opinionated advice, just don't use extend. The only exception is if the class you extend\nis abstract (you don't instantiate/use it stand-alone).\n\n#### Provide JSDoc and Comments\n\nWhenever you feel like you are doing something that is not verbose, leave a comment, and also leave JSDoc, especially for \nvariables. It becomes truly helpful when you are using an API written by someone else and the IDE shows you which variables it\nneeds and a description for them.\n\nIf you find a snippet, or something that describes a certain thing, don't be afraid to leave links,\nespecially if you're writing an facade to an external API, leave link to the docs, it will save you and other devs\ntime.\n\nLeave as much comments as you can, but don't go off the grid, stay to the point. And only leave comments when necessary:\n\n```js\n// BAD (The code already tells you what you do)\n// We are iterating through users \nusers.forEach(user => {});\n```\n```js\n// GOOD (Even if you can read it nicely, you need to understand the intention fast) \n// We are calculating the total cost of all products so we can use it in total calculation cost.\nlet totalCost = 0;\nproducts.forEach(product => {\n totalCost += product.cost;\n})\n```\n\n\n## Dependency Injection\n\nI've looked around the NPM community and everything is too complex for what we really need.\n\nInversion principle is simple, let the service decide what dependencies it has. We are kinda doing this already\nbecause each module imports what it needs in order to execute properly.\n\nThe reason for changing it a bit is the fact that it will allow easy testing. For example, we want to test that `PaymentService.charge` is been called when something happens.\n\n```js\nimport PaymentService from 'somewhere';\n\nclass ItemService {\n static createItem(item) {\n Items.insert(item);\n PaymentService.charge(item.userId, 20.00);\n }\n}\n\nexport default ItemService;\n```\n\nNow in my test how would I be sure that `PaymentService.charge` is called without actually altering `PaymentService`?\nThis requires a change in strategy, by injecting dependencies in the constructor:\n\n```js\nimport PaymentService from 'somewhere';\n\nclass ItemService {\n constructor({paymentService}) {\n this.paymentService = paymentService;\n }\n \n createItem(item) {\n Items.insert(item);\n this.paymentService.charge(item.userId, 20.00);\n }\n}\n\nexport default new ItemService({\n paymentService: PaymentService\n});\n\nexport {ItemService}\n```\n\nOk now if we would like to test it, we have access to the exported variable: `ItemService` and we can pass-in a [stub](http://sinonjs.org/releases/v4.0.1/stubs/) for `PaymentService` in its constructor.\n\nThe only problem with this pattern is the fact that it creates an instance without you wanting to,\ntherefore you can create a separate `ServiceModel` file (ItemServiceModel.js) and instantiate (and export) it inside `ItemService.js`, and when you test\nyou only need to play with `ItemServiceModel`\n\n\n\n\n \n \n\n\n","source":"chapters/3/services.md","raw":"---\ntitle: 'Services'\ndescription: Using services to decouple your code\ndisqusPage: 'Chapter 3: Services'\n---\n\n## What is a service? \nA service is a unit of work (a function) or a group of very related functionality (a class)\n\nUsing services is linked to the [Single responsibility principle](https://en.wikipedia.org/wiki/Single_responsibility_principle)\n\n```simple-text\nQ: So when do we need to use services?\nA: Everytime!\nQ: Where do we use services?\nA: Everywhere!\n```\n\nOk we're done here, everything is clear now, right?\n\nJokes aside, you should treat every functionality in your app as a service, for example:\nYou want to send an email, and ofcourse, you know how to do this, you use *Email.send*. Well, *Email* is actually a service, but now how you send it in your app ?\n\nLet's say you create a Post, and you need to notify the Admin to approve it.\nWhere do you store that unit of logic ? Inside a Meteor method ? Inside a function ? Inside a class ?\n\nThe answer is: **NOT inside the Meteor method**\n\nUsually, you tend to couple logic in your Meteor methods which is a very very bad terrible thing, because Meteor methods act as server-side routes, they act as the *C* (Controller) in *MVC*, \nthe *C* does not contain logic, the *C* is a boss that delegates work to *M* (Services aka Models)\n\nIt is also bad because methods are a communication layer between the client and the server, they shouldn't store business logic, or logic of any kind, imagine them as proxies that communicate with your services.\n\nLet's start focusing about services in depth, and understand some nice principles about\ncrafting them in a nice way.\n\nDon't forget to read: https://github.com/ryanmcdermott/clean-code-javascript \n\n## Sample\n```js\nclass ItemService {\n static createItem() {\n // put logic here for item creation\n }\n \n static updateItem(id, data) {\n const item = this._getItem(id)\n // do something with it\n }\n \n static _getItem(itemId) {\n // returns the item from database or throws an exception\n }\n}\n\nexport default ItemService;\n```\n\nIn most cases, you want your services to be a class with static methods, and not an instance of that class `new ItemService()`, however\nwe'll see below why in some cases using the instance makes more sense.\n\n## Structuring\n\nBy default we are going to put them inside:\n`/imports/api/{module}/services`\n\nUsually {module} represents the collection name it handles it, but this is not always the case, it can represent something that is handling multiple collections to achieve its goal.\n\nIf your services need more decoupling feel free to nest them:\n`/imports/api/{module}/services/{submodule}`\n\nIf you are writing HTTP APIs separate the Service of fetching/persisting data and put it in `/imports/db/{module}` and\nthe one that does include the app/business logic keep it in `/imports/api/{module}`. The one inside `/imports/api` should instantiate the one from `/imports/db` with the correct API keys (most likely retrieved from `Meteor.settings`)\n\nName your services with uppercase if classes (ItemService), or lowercase if functions (doSomething).\nIf your service is a class, suffix it with service, if it's a function, make the sure the filename is a verb.\n\nInside a function service module, you can create additional functions, but you must export only one, if you need to\nexport multiple functions, it will become a service class with static methods.\n\n## Creating Services\n\nGo API first. Don't try to think about the logic, try to think about how you are going to use it. This is why [**TDD**](https://technologyconversations.com/2013/12/20/test-driven-development-tdd-example-walkthrough/) works so well, because\nit lets you think about the API and you also write a test, and passing tests means you finished implementation.\n\nSo, instead of patterns, try to think about how you would use it, and just write a test for it first. You will notice that your development speed will increase dramatically (after first tries). No kidding!\n\n#### Helpful questions:\n1. What would be the cleanest, easiest way to use this Service?\n2. How can I make it so it's easier to understand by others?\n3. What comments can I leave so the next developer that comes in understands this?\n4. Does my service have a single responsability?\n5. Is there any functionality in my service that is outside its scope so I can decouple it?\n\n## Conventions\n\nAgain, don't forget to read: https://github.com/ryanmcdermott/clean-code-javascript. I'm not joking, read it! \n\n#### Functions should be actions or interrogations\n\nPlease favor longer names for small scope, lower names for large scopes.\n\n```js\n// BAD\nobject.author()\nobject.veryBad()\nobject.closing()\nobject.mkSmtGdAbtIt()\n```\n\n```js\n// GOOD\nobject.getAuthor()\nobject.isGood()\nobject.close()\nobject.makeSomethingGoodAboutIt()\n```\n\n#### Variable names should be substantives or interrogations\n\n```js\n// BAD\nlet making;\nlet made;\n```\n\n```js\n// GOOD\nlet isMaking;\nlet isMade;\n```\n\n#### Functions should be small\n\nA function should not be larger than 10 lines. Honestly, if you have that, then it must be refactored. Exceptions being, large switch cases, Pure Components and render() functions from React.\n\n#### Indent with 4 spaces\n\nWith JS and with JSX.\nWhy 4? Because it forces you to decouple and makes your spaghetti much more visible and annoying!\nPlus it feels that the code is more airy and readable.\n\nWhat about callback hell and stuff ?\n1. Use promises, or the sexy async/await syntax\n2. Decouple your callbacks\n\nThis is opinionated, there is no wrong way to indent, there are other factors far more important.\n\n#### Classes hide in large functions\n\nThere are cases where you have a single function as service, and that function begins to grow,\nand you find that decoupling it in multiple functions gets tedious because you will need to pass\nmany variables along, and you realize that those variables are strings or numbers and can't be properly modified,\nso you need to return them, and even if your intentions are pure, your code will become\nmore messy.\n\nThe rule is simple: when it's difficult to decouple a function, create a class. Because inside a class, each method\nhas access to `this` context, so you no longer need to pass variables along.\n\n#### Don't extend classes\n\nAnother opinionated advice, just don't use extend. The only exception is if the class you extend\nis abstract (you don't instantiate/use it stand-alone).\n\n#### Provide JSDoc and Comments\n\nWhenever you feel like you are doing something that is not verbose, leave a comment, and also leave JSDoc, especially for \nvariables. It becomes truly helpful when you are using an API written by someone else and the IDE shows you which variables it\nneeds and a description for them.\n\nIf you find a snippet, or something that describes a certain thing, don't be afraid to leave links,\nespecially if you're writing an facade to an external API, leave link to the docs, it will save you and other devs\ntime.\n\nLeave as much comments as you can, but don't go off the grid, stay to the point. And only leave comments when necessary:\n\n```js\n// BAD (The code already tells you what you do)\n// We are iterating through users \nusers.forEach(user => {});\n```\n```js\n// GOOD (Even if you can read it nicely, you need to understand the intention fast) \n// We are calculating the total cost of all products so we can use it in total calculation cost.\nlet totalCost = 0;\nproducts.forEach(product => {\n totalCost += product.cost;\n})\n```\n\n\n## Dependency Injection\n\nI've looked around the NPM community and everything is too complex for what we really need.\n\nInversion principle is simple, let the service decide what dependencies it has. We are kinda doing this already\nbecause each module imports what it needs in order to execute properly.\n\nThe reason for changing it a bit is the fact that it will allow easy testing. For example, we want to test that `PaymentService.charge` is been called when something happens.\n\n```js\nimport PaymentService from 'somewhere';\n\nclass ItemService {\n static createItem(item) {\n Items.insert(item);\n PaymentService.charge(item.userId, 20.00);\n }\n}\n\nexport default ItemService;\n```\n\nNow in my test how would I be sure that `PaymentService.charge` is called without actually altering `PaymentService`?\nThis requires a change in strategy, by injecting dependencies in the constructor:\n\n```js\nimport PaymentService from 'somewhere';\n\nclass ItemService {\n constructor({paymentService}) {\n this.paymentService = paymentService;\n }\n \n createItem(item) {\n Items.insert(item);\n this.paymentService.charge(item.userId, 20.00);\n }\n}\n\nexport default new ItemService({\n paymentService: PaymentService\n});\n\nexport {ItemService}\n```\n\nOk now if we would like to test it, we have access to the exported variable: `ItemService` and we can pass-in a [stub](http://sinonjs.org/releases/v4.0.1/stubs/) for `PaymentService` in its constructor.\n\nThe only problem with this pattern is the fact that it creates an instance without you wanting to,\ntherefore you can create a separate `ServiceModel` file (ItemServiceModel.js) and instantiate (and export) it inside `ItemService.js`, and when you test\nyou only need to play with `ItemServiceModel`\n\n\n\n\n \n \n\n\n","date":"2018-03-05T20:41:32.070Z","updated":"2018-03-05T20:41:32.070Z","path":"chapters/3/services.html","_id":"cj9twby87000b9ajx7yksmyy3","comments":1,"layout":"page","content":"A service is a unit of work (a function) or a group of very related functionality (a class)
\nUsing services is linked to the Single responsibility principle
\n1 | Q: So when do we need to use services? |
Ok we’re done here, everything is clear now, right?
\nJokes aside, you should treat every functionality in your app as a service, for example:\nYou want to send an email, and ofcourse, you know how to do this, you use Email.send. Well, Email is actually a service, but now how you send it in your app ?
\nLet’s say you create a Post, and you need to notify the Admin to approve it.\nWhere do you store that unit of logic ? Inside a Meteor method ? Inside a function ? Inside a class ?
\nThe answer is: NOT inside the Meteor method
\nUsually, you tend to couple logic in your Meteor methods which is a very very bad terrible thing, because Meteor methods act as server-side routes, they act as the C (Controller) in MVC, \nthe C does not contain logic, the C is a boss that delegates work to M (Services aka Models)
\nIt is also bad because methods are a communication layer between the client and the server, they shouldn’t store business logic, or logic of any kind, imagine them as proxies that communicate with your services.
\nLet’s start focusing about services in depth, and understand some nice principles about\ncrafting them in a nice way.
\nDon’t forget to read: https://github.com/ryanmcdermott/clean-code-javascript
\n1 | class ItemService { |
In most cases, you want your services to be a class with static methods, and not an instance of that class new ItemService()
, however\nwe’ll see below why in some cases using the instance makes more sense.
By default we are going to put them inside:\n/imports/api/{module}/services
Usually {module} represents the collection name it handles it, but this is not always the case, it can represent something that is handling multiple collections to achieve its goal.
\nIf your services need more decoupling feel free to nest them:\n/imports/api/{module}/services/{submodule}
If you are writing HTTP APIs separate the Service of fetching/persisting data and put it in /imports/db/{module}
and\nthe one that does include the app/business logic keep it in /imports/api/{module}
. The one inside /imports/api
should instantiate the one from /imports/db
with the correct API keys (most likely retrieved from Meteor.settings
)
Name your services with uppercase if classes (ItemService), or lowercase if functions (doSomething).\nIf your service is a class, suffix it with service, if it’s a function, make the sure the filename is a verb.
\nInside a function service module, you can create additional functions, but you must export only one, if you need to\nexport multiple functions, it will become a service class with static methods.
\nGo API first. Don’t try to think about the logic, try to think about how you are going to use it. This is why TDD works so well, because\nit lets you think about the API and you also write a test, and passing tests means you finished implementation.
\nSo, instead of patterns, try to think about how you would use it, and just write a test for it first. You will notice that your development speed will increase dramatically (after first tries). No kidding!
\nAgain, don’t forget to read: https://github.com/ryanmcdermott/clean-code-javascript. I’m not joking, read it!
\nPlease favor longer names for small scope, lower names for large scopes.
\n1 | // BAD |
1 | // GOOD |
1 | // BAD |
1 | // GOOD |
A function should not be larger than 10 lines. Honestly, if you have that, then it must be refactored. Exceptions being, large switch cases, Pure Components and render() functions from React.
\nWith JS and with JSX.\nWhy 4? Because it forces you to decouple and makes your spaghetti much more visible and annoying!\nPlus it feels that the code is more airy and readable.
\nWhat about callback hell and stuff ?
\nThis is opinionated, there is no wrong way to indent, there are other factors far more important.
\nThere are cases where you have a single function as service, and that function begins to grow,\nand you find that decoupling it in multiple functions gets tedious because you will need to pass\nmany variables along, and you realize that those variables are strings or numbers and can’t be properly modified,\nso you need to return them, and even if your intentions are pure, your code will become\nmore messy.
\nThe rule is simple: when it’s difficult to decouple a function, create a class. Because inside a class, each method\nhas access to this
context, so you no longer need to pass variables along.
Another opinionated advice, just don’t use extend. The only exception is if the class you extend\nis abstract (you don’t instantiate/use it stand-alone).
\nWhenever you feel like you are doing something that is not verbose, leave a comment, and also leave JSDoc, especially for \nvariables. It becomes truly helpful when you are using an API written by someone else and the IDE shows you which variables it\nneeds and a description for them.
\nIf you find a snippet, or something that describes a certain thing, don’t be afraid to leave links,\nespecially if you’re writing an facade to an external API, leave link to the docs, it will save you and other devs\ntime.
\nLeave as much comments as you can, but don’t go off the grid, stay to the point. And only leave comments when necessary:
\n1 | // BAD (The code already tells you what you do) |
1 | // GOOD (Even if you can read it nicely, you need to understand the intention fast) |
I’ve looked around the NPM community and everything is too complex for what we really need.
\nInversion principle is simple, let the service decide what dependencies it has. We are kinda doing this already\nbecause each module imports what it needs in order to execute properly.
\nThe reason for changing it a bit is the fact that it will allow easy testing. For example, we want to test that PaymentService.charge
is been called when something happens.
1 | import PaymentService from 'somewhere'; |
Now in my test how would I be sure that PaymentService.charge
is called without actually altering PaymentService
?\nThis requires a change in strategy, by injecting dependencies in the constructor:
1 | import PaymentService from 'somewhere'; |
Ok now if we would like to test it, we have access to the exported variable: ItemService
and we can pass-in a stub for PaymentService
in its constructor.
The only problem with this pattern is the fact that it creates an instance without you wanting to,\ntherefore you can create a separate ServiceModel
file (ItemServiceModel.js) and instantiate (and export) it inside ItemService.js
, and when you test\nyou only need to play with ItemServiceModel
A service is a unit of work (a function) or a group of very related functionality (a class)
\nUsing services is linked to the Single responsibility principle
\n1 | Q: So when do we need to use services? |
Ok we’re done here, everything is clear now, right?
\nJokes aside, you should treat every functionality in your app as a service, for example:\nYou want to send an email, and ofcourse, you know how to do this, you use Email.send. Well, Email is actually a service, but now how you send it in your app ?
\nLet’s say you create a Post, and you need to notify the Admin to approve it.\nWhere do you store that unit of logic ? Inside a Meteor method ? Inside a function ? Inside a class ?
\nThe answer is: NOT inside the Meteor method
\nUsually, you tend to couple logic in your Meteor methods which is a very very bad terrible thing, because Meteor methods act as server-side routes, they act as the C (Controller) in MVC, \nthe C does not contain logic, the C is a boss that delegates work to M (Services aka Models)
\nIt is also bad because methods are a communication layer between the client and the server, they shouldn’t store business logic, or logic of any kind, imagine them as proxies that communicate with your services.
\nLet’s start focusing about services in depth, and understand some nice principles about\ncrafting them in a nice way.
\nDon’t forget to read: https://github.com/ryanmcdermott/clean-code-javascript
\n1 | class ItemService { |
In most cases, you want your services to be a class with static methods, and not an instance of that class new ItemService()
, however\nwe’ll see below why in some cases using the instance makes more sense.
By default we are going to put them inside:\n/imports/api/{module}/services
Usually {module} represents the collection name it handles it, but this is not always the case, it can represent something that is handling multiple collections to achieve its goal.
\nIf your services need more decoupling feel free to nest them:\n/imports/api/{module}/services/{submodule}
If you are writing HTTP APIs separate the Service of fetching/persisting data and put it in /imports/db/{module}
and\nthe one that does include the app/business logic keep it in /imports/api/{module}
. The one inside /imports/api
should instantiate the one from /imports/db
with the correct API keys (most likely retrieved from Meteor.settings
)
Name your services with uppercase if classes (ItemService), or lowercase if functions (doSomething).\nIf your service is a class, suffix it with service, if it’s a function, make the sure the filename is a verb.
\nInside a function service module, you can create additional functions, but you must export only one, if you need to\nexport multiple functions, it will become a service class with static methods.
\nGo API first. Don’t try to think about the logic, try to think about how you are going to use it. This is why TDD works so well, because\nit lets you think about the API and you also write a test, and passing tests means you finished implementation.
\nSo, instead of patterns, try to think about how you would use it, and just write a test for it first. You will notice that your development speed will increase dramatically (after first tries). No kidding!
\nAgain, don’t forget to read: https://github.com/ryanmcdermott/clean-code-javascript. I’m not joking, read it!
\nPlease favor longer names for small scope, lower names for large scopes.
\n1 | // BAD |
1 | // GOOD |
1 | // BAD |
1 | // GOOD |
A function should not be larger than 10 lines. Honestly, if you have that, then it must be refactored. Exceptions being, large switch cases, Pure Components and render() functions from React.
\nWith JS and with JSX.\nWhy 4? Because it forces you to decouple and makes your spaghetti much more visible and annoying!\nPlus it feels that the code is more airy and readable.
\nWhat about callback hell and stuff ?
\nThis is opinionated, there is no wrong way to indent, there are other factors far more important.
\nThere are cases where you have a single function as service, and that function begins to grow,\nand you find that decoupling it in multiple functions gets tedious because you will need to pass\nmany variables along, and you realize that those variables are strings or numbers and can’t be properly modified,\nso you need to return them, and even if your intentions are pure, your code will become\nmore messy.
\nThe rule is simple: when it’s difficult to decouple a function, create a class. Because inside a class, each method\nhas access to this
context, so you no longer need to pass variables along.
Another opinionated advice, just don’t use extend. The only exception is if the class you extend\nis abstract (you don’t instantiate/use it stand-alone).
\nWhenever you feel like you are doing something that is not verbose, leave a comment, and also leave JSDoc, especially for \nvariables. It becomes truly helpful when you are using an API written by someone else and the IDE shows you which variables it\nneeds and a description for them.
\nIf you find a snippet, or something that describes a certain thing, don’t be afraid to leave links,\nespecially if you’re writing an facade to an external API, leave link to the docs, it will save you and other devs\ntime.
\nLeave as much comments as you can, but don’t go off the grid, stay to the point. And only leave comments when necessary:
\n1 | // BAD (The code already tells you what you do) |
1 | // GOOD (Even if you can read it nicely, you need to understand the intention fast) |
I’ve looked around the NPM community and everything is too complex for what we really need.
\nInversion principle is simple, let the service decide what dependencies it has. We are kinda doing this already\nbecause each module imports what it needs in order to execute properly.
\nThe reason for changing it a bit is the fact that it will allow easy testing. For example, we want to test that PaymentService.charge
is been called when something happens.
1 | import PaymentService from 'somewhere'; |
Now in my test how would I be sure that PaymentService.charge
is called without actually altering PaymentService
?\nThis requires a change in strategy, by injecting dependencies in the constructor:
1 | import PaymentService from 'somewhere'; |
Ok now if we would like to test it, we have access to the exported variable: ItemService
and we can pass-in a stub for PaymentService
in its constructor.
The only problem with this pattern is the fact that it creates an instance without you wanting to,\ntherefore you can create a separate ServiceModel
file (ItemServiceModel.js) and instantiate (and export) it inside ItemService.js
, and when you test\nyou only need to play with ItemServiceModel
So, we discussed about many concepts and we described where to put them. But the thing is,\nit doesn’t really matter that much, what we describe here are a set of practices, there’s no such thing\nas the best way, or the way. Whatever you choose, be consistent and try to document it.
\nTo give you an overall image, it will look something like this:
\n
|
|
|
|
So, we discussed about many concepts and we described where to put them. But the thing is,\nit doesn’t really matter that much, what we describe here are a set of practices, there’s no such thing\nas the best way, or the way. Whatever you choose, be consistent and try to document it.
\nTo give you an overall image, it will look something like this:
\n
|
|
|
|
Because your code grows and you make less mistakes.\nBecause it’s often faster for you to write a service and test it in a simple unit-test, rather then going on browser for manual testing.
\nThere are so many articles about why testing is mandatory. Just take my word for it.
\nThis article assumes you thoroughly read: https://guide.meteor.com/testing.html
\nIf you decoupled your code nicely, it’s so easy to write tests:
\n
|
|
|
|
Now you can easily do in your terminal: npm run test
. Then open your browser on http://localhost:3050 to see the results,\nthey will be updated live as you write your test.
Test files should end in *.test.js
and they should have a parent (not necessarily direct) with name client
or server
\ndepending where we want to run them.
|
|
Whenever someone does a Pull-Request, you can see if the code breaks some tests or not. This is invaluable, and so easy to implement.\nReally!
\nSome good CI tools:
\n\n","site":{"data":{}},"excerpt":"","more":"Because your code grows and you make less mistakes.\nBecause it’s often faster for you to write a service and test it in a simple unit-test, rather then going on browser for manual testing.
\nThere are so many articles about why testing is mandatory. Just take my word for it.
\nThis article assumes you thoroughly read: https://guide.meteor.com/testing.html
\nIf you decoupled your code nicely, it’s so easy to write tests:
\n
|
|
|
|
Now you can easily do in your terminal: npm run test
. Then open your browser on http://localhost:3050 to see the results,\nthey will be updated live as you write your test.
Test files should end in *.test.js
and they should have a parent (not necessarily direct) with name client
or server
\ndepending where we want to run them.
|
|
Whenever someone does a Pull-Request, you can see if the code breaks some tests or not. This is invaluable, and so easy to implement.\nReally!
\nSome good CI tools:
\n\n"},{"title":"Testing","description":"It's time to handle file uploads right","disqusPage":"Chapter 3: Uploads","_content":"\n\n\n","source":"chapters/3/uploads.md","raw":"---\ntitle: 'Testing'\ndescription: It's time to handle file uploads right\ndisqusPage: 'Chapter 3: Uploads'\n---\n\n\n\n","date":"2017-11-10T12:45:17.021Z","updated":"2017-11-10T12:45:17.021Z","path":"chapters/3/uploads.html","comments":1,"layout":"page","_id":"cj9twby89000e9ajx3t9etf6u","content":"","site":{"data":{}},"excerpt":"","more":""},{"title":"Cascade Style Sheet (CSS)","description":"Styling your Meteor application","disqusPage":"Chapter 2: CSS","_content":"\n## Introduction\n\n\nThe style is an important aspect of an application. Anyone can write css, but there are some subtle things that makes the difference between a good written css and a bad one.\n\nWe choose not to use a framework, but we are going to guide you throughout the process of building the base css you'll need in most of your projects.\n\nFor this tutorial, we are going to use [SASS (SCSS)](http://sass-lang.com/), because it's an amazing tool that offers some features that plain css misses that could improve your productivity right away and help you modularize your code. For example: variables, mixins, import, functions and many other cool features.\n\n\n## Setting Up\n\n```bash\nmeteor add fourseven:scss\nmeteor remove standard-minifier-css // meteor installs this by default to optimize the css\nmeteor add juliancwirko:postcss // for a better optimization of css\nmeteor npm install --save-dev autoprefixer css-mqpacker postcss-csso\n```\n\nWe are using [PostCSS](http://postcss.org/) because it makes our life easier by using some of it's plugins:\n- [Autoprefixer](https://github.com/postcss/autoprefixer) taking out our responsability to prefix our css rules for each browser.\n- [CSS MQPacker](https://github.com/hail2u/node-css-mqpacker) pack some CSS media query rules into one using PostCSS into one and adding them at the end of css file\n- [CSSO](https://github.com/css/csso) is a CSS minifier that compresses and cleans our code\n\nTo configure your newly installed plugins, you need to add this in package json:\n\n``` json\n\"postcss\": {\n \"plugins\": {\n \"autoprefixer\": {\n \"browsers\": [\n \"last 2 versions\",\n \"> 2%\"\n ]\n },\n \"css-mqpacker\": {},\n \"postcss-csso\": {}\n }\n }\n\n```\n\n## Styles Folder Structure\nThis is the basic folder structure we are using and recommend\n\n\n├── client \n│ ├── styles\n│ │ ├── cc-app.scss // Main file where we import all the other scss files\n│ │ └── _helpers // in this folder we add all files for variables, functions, mixins \n| │ │ └── _functions.scss // functions that we use in mixins \n| │ │ └── _helpers.scss // general rules all over the website\n| │ │ └── _mixins.scss // a group of declarations that you want to re-use\n| │ │ └── _variables.scss\n│ │ └── base \n| │ │ └── _normalize.scss // css reset for the browser \n| │ │ └── _font.scss // rules for importing external fonts \n| │ │ └── _general.scss // general rules for elements (html, body, links, a, etc)\n│ │ └── elements\n| │ │ └── grid.scss\n│ │ └── form \n| │ │ └── _button.scss\n| │ │ └── _checkbox.scss \n| │ │ └── _radio.scss\n| │ │ └── _text.scss\n| │ │ └── _textarea.scss\n| │ │ └── _select.scss\n| │ │ └── _form-helpers.scss\n│ │ └── layout \n| │ │ └── header\n| │ │ └── footer\n| │ │ └── main\n│ │ └── pages // a folder in which we add the css files for each page of application\n│ │ └── plugins // // in this folder we add the files from our external components that we use in the app\n│ ├── ...\n├── imports\n│ ├── ...\n└── server \n └── ...\n\n\nYou can find a pre-installed meteor project with this folder structure, [here](https://github.com/cult-of-coders/meteor-tuts-boilerplate)\n\n\n## Use of Flexbox\nTo structure our layout model, we are using CSS3 Flexbile Box because it solves some problems that the layout float model have:\n- vertical and horizontal centering\n- same height columns\n\nYou can learn more about it, [here](https://css-tricks.com/snippets/css/a-guide-to-flexbox/)\n\n## Use of Rem for font size\nWe are using rem size for our fonts because it offers responsiveness, scalability, improved reading experience, and greater flexibility in defining components. To read more about it, click [here](https://www.sitepoint.com/understanding-and-using-rem-units-in-css/) \n\n## Writing clean css\nIt is extremely important to write clean css code so that we have some recommendations for you:\n- When you create an application, prefix your classes with something specific and short. For example, we chose for our company \"cc\" (Cult of Coders). The reason behind it is that if we get an external library, we won't any have any conflicts\n- Use 3 or less levels of css nesting. Most of the time you don't need more than one level because if you want to override those styles only on a specific elements, you don't have to recreate the whole nesting levels\n- Using !important in your CSS usually means you're narcissistic & selfish or lazy. Respect the devs to come and don't use it!\n- For each component (or page / section) create a separate file in which you'll store only the specific styles for that section. This will make the code much cleaner, offering better readability and very easy to modify later \n- Naming standard convention. Our current recommendation is [BEM](http://getbem.com/naming/)\n- Be specific in naming your classes. The class names should represent your elements\n\n```scss\n/* BAD */\n.box {\n background-color: green !important;\n .box2 {\n .cc-box3 {\n font-size: 5px;\n .cc-button-x {\n color: red; \n }\n }\n }\n} \n.cc-div {}\n```\n\n```scss\n/* GOOD */\n.cc-donut {\n &--bg-main{\n background-color: $color-main;\n color: $color-white;\n }\n}\n.cc-donuts-list {\n height: 100%;\n display: flex;\n \n &__title {\n @include font-size(16px);\n }\n}\n```\n\n## Use of Variables\nVariables are simply amazing because you can store some general rules that you want to apply in your stylesheet.\n\n```scss\n$font-roboto: 'Roboto', sans-serif;\n$base-font-size: 62.5%;\n$color-success: #4CAF50;\n$shadow-dp0: 0px 0px 0px 0px rgba(black, 0.2), 0px 0px 0px 0px rgba(black, 0.14), 0px 0px 0px 0px rgba(black, 0.12);\n\n/* How to use them? */\n.cc-donuts-list {\n font-family: $font-roboto;\n color: $color-success;\n box-shadow: $shadow-dp0;\n}\n```\n\n## Use of mixins\nA mixin lets you make groups of CSS declarations that you want to reuse throughout your site\n```scss\n@mixin breakpoint ($value, $min-value: false) {\n @if $value == 'phone-small' {\n @media only screen and (max-width : 330px) { @content; }\n }\n\n @if $value == 'phone' {\n @media only screen and (max-width : 750px) { @content; }\n }\n\n @else if $value == 'tablet' {\n @media only screen and (min-width: 751px) and (max-width: 1199px) { @content }\n }\n\n @else if $value == 'desktop' {\n @media only screen and (min-width: 1601px) { @content }\n }\n\n @else {\n @media only screen and (max-width: $value) { @content; }\n }\n}\n\n/* How to use it? */\n.cc-notification {\n @include breakpoint(\"tablet\") {\n font-size: 50px;\n }\n}\n\n```\n## Homework\n\n#### 1. Donut colors\nStyle the section where we display the donuts. Add to each element from the section, a specific class. Each type of donut, will have a different color.\n\n#### 2. Responsive donuts\nThe creation of a donut needs to be responsive so we can add it from each device we would like \n","source":"chapters/2/css.md","raw":"---\ntitle: 'Cascade Style Sheet (CSS)'\ndescription: Styling your Meteor application\ndisqusPage: 'Chapter 2: CSS'\n---\n\n## Introduction\n\n\nThe style is an important aspect of an application. Anyone can write css, but there are some subtle things that makes the difference between a good written css and a bad one.\n\nWe choose not to use a framework, but we are going to guide you throughout the process of building the base css you'll need in most of your projects.\n\nFor this tutorial, we are going to use [SASS (SCSS)](http://sass-lang.com/), because it's an amazing tool that offers some features that plain css misses that could improve your productivity right away and help you modularize your code. For example: variables, mixins, import, functions and many other cool features.\n\n\n## Setting Up\n\n```bash\nmeteor add fourseven:scss\nmeteor remove standard-minifier-css // meteor installs this by default to optimize the css\nmeteor add juliancwirko:postcss // for a better optimization of css\nmeteor npm install --save-dev autoprefixer css-mqpacker postcss-csso\n```\n\nWe are using [PostCSS](http://postcss.org/) because it makes our life easier by using some of it's plugins:\n- [Autoprefixer](https://github.com/postcss/autoprefixer) taking out our responsability to prefix our css rules for each browser.\n- [CSS MQPacker](https://github.com/hail2u/node-css-mqpacker) pack some CSS media query rules into one using PostCSS into one and adding them at the end of css file\n- [CSSO](https://github.com/css/csso) is a CSS minifier that compresses and cleans our code\n\nTo configure your newly installed plugins, you need to add this in package json:\n\n``` json\n\"postcss\": {\n \"plugins\": {\n \"autoprefixer\": {\n \"browsers\": [\n \"last 2 versions\",\n \"> 2%\"\n ]\n },\n \"css-mqpacker\": {},\n \"postcss-csso\": {}\n }\n }\n\n```\n\n## Styles Folder Structure\nThis is the basic folder structure we are using and recommend\n\n
\n├── client \n│ ├── styles\n│ │ ├── cc-app.scss // Main file where we import all the other scss files\n│ │ └── _helpers // in this folder we add all files for variables, functions, mixins \n| │ │ └── _functions.scss // functions that we use in mixins \n| │ │ └── _helpers.scss // general rules all over the website\n| │ │ └── _mixins.scss // a group of declarations that you want to re-use\n| │ │ └── _variables.scss\n│ │ └── base \n| │ │ └── _normalize.scss // css reset for the browser \n| │ │ └── _font.scss // rules for importing external fonts \n| │ │ └── _general.scss // general rules for elements (html, body, links, a, etc)\n│ │ └── elements\n| │ │ └── grid.scss\n│ │ └── form \n| │ │ └── _button.scss\n| │ │ └── _checkbox.scss \n| │ │ └── _radio.scss\n| │ │ └── _text.scss\n| │ │ └── _textarea.scss\n| │ │ └── _select.scss\n| │ │ └── _form-helpers.scss\n│ │ └── layout \n| │ │ └── header\n| │ │ └── footer\n| │ │ └── main\n│ │ └── pages // a folder in which we add the css files for each page of application\n│ │ └── plugins // // in this folder we add the files from our external components that we use in the app\n│ ├── ...\n├── imports\n│ ├── ...\n└── server \n └── ...\n\n\nYou can find a pre-installed meteor project with this folder structure, [here](https://github.com/cult-of-coders/meteor-tuts-boilerplate)\n\n\n## Use of Flexbox\nTo structure our layout model, we are using CSS3 Flexbile Box because it solves some problems that the layout float model have:\n- vertical and horizontal centering\n- same height columns\n\nYou can learn more about it, [here](https://css-tricks.com/snippets/css/a-guide-to-flexbox/)\n\n## Use of Rem for font size\nWe are using rem size for our fonts because it offers responsiveness, scalability, improved reading experience, and greater flexibility in defining components. To read more about it, click [here](https://www.sitepoint.com/understanding-and-using-rem-units-in-css/) \n\n## Writing clean css\nIt is extremely important to write clean css code so that we have some recommendations for you:\n- When you create an application, prefix your classes with something specific and short. For example, we chose for our company \"cc\" (Cult of Coders). The reason behind it is that if we get an external library, we won't any have any conflicts\n- Use 3 or less levels of css nesting. Most of the time you don't need more than one level because if you want to override those styles only on a specific elements, you don't have to recreate the whole nesting levels\n- Using !important in your CSS usually means you're narcissistic & selfish or lazy. Respect the devs to come and don't use it!\n- For each component (or page / section) create a separate file in which you'll store only the specific styles for that section. This will make the code much cleaner, offering better readability and very easy to modify later \n- Naming standard convention. Our current recommendation is [BEM](http://getbem.com/naming/)\n- Be specific in naming your classes. The class names should represent your elements\n\n```scss\n/* BAD */\n.box {\n background-color: green !important;\n .box2 {\n .cc-box3 {\n font-size: 5px;\n .cc-button-x {\n color: red; \n }\n }\n }\n} \n.cc-div {}\n```\n\n```scss\n/* GOOD */\n.cc-donut {\n &--bg-main{\n background-color: $color-main;\n color: $color-white;\n }\n}\n.cc-donuts-list {\n height: 100%;\n display: flex;\n \n &__title {\n @include font-size(16px);\n }\n}\n```\n\n## Use of Variables\nVariables are simply amazing because you can store some general rules that you want to apply in your stylesheet.\n\n```scss\n$font-roboto: 'Roboto', sans-serif;\n$base-font-size: 62.5%;\n$color-success: #4CAF50;\n$shadow-dp0: 0px 0px 0px 0px rgba(black, 0.2), 0px 0px 0px 0px rgba(black, 0.14), 0px 0px 0px 0px rgba(black, 0.12);\n\n/* How to use them? */\n.cc-donuts-list {\n font-family: $font-roboto;\n color: $color-success;\n box-shadow: $shadow-dp0;\n}\n```\n\n## Use of mixins\nA mixin lets you make groups of CSS declarations that you want to reuse throughout your site\n```scss\n@mixin breakpoint ($value, $min-value: false) {\n @if $value == 'phone-small' {\n @media only screen and (max-width : 330px) { @content; }\n }\n\n @if $value == 'phone' {\n @media only screen and (max-width : 750px) { @content; }\n }\n\n @else if $value == 'tablet' {\n @media only screen and (min-width: 751px) and (max-width: 1199px) { @content }\n }\n\n @else if $value == 'desktop' {\n @media only screen and (min-width: 1601px) { @content }\n }\n\n @else {\n @media only screen and (max-width: $value) { @content; }\n }\n}\n\n/* How to use it? */\n.cc-notification {\n @include breakpoint(\"tablet\") {\n font-size: 50px;\n }\n}\n\n```\n## Homework\n\n#### 1. Donut colors\nStyle the section where we display the donuts. Add to each element from the section, a specific class. Each type of donut, will have a different color.\n\n#### 2. Responsive donuts\nThe creation of a donut needs to be responsive so we can add it from each device we would like \n","date":"2018-03-05T20:41:32.064Z","updated":"2018-03-05T20:41:32.064Z","path":"chapters/2/css.html","_id":"cj9twfqpk000f9ajxxnp74lt3","comments":1,"layout":"page","content":"
The style is an important aspect of an application. Anyone can write css, but there are some subtle things that makes the difference between a good written css and a bad one.
\nWe choose not to use a framework, but we are going to guide you throughout the process of building the base css you’ll need in most of your projects.
\nFor this tutorial, we are going to use SASS (SCSS), because it’s an amazing tool that offers some features that plain css misses that could improve your productivity right away and help you modularize your code. For example: variables, mixins, import, functions and many other cool features.
\n1 | meteor add fourseven:scss |
We are using PostCSS because it makes our life easier by using some of it’s plugins:
\nTo configure your newly installed plugins, you need to add this in package json:
\n1 | \"postcss\": { |
This is the basic folder structure we are using and recommend
\n\n├── client \n│ ├── styles\n│ │ ├── cc-app.scss // Main file where we import all the other scss files\n│ │ └── _helpers // in this folder we add all files for variables, functions, mixins \n| │ │ └── _functions.scss // functions that we use in mixins \n| │ │ └── _helpers.scss // general rules all over the website\n| │ │ └── _mixins.scss // a group of declarations that you want to re-use\n| │ │ └── _variables.scss\n│ │ └── base \n| │ │ └── _normalize.scss // css reset for the browser \n| │ │ └── _font.scss // rules for importing external fonts \n| │ │ └── _general.scss // general rules for elements (html, body, links, a, etc)\n│ │ └── elements\n| │ │ └── grid.scss\n│ │ └── form \n| │ │ └── _button.scss\n| │ │ └── _checkbox.scss \n| │ │ └── _radio.scss\n| │ │ └── _text.scss\n| │ │ └── _textarea.scss\n| │ │ └── _select.scss\n| │ │ └── _form-helpers.scss\n│ │ └── layout \n| │ │ └── header\n| │ │ └── footer\n| │ │ └── main\n│ │ └── pages // a folder in which we add the css files for each page of application\n│ │ └── plugins // // in this folder we add the files from our external components that we use in the app\n│ ├── ...\n├── imports\n│ ├── ...\n└── server \n └── ...\n\n\n
You can find a pre-installed meteor project with this folder structure, here
\nTo structure our layout model, we are using CSS3 Flexbile Box because it solves some problems that the layout float model have:
\nYou can learn more about it, here
\nWe are using rem size for our fonts because it offers responsiveness, scalability, improved reading experience, and greater flexibility in defining components. To read more about it, click here
\nIt is extremely important to write clean css code so that we have some recommendations for you:
\n1 | /* BAD */ |
1 | /* GOOD */ |
Variables are simply amazing because you can store some general rules that you want to apply in your stylesheet.
\n1 | $font-roboto: 'Roboto', sans-serif; |
A mixin lets you make groups of CSS declarations that you want to reuse throughout your site\n1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28@mixin breakpoint ($value, $min-value: false) {
@if $value == 'phone-small' {
@media only screen and (max-width : 330px) { @content; }
}
@if $value == 'phone' {
@media only screen and (max-width : 750px) { @content; }
}
@else if $value == 'tablet' {
@media only screen and (min-width: 751px) and (max-width: 1199px) { @content }
}
@else if $value == 'desktop' {
@media only screen and (min-width: 1601px) { @content }
}
@else {
@media only screen and (max-width: $value) { @content; }
}
}
/* How to use it? */
.cc-notification {
@include breakpoint(\"tablet\") {
font-size: 50px;
}
}
Style the section where we display the donuts. Add to each element from the section, a specific class. Each type of donut, will have a different color.
\nThe creation of a donut needs to be responsive so we can add it from each device we would like
\n","site":{"data":{}},"excerpt":"","more":"The style is an important aspect of an application. Anyone can write css, but there are some subtle things that makes the difference between a good written css and a bad one.
\nWe choose not to use a framework, but we are going to guide you throughout the process of building the base css you’ll need in most of your projects.
\nFor this tutorial, we are going to use SASS (SCSS), because it’s an amazing tool that offers some features that plain css misses that could improve your productivity right away and help you modularize your code. For example: variables, mixins, import, functions and many other cool features.
\n1 | meteor add fourseven:scss |
We are using PostCSS because it makes our life easier by using some of it’s plugins:
\nTo configure your newly installed plugins, you need to add this in package json:
\n1 | \"postcss\": { |
This is the basic folder structure we are using and recommend
\n\n├── client \n│ ├── styles\n│ │ ├── cc-app.scss // Main file where we import all the other scss files\n│ │ └── _helpers // in this folder we add all files for variables, functions, mixins \n| │ │ └── _functions.scss // functions that we use in mixins \n| │ │ └── _helpers.scss // general rules all over the website\n| │ │ └── _mixins.scss // a group of declarations that you want to re-use\n| │ │ └── _variables.scss\n│ │ └── base \n| │ │ └── _normalize.scss // css reset for the browser \n| │ │ └── _font.scss // rules for importing external fonts \n| │ │ └── _general.scss // general rules for elements (html, body, links, a, etc)\n│ │ └── elements\n| │ │ └── grid.scss\n│ │ └── form \n| │ │ └── _button.scss\n| │ │ └── _checkbox.scss \n| │ │ └── _radio.scss\n| │ │ └── _text.scss\n| │ │ └── _textarea.scss\n| │ │ └── _select.scss\n| │ │ └── _form-helpers.scss\n│ │ └── layout \n| │ │ └── header\n| │ │ └── footer\n| │ │ └── main\n│ │ └── pages // a folder in which we add the css files for each page of application\n│ │ └── plugins // // in this folder we add the files from our external components that we use in the app\n│ ├── ...\n├── imports\n│ ├── ...\n└── server \n └── ...\n\n\n
You can find a pre-installed meteor project with this folder structure, here
\nTo structure our layout model, we are using CSS3 Flexbile Box because it solves some problems that the layout float model have:
\nYou can learn more about it, here
\nWe are using rem size for our fonts because it offers responsiveness, scalability, improved reading experience, and greater flexibility in defining components. To read more about it, click here
\nIt is extremely important to write clean css code so that we have some recommendations for you:
\n1 | /* BAD */ |
1 | /* GOOD */ |
Variables are simply amazing because you can store some general rules that you want to apply in your stylesheet.
\n1 | $font-roboto: 'Roboto', sans-serif; |
A mixin lets you make groups of CSS declarations that you want to reuse throughout your site\n1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28@mixin breakpoint ($value, $min-value: false) {
@if $value == 'phone-small' {
@media only screen and (max-width : 330px) { @content; }
}
@if $value == 'phone' {
@media only screen and (max-width : 750px) { @content; }
}
@else if $value == 'tablet' {
@media only screen and (min-width: 751px) and (max-width: 1199px) { @content }
}
@else if $value == 'desktop' {
@media only screen and (min-width: 1601px) { @content }
}
@else {
@media only screen and (max-width: $value) { @content; }
}
}
/* How to use it? */
.cc-notification {
@include breakpoint(\"tablet\") {
font-size: 50px;
}
}
Style the section where we display the donuts. Add to each element from the section, a specific class. Each type of donut, will have a different color.
\nThe creation of a donut needs to be responsive so we can add it from each device we would like
\n"}],"Post":[],"PostAsset":[],"PostCategory":[],"PostTag":[],"Tag":[]}} \ No newline at end of file diff --git a/package-lock.json b/package-lock.json deleted file mode 100644 index 72ff0a0..0000000 --- a/package-lock.json +++ /dev/null @@ -1,13 +0,0 @@ -{ - "name": "hexo-site", - "version": "0.0.0", - "lockfileVersion": 1, - "requires": true, - "dependencies": { - "prettier": { - "version": "1.14.2", - "resolved": "https://registry.npmjs.org/prettier/-/prettier-1.14.2.tgz", - "integrity": "sha512-McHPg0n1pIke+A/4VcaS2en+pTNjy4xF+Uuq86u/5dyDO59/TtFZtQ708QIRkEZ3qwKz3GVkVa6mpxK/CpB8Rg==" - } - } -} diff --git a/package.json b/package.json index 1bb798c..3d379a7 100644 --- a/package.json +++ b/package.json @@ -3,7 +3,7 @@ "version": "0.0.0", "private": true, "hexo": { - "version": "3.6.0" + "version": "3.7.1" }, "dependencies": { "canonical-json": "0.0.4", @@ -31,4 +31,4 @@ "deploy": "hexo-s3-deploy", "start": "hexo server" } -} +} \ No newline at end of file diff --git a/source/recipes/queries.md b/source/recipes/queries.md index c4761be..f0ca1a2 100644 --- a/source/recipes/queries.md +++ b/source/recipes/queries.md @@ -151,7 +151,7 @@ And inside your resolver: } ``` -Now if you go to your [GraphQL Playground](http://localhost:3000/graphql) you can run this: +Now if you go to http://localhost:3000/graphql you can run this: ``` query { diff --git a/source/starterpack/deployment.md b/source/starterpack/deployment.md index 4e881a9..6538484 100644 --- a/source/starterpack/deployment.md +++ b/source/starterpack/deployment.md @@ -8,7 +8,7 @@ disqusPage: 'Starterpack:Deployment' ## PM2 Meteor -You can setup your server on AWS or DigitalOcean. We recommend you use `Ubuntu 16.04` to quickly deploy without hassles. +You can setup your server on [AWS](https://aws.amazon.com) or [DigitalOcean](https://www.digitalocean.com/). We recommend you use `Ubuntu 16.04` to quickly deploy without hassles. We have the ability to quickly deploy our apps. One of the easiest way to do it, is to use `pm2-meteor`: diff --git a/source/starterpack/security.md b/source/starterpack/security.md index 1032b24..61fd86b 100644 --- a/source/starterpack/security.md +++ b/source/starterpack/security.md @@ -112,7 +112,7 @@ export default { }; ``` -Pretty straight forward right ? The reason we do it like this, by centralizing security in one place, +Pretty straight forward right? The reason we do it like this, by centralizing security in one place, is to remove boilerplate code inside our methods and keep separation of concerns. You can do it however you want it, there is no right or wrong way to do it, depends on your use-case, but we believe that it is easier to maintain, and newly onboarded developers were writing secure code right from the start! diff --git a/source/starterpack/setting-up.md b/source/starterpack/setting-up.md index 072eae4..ae8dc7f 100644 --- a/source/starterpack/setting-up.md +++ b/source/starterpack/setting-up.md @@ -23,7 +23,7 @@ meteor npm i -S react-apollo apollo-live-client apollo-client apollo-cache-inmem # If you're looking into Server Side Rendering with React meteor npm i -S react-dom react-router apollo-link-schema -# Now we add the package +# Now we add the package for a plug and play zero-config graphql server with meteor and other helpful features meteor add cultofcoders:apollo # Optional but highly recommended (so you can import .gql/.graphql files) @@ -45,11 +45,11 @@ Add the following to `package.json`, it will allow us to specify the entry point ``` ``` -mkdir -p src/startup/client src/startup/server src/api -touch src/startup/client/index.js src/startup/server/index.js src/api/index.js +mkdir -p src/startup/client src/startup/server src/api tests +touch src/startup/client/index.js src/startup/server/index.js src/api/index.js tests/main.js ``` -Now go to `src/startup/server/index.js` and add this: +Now go to `src/startup/server/apollo.js` and add this in order to initialize GraphQL: ```js // src/startup/server/apollo.js @@ -79,6 +79,7 @@ load({ ``` ```js +// src/startup/server/index.js import '../../api'; import './apollo'; ``` @@ -101,11 +102,11 @@ query { ## Module Aliases -In addition to what we just described, you can also create module aliases so: +In addition to what we just described, you can also create module aliases so run the following command: -`npm i --save-dev babel-plugin-module-resolver` +`meteor npm i --save-dev babel-plugin-module-resolver` -In `.babelrc` +Create a file `.babelrc` in the root folder ``` { diff --git a/source/starterpack/structure.md b/source/starterpack/structure.md index dd53571..f5577ed 100644 --- a/source/starterpack/structure.md +++ b/source/starterpack/structure.md @@ -23,7 +23,7 @@ We will introduce to following folders: Our GraphQL schema will exist in: `src/api` and it is composed of `entities` and `modules`. An entity is a type, but since type is such a generic word in JS world, we are going to refer to them as entities. ```gql -# entities/User.gql +# src/api/entities/User.gql type User { _id: ID! firstName: String @@ -33,7 +33,7 @@ type User { ``` ```js -// entites/User.resolvers.js +// src/api/entites/User.resolvers.js export default { User: { fullName: user => `${user.firstName} ${user.lastName}`, @@ -44,7 +44,7 @@ export default { And the place where we aggregate them all: ```js -// entities/index.js +// src/api/entities/index.js import UserType from './User.gql'; import UserResolvers from './User.resolvers.js'; @@ -61,7 +61,7 @@ export default { We regard as a GraphQL module an object that contains typeDefs and/or resolvers. Using the `graphql-load` package this allows us to work with these modules and allows us to think about them logically separated. -In our case inside `entities/index.js` what we export is a GraphQL Module. +In our case inside `src/api/entities/index.js` what we export is a GraphQL Module. So to aggregate and use that, we'll create our entry point for loading inside: @@ -79,7 +79,7 @@ load([EntitiesModule, UserModule, PostModule]); A sample implementation of the `post` module inside `src/api/modules/post`: ``` -# ./typeDefs.gql +# src/api/modules/post/typeDefs.gql type Query { posts: [Post] } @@ -90,7 +90,7 @@ type Mutation { ``` ```js -// ./resolvers.js +// src/api/modules/post/resolvers.js export default { Query: { posts: () => { @@ -235,7 +235,7 @@ Posts.attachSchema(PostSchema); ``` ```js -// src/db/posts/index.js +// src/db/index.js import Posts from './posts/collection'; export { Posts };