isvalid.js is an asynchronous node.js library for validating and error correcting JSON. In contrary to JSON Schema it uses a very simple schema model - inspired by the Mongoose schemas.
Here's a simple example on how to use the validator:
var isvalid = require('isvalid');
isvalid(somedata, {
'user': { type: String, required: true },
'pass': { type: String, required: true }
}, function(err, validObj) {
/*
err: Error describing invalid data.
validObj: The validated data.
*/
});
The isvalid
function takes three parameters.
isvalid(dataToValidate, validationSchema, callback);
There is a convenience library for connect.js and express.js middleware available through the isvalid-express.js project.
- It now catches more errors in schemas - such as wrong values on validators.
- Schema errors are now thrown as a
SchemaError
which contains schema that failed through theschema
property. - The library is now completely asynchronous - allowing for I/O while formalizing, validating and comparing.
- Formalizer is publicly exposed in order to pre-formalize schemas manually.
- Schemas are now formalized per demand. Large schemas are formalized by the validator as they are needed.
- ValidationErrors now contain the pre-formalized schema - for better identification by developer.
- Automatic parsing of ISO-8601 dates into Date - contributed by thom-nic.
- Errors are thrown if validators are used out of context.
- ValidationError now contains the
validator
property - specifying which validator actually failed. - You can now specify custom error messages using the
error
validator. Object
now supports theallowUnknownKeys
validator.
isvalid.js uses a simple schema modal to specify how the data should be formatted. It supports generic validators for all types and type specific validators.
Errors in function parameters or schemas are thrown - as they are developer errors - and validation errors are passed to the callback.
- Wrong parameters throw
Error
instances. - Schema errors throw
SchemaError
instances. - Validation errors are passed to the callback as
ValidationError
instances.
The SchemaError
contains the schema
property which contains the actual schema in which there is an error. It also has a message
property with a description of the error.
The ValidationError
contains three properties besides the message
field of Error
.
keyPath
is an array indicating the key path in the data where the error occured.schema
is the schema that failed to validate.validator
is the name of the validator that failed.
Some types can be specified using shortcuts. Instead of specifying the type, you just use the type. This works with objects and arrays.
In the above simple example we used the object shortcut and should have looked like this if we hadn't.
isvalid(somedata, {
type: Object,
schema: {
'user': { type: String, required: true },
'pass': { type: String, required: true }
}
}, ...);
Object shortcuts are used like this:
{
"user": { type: String }
}
and is in fact the same as this:
{
type: Object,
schema: {
"user": { type: String }
}
}
Which means that data should be an object with a user
field of the type String
.
The same goes for arrays:
[
{ type: String }
]
is essentially the same as:
{
type: Array,
schema: { type: String }
}
Which means the data must be an array of strings.
These types are supported by the validator:
- Object
- Array
- String
- Number
- Boolean
- Date
- or custom validators.
There are some validators that are common to all types, and some types have specific validators.
You specify the type like this:
{ type: String }
In the above example the input must be of type String
.
All schemas must have the type
specified or have a custom validator through a custom
function - more about custom validators in it's designated section below.
Object and array shortcuts apply their type automatically - as seen above.
These validators are supported by all types.
Defaults data to specific value if data is not present in input. It takes a specific value or it can call a custom function to retrieve the value.
Type: Any value or a function.
Example:
{
"email": { type: String, default: "[email protected]" }
}
This tells the validator, that an email
field is expected, and if it is not found, it should just assign it with whatever is specified using default.
This works with all supported types - below with a boolean type:
{
"receive-newsletter": { type: Boolean, default: false }
}
Now if the receive-newsletter
field is absent in the data the validator will default it to false
.
default
also supports functions.
Example:
{
"created": {
type: String,
default: function(fn) {
fn('This is my default value');
}
}
}
In the above example the function is called whenever the input data does not have a created
field.
The function must take one parameter which holds the callback function to call with the actual default data. The reason is the asynchronous nature of the library and allows for asynchronous work to be done before calling the callback.
Values: true
, false
or 'implicit'
.
required
works a little like default. Except if the value is absent a validation error is send to the callback.
{ type: String, required: true }
The above specifies that the data must be present and be of type String
.
Example:
{
type: Object,
required: 'implicit',
schema: {
'user': { type: String, required: true }
'email': { type: String }
}
}
The above example is to illustrate what 'implicit'
does. Because the user
subschema is required, the parent object inherently also becomes required. If none of the subschemas are required, the parent object is also not required.
This enables you to specify that some portion of the data is optional, but if it is present - it's content should have some required fields.
See the example below.
{
type: Object,
required: false,
schema: {
'user': { type: String, required: true }
'email': { type: String }
}
}
In the above example the data will validate if the object is not present the input, even though user
is required - because the parent object is explicitly not required. If the object - on the other hand - is present it must have the user
key and it must be of type String
.
Remark: If
required
is not specified, thenObject
andArray
types are by default'implicit'
. All other types are by default non-required. Alsorequired
is ignored ifdefault
is specified.
Type: Object
Errors are really not a validator - it allows you to customize the errors emitted by the validators. All validators have default error messages, but these can be customized in order to make user and context friendly error messages.
An example below.
{
'username': {
type: String,
required: true,
match: /^[^ @]+$/,
errors: {
type: 'Username must be a string.',
required: 'Username is required.',
match: 'Username cannot contain any white spaces.'
}
}
}
Now in case any of the validators fail, they will emit the error message specified - instead of the default built-in error message. The message
property of ValidationError
will contain the message on validation failure.
The schema
validator of object and array types specifies the schema of any children. Objects have keys and values - arrays only have a single schema - which can be an object, though.
An example below of an object schema with a ´user´ key.
{
type: Object,
schema: {
'username': { type: String }
}
}
And example below of an array of strings.
{
type: Array,
schema: { type: String }
}
Type: Boolean
This is to make sure that keys not intended in the data are passed through. If an object contains a key unspecified in the schema it will come back with an error - if the value of this validator is set to false
.
On the other hand - if this is set to true
- any unknown keys are passed on unvalidated.
Objects do not allow unknown keys by default.
Arrays has two validator besides the common validators.
Type: Number
or String
This ensures that an array has a specific number of items. This can be either a number or a range. The validator sends an error to the callback if the array length is outside the bounds of the specified range(s).
An array that should have exactly 2 items:
{
type: Array,
len: 2,
schema: { … }
}
An array that should have at least 2 items:
{
type: Array,
len: '2-',
schema: { … }
}
An array that should have a maximum of 2 items:
{
type: Array,
len: '-2',
schema: { … }
}
An array that should have at least 2 items and a maximum of 5 items:
{
type: Array,
len: '2-5',
schema: { … }
}
An array that should have at two or less items, exactly 5 items or 8 or more items:
{
type: Array,
len: '-2,5,8-',
schema: { … }
}
Type: Boolean
This ensures that all elements in the array are unique - basically ensuring the array is a set. If two or more elements are the same, the validator sends an error to the callback.
Example:
{
type: Array,
unique: true,
schema: { … }
}
The
unique
validator does a deep comparison on objects and arrays.
Strings has one validator besides the common validators.
Type: Boolean
This does not do any actual validation. Instead it trims the input string in both ends - before any other validators are checked. Use this to remove any unforeseen white spaces - added by the user - before any other validation is done.
Type: RegExp
This ensures that a string can be matched against a regular expression. The validator sends an error to the callback if the string does not match the pattern.
This example shows a string that must contain a string of at least one character of latin letters or decimal numbers:
{ type: String, match /^[a-zA-Z0-9]+$/ }
Numbers has one validator besides the common validators.
Type: Number
or String
This ensures that the number is within a certain range. If not the validator sends an error to the callback.
The
range
validator uses the same formatting as the array'slen
validator described above.
Custom validators are for usage when the possibilities of the validation schema falls short. Custom validators basically outsources validation to a custom function.
Custom validators are specified by the custom
field of a schema - instead of type
.
Example (remark the absent of type
):
{
'date': {
custom: function(obj, schema, fn) {
if (!(obj instanceof Date)) {
return fn(new Error('Type is not a Date'));
}
fn(null, obj);
}
}
}
In the above example we have specified a custom validator. In the specific case we test whether the data is of type Date
. If not we pass an error to the callback. Otherwise we call the callback function with no error and the valid object.
The asynchronous nature of the library, allows for asynchronous operations in custom functions.
The custom function must take three parameters
- obj The Object that needs validation
- schema The schema to validate against
- This enables you to use the schema to pass in options.
- fn The callback function to call when validation either succeeds or fails.
- The callback function takes to parameters
- err An
Error
describing the validation error that occurred. - validObj The finished and validated object.
- err An
- The callback function takes to parameters
Remark: Errors are automatically converted into a ValidationError and sent to the the callback internally.
If you need to pass any options to your custom validator, you can do so by using the options
property of the schema.
An example below.
{
'myKey': {
options: {
myCustomOptions: 'here'
},
custom: function(obj, schema, fn) {
// schema.options will now contain whatever options you supplied in the schema.
// In this example schema.options.myCustomOptions equals 'here'.
}
}
}
Using any other key than custom
and options
in conjunction with a custom validator will throw an error.
Schemas with both
custom
andtype
set will throw aSchemaError
.
If the schema has type Number
and the input holds a String
containing numbers (or/and a point), the validator will automatically convert that into a number.
Likewise will schemas of type Boolean
be automatically converted into a Boolean
if a String
with the value of true
or false
is in the data.
If the schema is of type Date
and a String
containing an ISO-8601 formatted date is supplied, it will automatically be parsed and converted into a Date
.
Internally the library tests for object shortcuts by examining the absent of the type
and custom
keys. So if you need objects schemas with validators for keys with those names, you must use explicitly formatted object schemas - hence the shortcut cannot be used.