{"id":15395708,"url":"https://github.com/jdnichollsc/meteor-starter-template","last_synced_at":"2025-04-16T00:11:27.307Z","repository":{"id":74465116,"uuid":"74637868","full_name":"jdnichollsc/Meteor-Starter-Template","owner":"jdnichollsc","description":"A template to start with Meteor","archived":false,"fork":false,"pushed_at":"2017-01-05T05:05:12.000Z","size":236,"stargazers_count":5,"open_issues_count":0,"forks_count":3,"subscribers_count":3,"default_branch":"master","last_synced_at":"2025-04-16T00:11:21.781Z","etag":null,"topics":["application","meteor","meteor-apps","meteor-framework","meteor-starter-template","meteorjs","mongodb","nosql","nosql-database","real-time","realtime","template","website"],"latest_commit_sha":null,"homepage":null,"language":"JavaScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/jdnichollsc.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2016-11-24T04:28:49.000Z","updated_at":"2019-07-15T22:26:17.000Z","dependencies_parsed_at":"2023-07-30T02:16:16.396Z","dependency_job_id":null,"html_url":"https://github.com/jdnichollsc/Meteor-Starter-Template","commit_stats":{"total_commits":41,"total_committers":1,"mean_commits":41.0,"dds":0.0,"last_synced_commit":"403f692ddd480d6a017590c0fc1ee43f60a6f98f"},"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jdnichollsc%2FMeteor-Starter-Template","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jdnichollsc%2FMeteor-Starter-Template/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jdnichollsc%2FMeteor-Starter-Template/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jdnichollsc%2FMeteor-Starter-Template/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/jdnichollsc","download_url":"https://codeload.github.com/jdnichollsc/Meteor-Starter-Template/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":249173086,"owners_count":21224483,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["application","meteor","meteor-apps","meteor-framework","meteor-starter-template","meteorjs","mongodb","nosql","nosql-database","real-time","realtime","template","website"],"created_at":"2024-10-01T15:29:15.188Z","updated_at":"2025-04-16T00:11:27.288Z","avatar_url":"https://github.com/jdnichollsc.png","language":"JavaScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Meteor Starter Template\nA template to start with Meteor. It includes a summary to understand the most important features and how to work with Meteor in the correct way.\n\n![Meteor.js](images/meteor.jpeg)\n\n## Distributed Data Protocol - DDP\nA protocol for communication between clients and the server. Sends the data via EJSON (a JSON implementation) that supports more types.\n - Publish and subscribe\n - Remote procedure calls\n\n# Meteor Structure\nPath         | Explanation\n----------   | -------------\n`./client/`  | Runs on client only.\n`./server/`  | Runs on server only.\n`./private/` | Assets for server code only.\n`./public/`  | Static assets, fonts, images, etc.\n`./lib/`     | Runs before everything else.\n`./test/`    | Doesn't run anywhere.\n`./**/**`    | Runs on client and server.\n`main.*`     | Runs after everything else.\n\n# Publications and Subscriptions\nTo control the publications we need to remove the default auto-publishing package:\n```cmd\nmeteor remove autopublish\n```\nIn the **server** we configure that we will publish to our clients\n```javascript\nMeteor.publish('posts', function(currentAuthor) {\n  return Posts.find({ author: currentAuthor });\n});\n```\nAnd in the **client** we subscribe to the publications\n```javascript\nMeteor.subscribe('posts', 'jdnichollsc');\n```\n\n# Helpers\nWe can use the helpers to get data on the client\n```javascript\nTemplate.posts.helpers({\n  recentPosts: function(){\n    return Posts.find({ createdAt: { $gte : moment().subtract(1, 'days').startOf('day') } });\n  }\n});\n```\nWe can exclude certain properties to get only what is needed from the server\n```javascript\nMeteor.publish('allPosts', function(currentAuthor){\n  return Posts.find({ author: currentAuthor }, {fields: {\n    date: false\n  }});\n});\n```\n\n# Routes (You can use the [new router system](https://github.com/kadirahq/flow-router))\nThe **Iron Router** package allows us to configure routing in the application, to use filters and manage subscriptions.\n```cmd\nmeteor add iron:router\n```\nWe can create a dynamic zone to show the current route using **layouts** and the **yield** helper.\n\n\u003e **./client/views/layout.html**\n********************************\n\n```html\n\u003ctemplate name=\"layout\"\u003e\n  \u003cdiv class=\"container\"\u003e\n    {{\u003e yield}}\n  \u003c/div\u003e\n\u003c/template\u003e\n```\n\nAnd we can configure the routes of our application\n\u003e **./lib/router.js**\n*********************\n\n```javascript\nRouter.configure({\n  layoutTemplate: 'layout'\n});\nRouter.route('/', {name: 'authors'});\n```\n\nThe **Iron Router** has a helper to generate links dynamically\n```html\n\u003ca href=\"{{pathFor 'authors'}}\"\u003eAuthors\u003c/a\u003e\n```\nPre-loading data and showing templates\n```javascript\nRouter.configure({\n  layoutTemplate: 'layout',\n  loadingTemplate: 'loading',\n  notFoundTemplate: 'notFound',\n  waitOn: function() { return Meteor.subscribe('posts'); }\n});\n```\nWe can use parameters in the routes to load data\n```javascript\nRouter.route('/posts/:_id', {\n  name: 'postPage',\n  data: function() { return Posts.findOne(this.params._id); }\n});\n```\n\n# Session\nIt is a global store of reactive data, a central communication bus for different parts of the application.\n* Set a value\n```javascript\nSession.set('pageTitle', 'A different title');\n```\n* Get a value\n```javascript\nSession.get('pageTitle');\n```\n\n# Reactive blocks\nIt is a block of code that is executed when the data changes.\n```javascript\nTracker.autorun(function() {\n alert(Session.get('message'));\n});\n//Or when Meteor has loaded the collections\nMeteor.startup(function() {\n Tracker.autorun(function() {\n  console.log('There are ' + Posts.find().count() + ' posts');\n });\n});\n```\n\nAnd in the client side we can use the **observe** function to execute callbacks.\n```javascript\nPosts.find().observe({\n  added: function(post) { },\n  changed: function(post) { },\n  removed: function(post) { }\n});\n```\n\n# User authentication\nWe can add some packages to handle an account system\n```cmd\n//meteor add accounts-ui\nmeteor add ian:accounts-ui-bootstrap-3\nmeteor add accounts-password\n```\nAnd include the **loginButtons** helper in the template that you want\n\u003e **./client/views/layout.html**\n********************************\n```html\n\u003ctemplate name=\"header\"\u003e\n  \u003cnav class=\"navbar navbar-default\" role=\"navigation\"\u003e\n    \u003cdiv class=\"collapse navbar-collapse\" id=\"navigation\"\u003e\n      \u003cul class=\"nav navbar-nav navbar-right\"\u003e\n        {{\u003e loginButtons}}\n      \u003c/ul\u003e\n    \u003c/div\u003e\n  \u003c/nav\u003e\n\u003c/template\u003e\n```\n\nTo see our users we can use the **users** collection\n```javascript\nMeteor.users.find().count();\n```\n\n# Security\nWe need to remove the **insecure** package to handle the security **(Prevent the anonymous actions)**\n```cmd\nmeteor remove insecure\n```\nIf we want to allow actions only from **authenticated users**, we can modify the rules of the collections using **allow** and **deny** functions.\n```javascript\nPosts.allow({\n  insert: function(userId, doc) {\n    // only allow posting if you are logged in\n    return !! userId;\n  }\n});\n```\nAlso we can modify the rules to update and remove documents created only by the owner user\n\u003e **./lib/permissions.js**\n**************************\n```javascript\n// check that the userId specified owns the documents\nownsDocument = function(userId, doc) {\n  return doc \u0026\u0026 doc.userId === userId;\n}\n```\n\u003e **./collections/posts.js**\n****************************\n```javascript\nPosts.allow({\n  update: ownsDocument,\n  remove: ownsDocument\n});\n```\n\nWe can indicate only the fields that the user can modify\n```javascript\nPosts.deny({\n  update: function(userId, post, fieldNames) {\n    // may only edit the following two fields:\n    return (_.without(fieldNames, 'url', 'title').length \u003e 0);\n  }\n});\n```\n\n# Events (Client side)\nWe can create events listeners to save data, redirect the users, etc from the **client** side.\n```javascript\nTemplate.postSubmit.events({\n  'submit form': function(e) {\n    e.preventDefault();\n    var $target = $(e.target);\n    var post = {\n      url: $target.find('[name=url]').val(),\n      title: $target.find('[name=title]').val()\n    };\n\n    post._id = Posts.insert(post);\n    Router.go('postPage', post);\n  },\n  'click #myButton': function(e){\n    //We can execute server methods\n    Meteor.call('addAuthors', { name: 'Nicholls' }, function(error, result) {\n      if (error){\n        console.log(error.reason);\n      }\n      else{\n        console.log(\"Redirect user...\");\n      }\n    });\n    return false;\n  }\n});\n```\n\n## Local Collections\nWe can create collections only in the client, for example to show a list of errors\n\u003e **./client/helpers/errors.js**\n********************************\n```javascript\nErrors = new Mongo.Collection(null);\nthrowError = function(message) {\n  Errors.insert({message: message});\n};\n```\n\nAnd we can remove the error after some time of having been rendered in the browser\n\u003e **./client/views/errors.js**\n******************************\n```javascript\nTemplate.error.onRendered(function() {\n  var error = this.data;\n  Meteor.setTimeout(function () {\n    Errors.remove(error._id);\n  }, 3000);\n});\n//OR ONLY CREATED\nTemplate.error.onCreated(function() {\n  //...\n});\n```\n\n# Methods (Server side)\nAre functions executed from the **server** side to prevent user attacks.\n```javascript\nMeteor.methods({\n  'addAuthors'({ name, birthdate }) {\n    new SimpleSchema({\n      name: { type: String },\n      birthdate: { type: Date }\n    }).validate({ name, birthdate });\n\n    if (name === 'admin') {\n      throw new Meteor.Error(\"You can't create an author with the name admin\");\n    }\n    //...\n  }\n});\n```\n\n# Template helpers\n* For each:\n```html\n{{#each widgets}}\n  {{\u003e widgetItem}}\n{{/each}}\n```\n\n* Use an object property to load templates:\n```html\n{{#with myWidget}}\n  {{\u003e widgetPage}}\n{{/with}}\n//OR MORE EASY...\n{{\u003e widgetPage myWidget}}\n```\n\n* Show only if the user is authenticated (**currentUser** is a helper from the **accounts** package):\n```html\n{{#if currentUser}}\n \u003ca href=\"{{pathFor 'postSubmit'}}\"\u003eSubmit Post\u003c/a\u003e\n{{/if}}\n```\n\n# Hooks\n* Show a template when the route is invalid:\n```javascript\nRouter.onBeforeAction('dataNotFound', {only: 'postPage'});\n```\n* Prevent the access to the routes from anonymous users:\n```javascript\nRouter.route('/submit', {name: 'postSubmit'});\nvar requireLogin = function() {\n  if (! Meteor.user()) {\n    if (Meteor.loggingIn()) {\n      this.render(this.loadingTemplate);\n    } else {\n      this.render('accessDenied');\n    }\n  } else {\n    this.next();\n  }\n};\nRouter.onBeforeAction(requireLogin, {only: 'postSubmit'});\n```\n\u003e **./client/views/accessDenied.html**\n**************************************\n```javascript\n\u003ctemplate name=\"accessDenied\"\u003e\n  \u003cdiv class=\"access-denied page\"\u003e\n    \u003ch2\u003eAccess Denied\u003c/h2\u003e\n    \u003cp\u003ePlease log in.\u003c/p\u003e\n  \u003c/div\u003e\n\u003c/template\u003e\n```\n\n# Packages\n\n### Spinner\nWe can add a package to create a loading template\n```cmd\nmeteor add sacha:spin\n```\nAnd using the **spinner** helper\n\n\u003e **./client/views/loading.html**\n*********************************\n```html\n\u003ctemplate name=\"loading\"\u003e\n  {{\u003espinner}}\n\u003c/template\u003e\n```\n\n### Check\nA package to validate types and structure of variables.\n```cmd\nmeteor add check\n```\nAnd we can check objects to validate\n```javascript\nMeteor.methods({\n  postInsert: function(postAttributes) {\n    check(Meteor.userId(), String);\n    check(postAttributes, {\n      title: String,\n      url: String\n    });\n    \n    var user = Meteor.user();\n    //...\n  }\n});\n```\n\n# Meteor utilities\n\nUtility                      | Action\n---------------------------  | -------------\n`Meteor.isClient`            | Check if the current code is executed from the client side\n`Meteor.isServer`            | Check if the current code is executed from the server side\n`Meteor._sleepForMs(5000)`   | Wait for 5 seconds\n\n\n# Packages commands\n\nCommand                                       | Action\n--------------------------------------------  | -------------\n`meteor`                                      | Runs meteor app\n`meteor list`                                 | Show packages\n`meteor shell`                                | Access to server code\n`meteor mongo`                                | Access to the database\n`meteor create app_name`                      | Create meteor app\n`meteor create --package jdnichollsc:errors`  | Create meteor package\n`meteor add package_name`                     | Add meteor packages\n`meteor remove package_name`                  | Remove meteor packages\n`meteor reset`                                | Delete the database and reset the project\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fjdnichollsc%2Fmeteor-starter-template","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fjdnichollsc%2Fmeteor-starter-template","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fjdnichollsc%2Fmeteor-starter-template/lists"}