README
parambulator - Node.js module
A simple way to generate nice error messages for named parameters.
This module is used by the Seneca framework for input validation.
This module works on both Node.js and browsers.
If you're using this module, feel free to contact me on twitter if you have any questions! :) @rjrodger
Current Version: 1.5.2
Usage
Use this module to validate input or configuration parameters provided as JSON. You can ensure that the JSON structure and data types are what you need. You'll get friendly, useful error messages for your users, and that makes your API better!
var parambulator = require('parambulator')
var paramcheck = parambulator({
price: {type$:'number'}
})
// this passes
paramcheck.validate( { price: 10.99 }, function(err) { console.log(err) } )
// this fails - price should be a number
paramcheck.validate( { price: 'free!' }, function(err) { console.log(err) } )
// output: The value 'free!' is not of type 'number' (parent: price).
Why?
You're writing a module and you accept configuration as a structured JavaScript object. For example, opening a database connection: MongoDB driver. Or you want to have named parameters: http.request.
It's nice to be able to validate the input and provide useful error messages, without hand-coding the validation.
But What About JSONSchema!
Yes, JSONSchema would be the proper way to do this. But the syntax is too hard, and the error messages aren't friendly. This is a Worse is Better! approach.
There's also a philosophical difference. JSONSchema defines a formal structure, so you need to be fairly precise and complete. Parambulator defines a list of rules that are tested in the order you specify, and you can be vague and incomplete.
Key Features:
- Easy syntax, rules are tested in order
- Add your own rules
- Customize the error messages
And yes Virginia, it does validate its own input.
Installation
npm install parambulator
or
bower install parambulator
And in your code:
var parambulator = require('parambulator')
or
<script src="bower_components/parambulator/parambulator-min.js"></script>
Usage
Import the module using the standard require syntax:
var parambulator = require('parambulator')
This is a function that creates Parambulator object instances. This function accepts two arguments:
- spec - the rule specification to test against
- pref - your preferences, such as custom error messages and rules
Example:
var paramcheck = parambulator({ price: {type$:'number'} })
The paramcheck variable is an instance of Parambulator. This object only has one method: validate, which accepts two arguments:
- args: the object to validate
- cb: a callback function, following the standard Node.js error convention (first arg is an Error)
Example:
paramcheck.validate( { price: 10.99 }, function(err) { console.log(err) } )
The callback function is called when the validation has completed. Processing of rules stops as soon as a rule fails. If validation fails, the first argument to the callback will be a standard JavaScript Error object, with an error message in the message property.
Examples
Heavily commented examples are provided in the doc/examples folder: https://github.com/rjrodger/parambulator/tree/master/doc/examples
You should probably read this rest of this first, though.
Rules
The validation rules are defined in the spec argument to parambulator. The rules are specified as an object, the properties of which are the rule names, and the values the rule options, like so: {required$:['foo','bar']}
. The rules are executed in the order that they appear (JavaScript preserves the order of object properties).
Rule names always end with a $
character. Properties that do not end with $
are considered to be literal property names:
{
required$: ['foo','bar'],
foo: {
type$: 'string'
}
}
This specification requires the input object to have two properties, foo and bar, and for the foo property to have a string value. For example, this is valid:
{ foo:'hello', bar:1 }
But these are not:
{ foo:1, bar:1 } // foo is not a string
{ foo:'hello' } // bar is missing
The rules are evaluated in the order they appear:
- at the current property (i.e. the top level), check for properties foo and bar, as per
required$: ['foo','bar']
- descend into the foo property, and check that it's value is of
type$: 'string'
You can nest rules within other rules. They will be evaluated in the order they appear, depth first.
For each input property, the rules apply to the value or values within that property. This means that your rule specification mirrors the structure of the input object.
For example, the specification:
{
foo: {
bar: { type$: 'integer' }
}
}
matches
{ foo: { bar: 1 } }
but does not match
{ bar: { foo: 1 } }
In general, rules are permissive, in that they only apply if a given property is present. You need to use the required$ rule to require that a property is always present in the input.
Each rule has a specific set of options relevant to that rule. For example, the required$ rule takes an array of property names. The type$ rule takes a string indicating the expected type: string, number, boolean, etc. For full details, see the rule descriptions below.
Literal properties can also accept a wildcard string expression. For example:
{ foo: "ba*" }
This matches:
{ foo: "ba" }
{ foo: "bar" }
{ foo: "barx" }
but not
{ foo: "b" }
Wildcards
Sometimes you don't know the property names in advance. To handle this case, you can also use wildcard expressions in literal properties:
{ 'a*': { type$: 'boolean' } }
This matches:
{
a: true,
ax: false,
ayz: true
}
In particular, '*'
on its own will match any property (at the same level). Wildcard expressions have the usual syntax: *
means match anything, and ?
means match a single character.
What about repeatedly nested rules? In this situation, you want to apply the same set of rules at any depth. You can use the special literal property '**'
to achieve this:
{ '**': { a: {type$: 'boolean' } } }
This matches:
{ a:true, x:{a:false, y:{a:true}}}
ensuring that any properties called a will be an integer. The recursive descent starts from the current level.
Arrays
Arrays are treated as if they were objects. The property names are simply the string values of the integer array indexes. For example:
{ a: {'0':'first'} }
This matches:
{ a:['first'] }
Due to a quirk in the Chrome V8 engine, the order of integer properties is not preserved. Use the special prefix __
as a workaround:
{ a: {'__1':'first', '__0':'second'} }
This matches:
{ a:['second','first'] }
but the rules are tested in order:
'__1':'first'
'__0':'second'
Custom Errors
Each rule has an associated error message. By default these explain the reason why a rule failed, and give the property path (in standard JavaScript dot syntax: foo.bar.baz
) of the offending value. You can customize these error messages, by providing your own string templates, or by providing a function that returns the error message text.
Use the msgs property of the pref argument (the second argument to parambulator) to define custom error messages:
var pm = parambulator({...},{
msgs: {
required$: 'Property <%=property%> is required, yo!'
}
})
The template syntax is provided by the underscore module: http://underscorejs.org/#template
The following properties are available:
- property: the relevant property name
- value: the string representation of the value that failed in some way
- point: the actual value, which could be of any type, not just a string
- rule.name: the name of the rule
- rule.spec: the rule specification, e.g.
'boolean'
for ruletype$:'boolean'
- parentpath: a string locating the value in the input (properties in dot-syntax)
- json: a reference to the JSON.stringify function, use like so: <%=json(rule.spec)%>
The parentpath will use the term top level when the error concerns the top level of the input object. You can customize this term using the topname option:
var pm = parambulator({...},{
topname: 'name_of_param_in_my_function_definition'
})
You can also modify the error message using the msgprefix and msgsuffix options, which are prepended and appended to the message body, respectively, and also support the template syntax.
You can also specify a custom error message using a function. This lets you customize on the specific failing conditions, such as the property name:
var pm = parambulator({...},{
msgs: {
required$: function(inserts){
if( 'voodoo' == inserts.property ) {
return "don't dare do: "+inserts.value
}
else {
return 'Property '+inserts.property+' is required, yo!'
}
}
}
})
The inserts parameter is an object containing the properties as above.
Rules
The following rules are provided out-of-the-box. To define your own rules, see below.
Each rule operates at the current point. This is the current property location inside the input object.
For example, with input:
{
foo: {
bar: {
baz: 'zzz'
}
}
}
the point foo.bar
is the object:
{ baz: 'zzz'}
literal property
Match an input property. You can use wildcards. Accepts a set of sub rules, or a wildcard string to match against. The property names match against property names in the current point.
{
a: { ... }
'b*': { ... }
c: 'z*'
}
boolean rules
As a convenience, rules that take a property name, such as required$, can be specified for a property using the form:
{
foo: 'required