Notes about full stack Javascript software development

Menu

Handling data in MEAN.io with Mongoose.js

Brace yourselves, long post coming, but it matters. MEAN.io is a fully functioning app right out of the box, it amounts to a platform which allows user authentication and authoring articles which then appear in an index. It all works miraculously well, a second after you’ve installed it and booted your Node server, but what are the odds that it’s the app that you need? Virtually none, of course. You can, though, with limited experience alter the app to suit other needs. For this you need to understand what happens to data from its collection from the user by Angular on the client-side to its storage in MongoDB. This, I think, is actually the coolest part of the MEAN stack. You can push the exact same Javascript object from the client to the database without any difficulties or translations into other languages. The missing link which greatly facilitates this process is a beast called Mongoose.js.

Mongoose brands itself as an “elegant mongodb object modeling for node.js“. It is what lets you quite easily from a node app connect to a Mongo database, model and define your data objects, store and find them in the db and much, much more. For this reason Mongoose is what the authors of MEAN.io rightfully so chose to use.

This is how it works:

MEAN is MVC on the client-side and MVC on the server side, so it has models, its models on the server-side are mongoose models, and for that before they are created, mongoose is needed so it brought into the module with Node’s require:

var mongoose = require('mongoose')

In server.js then mongoose is used to connect to the MongoDB defined in the db part of the global config

// Bootstrap db connection
var db = mongoose.connect(config.db)

The config specifies mongodb as the type of database we are using, localhost as the server and mean-dev as the database we are saving to. This reads with great brevity as follows

db: 'mongodb://localhost/mean-dev',

Then here in /app/models/article.js a Schema variable is declared as a Mongoose schema.

This is quite self explanatory, but in case you are unfamiliar. We are instantiating a new mongoose schema object so that we inherit all of its handy methods and each of the new object’s custom new fields is defined with another object so that we give that field a definition – is it a string, is it a date, is it an id, does it have a default and what is it, should white space be trimmed etc. All of these amounts to something you may be used to doing when starting your SQL database, but here the schema is defined in the actual code logic, not in the database itself. While it is similar to SQL schemas, this is much more flexible as schemas in SQL are notoriously cumbersome hard to change and often have to cause down time for the whole software application.

The user: {type : Schema.ObjectId, ref : ‘User’} is a bit more peculiar and amounts to the MongoDB way of handling joins. It tells mongoose that the user field in the Article model is an id which matches a document in the User collection.

Mongoose schemas go further though and can actually define methods to your data objects as well. These are called statics. From the Mongoose doc – Schemas not only define the structure of your document and casting of properties, they also define document instance methods, static Model methods, compound indexes and document lifecycle hooks called middleware.

This explains the following load method for the Article model defined in the schema.

Remember this load static method as it plays an important role in loading the data.

We are now ready to create the actual model.

mongoose.model('Article', ArticleSchema);

Within mongoose a model by the name of Article as defined by the ArticleSchema is created, very easy right It is very important to note that unless set otherwise in the schema, mongoose will use the name of the model as name of the collection in your MongoDB server. So Article models will be stored in the articles collection.

We can verify this with Robomongo, a neat client for Mongo:

This model is then bootstrapped globally for this app in the server.js file – the one Node actually runs to start the whole app.

Here the fs.readdirSync method is used to return a list of the files in the /app/models folder and a forEach loop requires them all into the global memory for this app.

Now the models are ready to be used by the controllers which are in charge of the server side logic.If you look at the articles controller you will notice it makes use of both the Article and User models.

This article function from the articles controller is actually used in router.js with app.param which maps a route parameter to an actual article. An amazingly cool feature of express which is explained here.

app.param('articleId', articles.article)

This articleId param is then used by the get, put, del requests to the articles controller.

If you look at the handler functions like for example articles.show, you will see that req.article holds the article data, thanks to the mapping of the load method to the articleId param. So you can just go ahead and just return it with jsonp as a response. Again, very cool, very convenient.

exports.show = function(req, res){
res.jsonp(req.article);
}

The delete request calls the destroy function which calls the remove method of the article model we’re working with and wipes it out from the db.

And there you have it, we have analyzed in detail the role which Mongoose.js plays in mean.io – it connects to the database, defines the models, loads them, creates, reads, update and deletes them from the db which means that it essentially handles the whole connectivity between MongoDB and Express – the server-side of your app. Next we will analyze how this data is handled on the client-side by Angular.