Introducing the Parambulator module for validating “options” objects in Node.js

If you’ve used any Node.js modules at all, you’ll have noticed a common pattern with configuration. Many modules provide complex functionality that you can control by providing a JavaScript object with a bunch of named options, possibly with sub options. Here’s an example:

var toast = require('toaster').cook({ duration:5, bread:'white', sides:['A','B'] })

The duration, bread, and sides options need to be a number, a string, and an array respectively. It would be nice to validate this, and provide useful error messages if the calling code gets it wrong.

For real world examples of this pattern, check out:

In each of these modules, validation of the options is ad hoc and mixed in with other code. Sometimes you get an error message, if you’re lucky.

A real solution to this problem should be:

  • easy and quick to use, otherwise why bother,
  • declarative (i.e. no code required), that’s just good practice,
  • provide good error messages, which is pretty much the whole point,
  • customizable, in case you have weird and wonderful option structures.

So what are your options for validating the options, and providing decent error messages?

The basic approach is just to do ad hoc validation in code, and set a few defaults while you’re at it:

function handleoptions(opts) {
  if( !opt.foo ) throw new Error(“No foo, bro!”)
  opt.bar = opt.bar || “default value”
  var port = parseInt(opt.port,10)
  ...
}

That’s nice. But you need to read the code to get a clear idea of what’s going on. Also, things start out neat, but eventually that validation logic ends up all over the place. And, unless you’re incredibly pedantic, you’ll miss many error conditions. Oh, and don’t forget about the bugs in the validation code itself – it happens! So easy, quick and customizable, but not declarative, and error messages are a bit take it or leave it.

A more sophisticated approach is to recognize that this problem can be generalized. So use JSONSchema. As with XML schema, this way lets you specify exactly what you want the object to conform to. But it’s hard work (you have to be pretty exact), and JSON Schema are hard to read, and it’s a lot of overhead. You do get to tick the declarative box, but it’s not easy or quick, the error messages are nasty, and customization is “here be dragons” territory.

So, in the best Node.js tradition, I decided to solve this little problem with a new module: Parambulator.  Actually this module was also written with another purpose in mind: as example code for the nodejsdublin developer meetup to provide a small example module for those looking to write their own.

Parambulator let’s you define a quick and easy schema for your options. You don’t have to be exhaustive. Unlike JSONSchema, you can use the same object structure as your expected input, so there’s fewer mental gymnastics. And you get nice error messages by default, which you can customize. Here’s an example:

var toastcheck = parambulator({
  duration: {type$:'integer'},
  bread:    {enum$:['white','brown']},
  sides:    {type$:'array'}
})

toastcheck.validate( options, function(err) { … } )

This gives you “good enough” validation – about as much as you bother writing with ad hoc code. And you can be stricter if you like. It’s declarative, so you can see at a glance what the validation checks are. It’s still a schema, of sorts, but it’s a lot simpler than a full JSONSchema. And you can customize the validation rules and error messages (see the docs for details).

The next time you’re writing a module, and you’d like to make your options handling a little more robust, try it out and let me know. Also if you write any cool validation rules, send me a pull request!

Parambulator: https://github.com/rjrodger/parambulator

npm install parambulator

 

This entry was posted in Node.js. Bookmark the permalink.

Leave a Reply

Your email address will not be published. Required fields are marked *

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>