lunes, 24 de marzo de 2014

A bit deeper into Promises and Mongoose

On the internet you can find thousands of tutorials about promises and how to use them.
I recommend this webpage http://www.promisejs.org/ if you are starting with promises. I don't pretend to cover promises basics in this post.

Promises are a very simple but I had some problems at the beginning with a real life example. I only found very simple promises examples.

For this example we are going to use the Q library (https://github.com/kriskowal/q). In that page there are many examples on how to use the library.

We're also using Mongoose to save a client to the database.

For this example, the client id must be unique.

This is the client schema (client-model.js):

module.exports = function(app, mongoose){
 
 var Schema = mongoose.Schema;  

 var client = new Schema({
     id: String,
     name: String,
     lastName: String,
     gender: String
 });
 return mongoose.model('Client', client);
};


And this is the function that saves the client. We will validate an empty or repeated ID, empty name or empty lastName (this can be validated by Mongoose with required and unique, but for this example we'll do it manually)



var Q = require('q'),
 mongoose = require('mongoose');

module.exports = function(app) {

 var clientModel =  require('../models/client-model.js')(app, mongoose);

 var isNotEmptyOrNull = function(pValue) {
  if (!pValue) {
   return false;
  } else if (pValue.length < 1) {
   return false;
  }
  return true;
 };

 app.post('/api/client', function(pRequest, pResponse) {

  // Validate empty id, name and lastName
  Q.all([
   isNotEmptyOrNull(pRequest.body.id),
   isNotEmptyOrNull(pRequest.body.name),
   isNotEmptyOrNull(pRequest.body.lastName)
  ])
  .then(function(pResults) {
   // pResults is an array with the result of each function. In this case, we expect it to be [true, true, true]
    
   var findClient = Q.nbind(clientModel.find, clientModel);

   // Check if id, name and lastName is not null or empty
   var isValidObject = pResults.every(function(pResult) {
    return pResult;
   });

   if (!isValidObject) {
    // It's not a valid object, we send a bad request to the user
    throw 'Id, name and lastName are required';
   }

   return findClient({ id: pRequest.body.id});
  })
  .then(function(pFoundClient) {

   // The id already exists
   if (pFoundClient && pFoundClient.length > 0) {
    throw 'Id must be unique';
   }

   // Everything is ok, we save the client
   var clientToSave = new clientModel(pRequest.body);
   return Q.when(clientToSave.save());
  })
  .then(function() {

   return pResponse.send('Client saved');

  })
  .catch(function(pError) {
   // Catch any error
   console.log(pError);
   return pResponse.send(400, pError);
  });
 });

};

This is the explanation on Q.all from the Q API reference:
Returns a promise that is fulfilled with an array containing the fulfillment value of each promise, or is rejected with the same rejection reason as the first promise to be rejected. This method is often used in its static form on arrays of promises, in order to execute a number of operations concurrently and be notified when they all succeed
And this is nbind:
Creates a promise-returning function from a Node.js-style method, optionally binding it with the given variadic arguments.
With that example it should be clear how to use Q Promises and Mongoose to save the object. Please leave questions, comments or feedback. Thanks

martes, 18 de marzo de 2014

NodeJS and ExpressJS - Load Controllers Dinamically



When you create an Express application you have some options for your routes file:

  1. Put all your routes in a single file (Bad idea!)
  2. Use as many requires as routes files (Not as bad as 1, but still no good)
  3. Use an existing module like express-load for example (https://github.com/jarradseers/express-load)
  4. You think 3 is too much overhead and you want to use your own code to 'require' the routes files

In this post, I'll focus in option #4.

If you prefer you can open this gist (https://gist.github.com/davibq/9633951).

This gist contains 3 files:
  • The app.js file generated when you create an express project.
  • An example of a route file with 2 GET endpoints
  • Our custom routes initializer.
If you're a little lazy and don't want to open the gist, here is the routes-initializer file:


/* Requires */
var fs = require('fs');
 
/* Constants */
/**
 * Folder where the routes are placed
 * @type {String}
 */
var ROUTES_FOLDER = './routes';
 
/**
 * This is used to identify the Routes in the folder. 
 * If the file name doesn't contain this variable value, it's not loaded.
 * This way we avoid to load Readme.md as a controller, for example
 * @type {String}
 */
var INIT_ROUTES_IDENTIFIER = 'Route.js';
 
/* Main function */
module.exports = function(app) {
 console.log('Loading Routes...');
 
 var filesInRoutesDirectory = fs.readdirSync(ROUTES_FOLDER);
 
 filesInRoutesDirectory.forEach(function(pRouteFile) {
  if (pRouteFile.indexOf(INIT_ROUTES_IDENTIFIER)>=0) {
   require(ROUTES_FOLDER+'/'+pRouteFile)(app);
  }
 });
 
 console.log('Routes Loaded...');
};


In order to get this code working, you should change the ROUTES_FOLDER variable and the INIT_ROUTES_IDENTIFIER.
This piece of code is really simple, it loads the files in the directory 'ROUTES_FOLDER' and then makes a require of each file to initialize each file.


In case you have any feedback, please leave a comment.