How to structure your NodeJS Models

In MVC your models are objects that save out to the data store and do basic low level manipulations on your data. Over the past few years I’ve worked with many different ways of structuring models and have finally come to a solution I feel is elegant, simple and easy to understand. It’s the exact structure I use for Tower Storm and it’s based mainly off the way the mongoose npm package works.

Here’s an example of the most common model of all time: User. The database functions in these examples are all psudocode but should be pretty self explanitory as to what they’re doing. You can get a copy of the source code at https://github.com/timjrobinson/nodejsmodels

The trouble with writing models is sometimes you want an instance of an object which you can change. Like you want to change a specific users username. And other times you want to get all users of a specific type. But you don’t want the method for fetching users to be part of each user because that doesn’t make any sense.

/** user.js **/

var User = function (data) {
this.data = data;
}

User.prototype.data = {}

User.prototype.changeName = function (name) {
this.data.name = name;
}

User.findById = function (id, callback) {
db.get('users', {id: id}).run(function (err, data) {
if (err) return callback(err);
callback(null, new User(data));
});
}

module.exports = User;

So what we’ve done is created a User class that can both be an instance of a user and has a static method to find users. This means you can use the same User model to do anything you need to do with users. Each user also has a data object. This is where any data that should be saved back to the database is stored. Here’s an example of how you’d use it:

/** app.js **/

User = require("./user.js")

var toby = new User({name: "Toby"});

User.findById(42, function (err, user) {
user.changeName("Brian");
});

We first created a user called Toby. Then we found a user of id 42 from the database and once it was returned we changed that users name to Brian.

Now a model like this is still missing quite a few important features. Firstly it can’t save out to the database, lets fix that with a save function:

/** user.js **/

User.prototype.save = function (callback) {
var self = this;
db.get('users', {id: this.data.id}).update(JSON.stringify(this.data)).run(function (err, result) {
if (err) return callback(err);
callback(null, self); 
});
}

This is why we stored all the users data in this.data rather than straight onto the user. Because it makes saving and loading the user from the database super easy. You just stringify it to save and do new User(data) when loading.

Now it’s bad practice to access all it’s data by changing user.data.something and sometimes you may forget and wonder why user.name is always undefined when the user clearly has a name. So lets add getters and setters to the model.

/** user.js **/

User.prototype.get = function (name) {
return this.data[name];
}

User.prototype.set = function (name, value) {
this.data[name] = value;
}

I also like to enforce some sort of database schema. Otherwise with many developers on a team it’ll be hard to keep track of all the different variables the user could have.

/** schemas.js **/

schemas = {
user: {
id: null,
name: null,
password: null
}
}

module.exports = schemas;
/** user.js **/

var schemas = require("./schemas.js");
var _ = require("lodash");

User.prototype.sanitize = function (data) {
data = data || {};
schema = schemas.user;
return _.pick(_.defaults(data, schema), _.keys(schema)); 
}

That last line in sanitize uses a couple of lodash functions to only keep variables that are in the user schema. _.defaults merges all variables from schemas.user into data that don’t exist already. _.keys gets all the keys from the schema. _.pick only keeps the variables who’s names were returned by _.keys. Basically it ensures this.data matches our schema exactly.

Now we can use this sanitize function both in the constructor of user and also before saving. That way every user will look exactly the same in the database with no stray data. Here’s what these functions look like now:

user.js

var User = function (data) {
this.data = this.sanitize(data);
}

User.prototype.save = function (callback) {
var self = this;
this.data = this.sanitize(this.data);
db.get('users', {id: this.data.id}).update(JSON.stringify(this.data)).run(function (err, result) {
if (err) return callback(err);
callback(null, self); 
});
}

We now have an easy to use User model that can retrieve users from the database, change their properties and save them back with all data being sanitized and checked automatically.

Now many of our models will use similar functions for get/set/save/sanitize. So instead of copy and pasting code you should create a generic model that you can extend all your other models from. Inheritence is kind of tricky to implement with Javascript so unless you’re an amazing coder I’d recommend looking into es6’s classes or using coffeescript, which is what I use for Tower Storm.

You can download and play with all the source code (including a little mock database for getting and saving data) for the tutorial at: https://github.com/timjrobinson/nodejsmodels

Let me know if you have any questions or feel this model structure could be improved.

 

tim