Back-end7 minute read

Developer's Guide to Improving Project Structure in Meteor Applications

Meteor is a framework for rapidly building real-time JavaScript web applications. It can be used to build complex applications with amazing ease. However, that can often result in adoption of bad development practices and poorly structured code.

In this article, Toptal engineer Darion Cassel shares some simple ways to improve the structure of your next Meteor-based web application without resorting to complicated boilerplates and scaffolding tools.


Toptalauthors are vetted experts in their fields and write on topics in which they have demonstrated experience. All of our content is peer reviewed and validated by Toptal experts in the same field.

Meteor is a framework for rapidly building real-time JavaScript web applications. It can be used to build complex applications with amazing ease. However, that can often result in adoption of bad development practices and poorly structured code.

In this article, Toptal engineer Darion Cassel shares some simple ways to improve the structure of your next Meteor-based web application without resorting to complicated boilerplates and scaffolding tools.


Toptalauthors are vetted experts in their fields and write on topics in which they have demonstrated experience. All of our content is peer reviewed and validated by Toptal experts in the same field.
Darion Cassel
Verified Expert in Engineering

Darion has 3+ years of experience building web applications in JavaScript & Python. He has worked for companies like Rackspace, & CommVault.

PREVIOUSLY AT

Google
Share

Over the past few years, there has been a dramatic increase in the number of JavaScript frameworks available. Ranging from the tried-and-true AngularJS to the score of frameworks that come out every month, there is an impressive diversity to choose from. One that caught my attention a few years ago is a framework called Meteor. Unlike most frameworks, Meteor aims to be a complete platform for JavaScript app development. For those who are new to Meteor, I encourage you to check it out on their website. This article will not be an introduction to Meteor. Instead, we will explore some simple ways to introduce structure in Meteor projects.

A Developer's Guide to Improving Project Structure in Meteor Framework

A Developer's Guide to Improving Project Structure in Meteor Framework

One of the great benefits of Meteor is that it is very easy to rapidly prototype complex JavaScript applications with it. Because Meteor is a hybrid of front-end templating and rendering coupled with a Node-based server interacting with MongoDB (a unified solution), most of the initial setup necessary for the creation of a full-stack web app is already done for you. However, the ease of development that this provides can be a trap. It is easy to fall into bad practices and end up with a jumble of unstructured code when building a Meteor app. I have a few suggestions for how this can be avoided.

Scaffolding: Manageable Directory Hierarchy in Meteor

First, it is important to maintain the recommended folder structure when building out your application. Meteor allows you to place files anywhere within the project folder by default - you can even have all of your code in a single file if you desire. While this might work for a hackathon project, the complexity incurred by usual production-level, scalable apps is hard to manage without a sound structure.

In order to solve this problem, I recommend checking out Chris Mather’s npm package iron. The package has a variety of configuration options so it will be hard to describe here, but in general it constructs a project structure that will look something like this:

my-app/
|- .iron/
   |- config.json
|- bin/
|- build/
|- config/
   |- development/
      |- env.sh
      |- settings.json
|- app/
   |- client/
      |- collections/
      |- lib/
      |- stylesheets/
      |- templates/
      |- head.html
   |- lib/
      |- collections/
      |- controllers/
      |- methods.js
      |- routes.js
   |- packages/
   |- private/
   |- public/
   |- server/
      |- collections/
      |- lib
      |- methods.js
      |- publish.js
      |- bootstrap.js

However, for some projects a folder and file structure like this can be an overkill. Not every project will need to have this fine-grained a level of organization, with separations for collections, methods, and publish code on the server. For those who don’t have too large of a project or simply don’t want to have to install and learn another npm package, I recommend this basic folder structure:

The key elements are a public folder that contains files like your favicon and other static assets, and the client, common, and server folders. The client and server folders should (of course) contain code that is executed on the client and server respectively. The common folder contains code that must be accessible to both the client and the server. An example of this is Schema code, which we will discuss in a bit.

There are two ways to perform the lowest level of organization: one is by file type, and the second is by function. Organization by file type means that in your client/templates folder, for example, you will have three folders - one for JavaScript files, one for CSS, and one for HTML. The HTML folder will contain all of your template HTML files, for example Login.html, Main.html, and so on. The JavaScript and CSS folders will contain template files of their type, respectively. Organization by function, on the other hand, means organizing by the concept that the files embody. For example, in client/templates, I would have a folder “Login”, with all the JavaScript, CSS, and HTML files associated with the app’s login process. I prefer organization by function as it allows you to be more clear about where you can find certain files or pieces of code. However, it’s not purely black and white, and most individuals and teams hit upon some mixture of these methods to structure their files and folders.

Schema: Your App Needs It Even If Your Database Doesn’t

The second kind of structure that I’d like to discuss is associated with the database. This article assumes that you’re using MongoDB. If you aren’t, the concepts will probably still apply but the specifics won’t. Those using MongoDB know that it allows the way we store our data to be unstructured. Since MongoDB is a NoSQL document-store database, there is no fixed schema for any “type” of data. This freedom means that you don’t have to worry about making sure that all of the objects are standardized to some rigid form, in fact, all of your app’s data could be thrown into a single collection if you desired. However, when it comes to making production-quality apps, this isn’t really as desireable. In order to manage this and add useful features such as validation of write-requests, we can turn to two wonderful Meteor packages: Aldeed’s SimpleSchema and Collection2.

The SimpleSchema package, as the name suggests, allows you to reactively validate objects in your app. Check out the docs on GitHub. The Collection2 package bootstraps off of SimpleSchema and allows you to bring proper schemas to Meteor collections. What this enables is automatic validation of client and server-side write requests to any collection with a schema attached to it. Both of these packages are very deep and customizable so it’s hard to give a complete understanding of them in a few paragraphs. Rather, I recommend you look at the detailed readmes that Aldeed has compiled for the specifics. I’ll simply talk about how I got value out of these packages. One of the best things that they enabled was validation of user’s form input. This comes in handy for validating Meteor User documents (from the Accounts package). By default, Meteor Users have a surprisingly complex implicit schema. Here’s a picture of a part of it from the code that Aldeed was so kind to make available:

Schema.UserProfile = new SimpleSchema({
    firstName: {
        type: String,
        optional: true
    },
    lastName: {
        type: String,
        optional: true
    },
    birthday: {
        type: Date,
        optional: true
    },
    gender: {
        type: String,
        allowedValues: ['Male', 'Female'],
        optional: true
    },
    organization : {
        type: String,
        optional: true
    },
    website: {
        type: String,
        regEx: SimpleSchema.RegEx.Url,
        optional: true
    },
    bio: {
        type: String,
        optional: true
    },
    country: {
        type: Schema.UserCountry,
        optional: true
    }
});

That’s simply the schema for the User’s profile object. Attempting to validate all of the User object would be a mess without a purpose-built package like SimpleSchema . While all of those fields appear optional in the picture, if you wanted a properly validated User schema they probably wouldn’t be, and things like “Schema.UserCountry” actually redirect to other schemas for validation. By attaching this schema to the User object and integrating this reactively to our forms, perhaps with a package like Aldeed’s AutoForm, we can gain a fine degree of control over what does and doesn’t make it into our databases, saving time and effort with an conceptual model of how data is handled in our application that can be pointed to and discussed on concrete terms.

Roles: For the 401 and 403s

The final suggestion I have for structuring and improving a Meteor project is Alanning’s Roles package. This is an authorization system for Meteor, and it allows you to check user access levels for any part of your web app. Permissions are attached to the User profile in the form of strings which are later validated or invalidated when the user tries to access any Meteor methods or data published to the client. For example:

if (Roles.userIsInRole(Meteor.userId(), "admin")) {
     tabsArr.push({
            name: "Users",
            slug: "users"
      });
 }

Although the core of the system is relatively simple, it allows for complex and fine-grained permissions for any part of your application. Since all of the roles are stored as strings, you can set up a system that is as deep as you like - “admin”, “admin.division1.manage”, “admin.division1.manage.group2”, and so on.

The trouble with the freedom that this package allows is that it can be hard to keep track of a highly granular roles system. The package does provide functions such as “getAllRoles” which, as the name suggests, retrieves all of the roles that you’ve created, but it’s up to you to keep track of what all of their meanings are, and when a given role should be used. And for those who are curious what the difference between “roles” and “permissions” are, for the purposes of this package they are essentially no different.

Wrapping Up

Unfortunately, because of the breadth of the article (each of the packages mentioned here deserves its own tutorial) it was not possible to go into detail about any particular package, but I hope I shed some light on how you can work towards a “standardized” Meteor application that you can be confident will work well in production and at scale. If you’re looking for more information, check out the links I posted and take a look at one more thing that didn’t make it to this article, but is useful to have in an Meteor app: the Restivus package, which allows you to easily expose a REST API for your app.

As a disclaimer, I am not the author of any of these packages, and I apologize if I have misrepresented any of the features or aims of any package. Thank you for reading, and I hope this article was of benefit to you. Feel free to contact me with any questions you may have, or leave a comment below.

Hire a Toptal expert on this topic.
Hire Now
Darion Cassel

Darion Cassel

Verified Expert in Engineering

Pittsburgh, PA, United States

Member since January 8, 2016

About the author

Darion has 3+ years of experience building web applications in JavaScript & Python. He has worked for companies like Rackspace, & CommVault.

authors are vetted experts in their fields and write on topics in which they have demonstrated experience. All of our content is peer reviewed and validated by Toptal experts in the same field.

PREVIOUSLY AT

Google

World-class articles, delivered weekly.

Subscription implies consent to our privacy policy

World-class articles, delivered weekly.

Subscription implies consent to our privacy policy

Join the Toptal® community.