Tutorial: Implementing Models

Integrating Models
Implementing The Model

In traditional MVC applications models bind the requests for information from the View to the actions of the Controller.  In more simplistic terms it is the role of the model to obtain and supply data to the mechanism displaying information to the user.  In a traditional MVC application (or otherwise known as a full-stack application) the view engine would generally be a web page but in terms of an API whose nature is purely back-end activities there is no view engine present.

Despite this lack of apparent view engine our API still follows traditional design aspects by making use of Models and Controllers.  Let us take a look at how these are integrated.

Loading Models In To The Application

As our models make use of Waterline it requires a carefully orchestrated sequence of steps to invoke and make use of various models.  Fortunately these problems have already been solved and the code is available at YourAPIExpert.com’s GitHub Repository as “003 Endpoints”.

In order to load our models from a directory we make use of a short function invoked from the main server.js file.

  server.use(restify.gzipResponse());
  server.use(passport.initialize());
  server.use(passport.session());

  // In order to access various data sources we need to access these
  // capabilities from the various models available to us.
  require('./models.js')(__dirname+'/models', server);

On line 124 of server.js we instruct the application to invoke code from the models.js file and pass to it the main ‘server’ variable hence the position of the statement.  The main ‘server’ variable is, in essence, the instantiation of the RESTify function.

Let us now focus on the models.js file.

// The Essentials
var fs = require('fs');
var path = require('path');
var Waterline = require('waterline'); // ORM Library

//-----------------------------------------------------------------------------
// MAIN CODE BLOCK
//-----------------------------------------------------------------------------


// Exports the 'route' function
module.exports = function call(dirname, server) {
  var files = fs.readdirSync(dirname);
  server.orm = new Waterline();

  files.forEach(function (file) {
    if (path.extname(file) === '.js') {
      var filepath = dirname + '/' + file;
      if (fs.statSync(filepath).isDirectory()) {
        call(filepath, server, Waterline);
      } else {
        require(filepath)(server,Waterline);
      }
    }
  });
};

Lines 15 – 40 of models.js assist the application to iterate through a directory and load each of the models in to the application.  On line 18 we invoke the Waterline library and on line 28 we instantiate it and assign it to ‘server.orm’.  This will allow us to call it later in the controllers by referring to the ‘server’ variable rather than moving a separate and disparate additional variable across the application.  Lastly on line 36 we pass both the ‘server’ and ‘Waterline’ variables during the invocation of the models.

Next we will focus on the contents of an individual Model file – in this case the User model contained in model/User.js.

// Here we define the User model which is similar to
// Django as used by the Python folks.
module.exports = function(server, Waterline) {
  var User = Waterline.Collection.extend({

    identity: 'auth_user',
    connection: 'myLocalMySql',

    attributes: {
      id: {
        type: 'integer',
        primaryKey: true,
        autoIncrement: true
      },
      username: {
        type: 'string',
        required: true,
        unique: true
      },
      password: {
        type: 'string',
        required: true
      },
      last_login: {
        type: 'datetime',
      },
      is_superuser: {
        type: 'integer',
        default: 0
      },
      first_name: {
        type: 'string'
      },
      last_name: {
        type: 'string'
      },
      email: {
        type: 'string'
      },
      is_staff: {
        type: 'integer',
        default: 0
      },
      is_active: {
        type: 'integer',
        default: 1 // Set to 0 if implementing custom activation routines
      },
      date_joined: {
        type: 'datetime',
      },

    }
  });
server.orm.loadCollection(User);
}

Starting on line 24 we export the model and consume both the ‘server’ and ‘Waterline’ variables passed to us during invocation by the models.js code from above.  We make use of the ‘Waterline’ variable to extend the class in order to accommodate our ‘User’ model and on line 77 we make use of the previously initialized ‘server.orm’ variable to load our model in to the ORM.

Lines 27 through 76 define the model in line with the SQL schema located in sql/db.sql and are pretty straight-forward in their application and are defined in this documentation.  There are a few notable lines however.  On line 29 we instruct the ORM that this model applies to the ‘auth_user’ table of our database.  In order to access the specific database we instruct the ORM to make use of the connection identified on line 30.

The definition of this particular connection and its parameters is defined in config/development.json

  "orm": {
     "middleware": [{
        "mysql":"mysqlAdapter"
     }],
     "connections": {
        "myLocalMySql": {
           "adapter": "mysql",
           "host": "localhost",
           "port": 3306,
           "database":"demo",
           "user":"demo_dba",
           "password":"CbM5L4nkkVa8LKLp"
        }
    },
    "defaults": {
       "migrate": "safe"
    }
  }

Lines 28 to 45 contain the “orm” configuration definitions.  Pay special attention to line 30.  This line contains the name of the adapter to load and its definition and invocation traces all the way back to the main server.js file which we will look at next.  On lines 33 through 39 we defined the ‘myLocalMySql’ connection and its parameters as referred to by the ‘User’ model.

 

Finally and perhaps the most important is how we initialize the ORM in to our application.  The code for this action is included in server.js between lines 127 and lines 137.

  // Initialize the ORM
  ormConfig.adapters = {};
  ormConfig.middleware.forEach(function(middleware) {
    Object.keys(middleware).forEach(function(key) {
          ormConfig.adapters[key] = adapter[middleware[key]];
    });
  });
  server.orm.initialize(ormConfig, function(err, models) {
     if(err) throw err;
     server.models = models.collections;
     server.connections = models.connections;

On line 128 we declare a special placeholder variable named ‘ormConfig.adapters’.  Between lines 129 and 131 we define a special routine to iterate through the ‘middleware’ keys located in config/development.json and for each key found the routine invokes the appropriate library.  The library is defined in a special manner starting on line 27 of server.js

// Require any waterline compatible adapters here
var adapter = {};
adapter.mysqlAdapter = require('sails-mysql');

On line 28 we declare a special ‘adapters’ variable and on line 29 we assign the ‘sails-mysql’ library to ‘adapter.mysqlAdapter’.

So how do these seemingly disparate instantiations meet?  The answer lies on line 131 of server.js above.

    Object.keys(middleware).forEach(function(key) {
          ormConfig.adapters[key] = adapter[middleware[key]];
    })

On line 131 the code matches the definitions contained in the configuration file config/development.json with the ORM adapter library invoked earlier above on line 27 of server.js.

Thus when all variables are populated the code would read:

          ormConfig.adapters.mysqlAdapter = adapter.mysqlAdapter;

In order to invoke this adapter and initialize it on to the ORM the code between lines 134 and 137 initialize the adapter and assigns the model to the ‘server’ variable.

  server.orm.initialize(ormConfig, function(err, models) {
     if(err) throw err;
     server.models = models.collections;
     server.connections = models.connections;

On line 136 and 137 we associate the initialized models and connections with ‘server.models’ and ‘server.connections’ respectively which make them available for use in our controllers.

Next Up:  Using The Model In PassportJS

Pages: 1 2 3 4 5

Written by YourAPIExpert