Basics of MeanJS
The first thing I’ve noticed with MeanJS is the amazing Documentation.
For people familiar with the concepts of MVC (Model, View, Controller), the folder structure of MeanJS should be really easy to understand.
Models
Mongoose (built in) makes the part of Model building a lot easier, and very easy to read.
For people who are use to building the DB architecture first, then generating Models from it, I haven’t found a solution for that (and I think that because of the nature of MongoDB it’s a bit difficult working that way). I suggest starting to work from Models to DB instead.
The following Model is for an Article object, and is generated by MeanJS after install.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
var ArticleSchema = new Schema({
created: {
type: Date,
default: Date.now
},
title: {
type: String,
default: '',
trim: true,
required: 'Title cannot be blank'
},
content: {
type: String,
default: '',
trim: true
},
user: {
type: Schema.ObjectId,
ref: 'User'
}
});
mongoose.model('Article', ArticleSchema);
As you can see, the fields created, title, content, user are described here, each with its own type/default/error message/reference.
The types of fields (=SchemaTypes) that can be defined are
- String
- Number
- Date
- Buffer
- Boolean
- Mixed
- ObjectId
- Array
Mongoose has some good explanation about it here.
Notice that user is referencing another Model!
MongoDB doesn’t really have joins, but mongoose helps you to translate what you know and love from SQL to NoSQL by having these Model relationships (just like in other MVCs).
Controllers
For those who are familiar with Controllers in general, there’s not much new to introduce here.
Every Controller (that uses a Model) should require the Models you want to use:
1
2
var mongoose = require('mongoose'),
Article = mongoose.model('Article');
(for articles.server.controller.js)
These are standard Express controllers, every Action in a Controller is a function inside the exports (in NodeJS that’s the object that’s returned when the controller is required), for example the Article/List action:
1
2
3
4
5
6
7
8
9
10
11
exports.list = function(req, res) {
Article.find().sort('-created').populate('user', 'displayName').exec(function(err, articles) {
if (err) {
return res.send(400, {
message: getErrorMessage(err)
});
} else {
res.jsonp(articles);
}
});
};
As you can imagine, req & res are the request & response accordingly.
The first line describes a pretty straight forward query which fetches all Articles, sorted by creation date ascending.
The populate function helps “joining” (there aren’t real joins on MongoDB, so Mongoose gives us Population) the Users collection (MySQL->table MongoDB->collection) with the Article.
Afterwards we execute the query, check for errors and respond accordingly, otherwise return a JSON response (luckily MongoDB stores everything as JSON anyways, so this is incredibly fast).
Routes
Like in every MVC framework, routes define the different url routes that are used, and to which controller/action they go to. To better understand routes lets take a look at an example generated by MeanJS:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
var users = require('../../app/controllers/users'),
articles = require('../../app/controllers/articles');
module.exports = function(app) {
// Article Routes
app.route('/articles')
.get(articles.list)
.post(users.requiresLogin, articles.create);
app.route('/articles/:articleId')
.get(articles.read)
.put(users.requiresLogin, articles.hasAuthorization, articles.update)
.delete(users.requiresLogin, articles.hasAuthorization, articles.delete);
// Finish by binding the article middleware
app.param('articleId', articles.articleByID);
};
First we require the different controllers to which we’ll be routing (otherwise actions like articles.list won’t be defined).
As always we’re using modules.exports (in controller was simply referred to as exports, both should state the same in my opinion).
As you can probably gather, .get/.post/.put/.delete are HTTP methods.
Tests
I’m going to try and cover tests in the future, since there are frameworks both for Backend & Frontend testing, I’d like to cover this separately.
Backend Views
The views that are on the backend side are for errors pages (404, 500, etc.), and general layouts.
There’s a good explanation for why the developer decided to have some views basic views on the backend side.
The backend views use Swig templating engine, which reminds me of Django’s templating language (or many others). For the frontend views we’re using AngularJS’ templating (so no need to mix the two).
Debug / Inspect
Probably one of the most crucial things you’d like to do is inspecting the app.
node inspector is a great tool, that’s very similar to chrome’s web inspector, and let’s you inspect your application thoroughly.
make sure you have it installed
1
sudo npm install -g node-inspector
then simply instead of running grunt (that’s what you should execute when running the MeanJS app), run grunt debug and go to the following URL: http://localhost:1337/debug?port=5858
The ports are the default ones, which can be changes on gruntfile.js
Goodbye Backend, Hello Frontend!
I'm sure you've noticed that the controllers & models had .server in their filename. This to make a distinction between backend & frontend files, which can be harder to see with the MEAN. Frontend files have .client in their filename for the same reason.
On to the public/ folder!
Lib
Angular stuff, Bootstrap & jQuery, the base libraries. Nothing much to add.
Modules
Each Model has its own Module, remember we’re in AngularJS realm now.
Let’s look at articles/ which has
-
config -
-
articles.client.config.js - holds the configuration (duh!) of the articles module, in this example we’re adding some menu items:
1 2 3 4 5 6 7 8
angular.module('articles').run(['Menus', function(Menus) { // Set top bar menu items Menus.addMenuItem('topbar', 'Articles', 'articles', 'dropdown', '/articles(/create)?'); Menus.addSubMenuItem('topbar', 'articles', 'List Articles', 'articles'); Menus.addSubMenuItem('topbar', 'articles', 'New Article', 'articles/create'); } ]);
-
articles.client.routes.js - configuring routing for the module, pay attention because here we’re talking about the routing for AngularJS, this means after the Hash sign (#), example: http://localhost:3000/#!/articles/create
-
-
controllers - loading the variables from the backend’s controller, making some form logic.
-
services - various services, for instance Authentication service for the users module (
-
tests - as mentioned above, tests will be covered in a separate article as I think they deserve one.
-
views - this is where the familiar AngularJS views go (with things like data-ng-repeat, data-ng-bind and so on).
I hope this clarifies a bit about the folder structure of MeanJS, it can definitely be overwhelming in the beginning but one has to understand that every tool has a purpose.
Even more important - not all tools will cost you in performance (as many of them are for the development stage like SASS), if anything the performance will be a lot better than in your LAMP stack.
Next article will cover the testing frameworks (unless I have a tough week, in which case something shorter :) ).