NodeJS Callback Conventions and Your App

NodeJS has a pretty specific convention when implementing callbacks in modules - function(err,result). Does this always make sense?

Date

Did It Work Or Didn’t It?

I have a method on my Customer module where I add a customer to MongoDB:

var customer = require('./lib/customers');
customer.register("[email protected]","password","confirm", result, success);

The last two arguments here are callbacks. If you’re new to JavaScript - keep in mind it’s asynchronous which means that nothing really happens right now. If you need to know when something is done, you sending a callback function (which is probably better thought of as a continuation) which gets fired when the operation completed.

But how do you structure your callback?

In some circles (like Backbone and jQuery) you use a success/fail approach - like I did above. You pass one callback in that fires if everything succeeds, another that fires if it fails. This convention is rather clean:

//you can also make these anonymous functions inline
success = function(result){  
  alert("Yippee Skippy: " + result.email + " was added to the DB");
}
fail = function(err){  
  alert("Something bad happened: "+err);
}
customer.register("[email protected]","password", "password", success, fail);

Node’s convention is a little different and (keeping in mind that I’m very new to Node) I can only guess that this convention came about to avoid deep nesting and excessively callback-heavy code. In Node, the style is thus:

//inline this time
customer.register("[email protected]","password","password",function(err,result){  
  if(err){    
    alert("Oh noes! " + err);  
  }else{    
      alert("yippee skippy...");  
  }
});

This code is clean and readable - and it’s also less so that makes us happy. But in our method we have to do this… which is a bit wonky:

var register = function(email,password,confirm,callback){   
  //pseudo code - instance and assign vals  
  var newCustomer = new _model();  
  //save it  
  newCustomer.save(function(err){    
    if(err){      
      callback(err,null);    
    }else{      
      callback(null,newCustomer);    
    }      
  });
}

Now if the save() method on newCustomer had followed the Node convention - then I could have just passed the callback directly into it:

var register = function(email,password,confirm,callback){   
  //pseudo code - instance and assign vals  
  var newCustomer = new _model();  
  //save it  newCustomer.save(callback);
}

… which is almost certainly why this standard was adopted (and I like it!). But it doesn’t so we have to write some extra code to handle it. And I could spend another few paragraphs musing on the goods and bads - but I don’t know nearly enough to do such a thing, so let’s move on…

What To Do?

If I learned anything from working with Rails over the last 6 years - it’s to let Rails be Rails. This isn’t a bad thing. Every time I try to get clever, I pay for it.

On the other hand, when I was talking to Batman yesterday (Dave Ward) he suggested the success/fail thing was a more widely-accepted way of doing things. Which I think I agree with… but the Node community doesn’t. Thus my post.

Anyway - if the convention for NodeJS is to use callback(err,result) and I’m using NodeJS well - that’s what I’ll do. In fact I’ve already run into an interesting situation with Vows (the testing framework I talked about yesterday) where it expected that I was using this structure.

I’ll have more to say about Vows in another post (I like it a lot) - but the fact that it (basically) wouldn’t execute more than one callback nudged me into this entire discussion.I’d love to hear your thoughts.