README
Destination:
- validate nested in object (JSON) fields.
- compare schema with value.
- reuse api validations.
Install
$ npm install node-verifier-schema --save
var Schema = require('node-verifier-schema');
Features
- Declarative approach for schema building.
- Several schema object build types.
- Built-in register for schemas. You can use schema aliases.
- Does not impose restrictions on a validator. You can use any framework (or its wrapper) that implements interface of schema.validator (this is easy).
- Correct nesting of schemas and fields.
- You may use shema constructor as function (returns Schema instance anyway).
- Support of async function call for value validation.
- Save your schema in YAML format and load by schema-loader.
- Errors without message - you can use for standard api results and multi-language api.
Simple Usage
var Schema = require('node-verifier-schema');
// Create schema
var sc1 = new Schema().object(function (required, optional) {
field('first_name');
required('last_name');
optional('middle_name');
});
// Eq
var sc1 = Schema.create().object(function () {
this.required('first_name');
this.field('last_name');
this.optional('middle_name');
});
// Eq
var sc1 = Schema.create().object(function () {
this.field('first_name');
this.field('last_name');
this.field('middle_name').optional();
});
// Eq
var sc1 = Schema.create();
sc1.required('first_name')
sc1.field('last_name')
sc1.optional('middle_name');
// Validation
var value = {};
sc1.verifier().verify(value, function (err, isValid, validationError) {
console.log(err); // null - js error
console.log(isValid); // false - validation result
console.log(validationError); // Schema.ValidationError { value: {}, rule: 'required', params: null, path: [ 'first_name' ] } - first error of validation
});
var value = { first_name: 'hello', last_name: 'world' }
sc1.verifier().verify(value, function (err, isValid, validationError) {
console.log(err); // null
console.log(isValid); // true
console.log(validationError); // null
});
Create Schema
var sh1 = new Schema();
var sh2 = Schema.create();
var sh3 = new Schema('nameForRegister1');
var sh4 = Schema.create('nameForRegister2');
Schema registration
var sc1 = new Schema();
// register
var sc2 = Schema.create('mySchema1'); // new Schema and register as "mySchema1"
var sc3 = new Schema('mySchema2'); // new Schema and register as "mySchema2"
Schema.register('mySchema', sc1); // return sc1
Schema.register('mySchema', sc1); // throw Error (schema "mySceham" was already registered)
// get registered
Schema.get('mySchema'); // return sc1
Schema.get('mySchema1'); // return sc2
Schema.get('mySchema2'); // return sc3
Schema.get('mySchema123123123'); // throw Error (schema "mySchema123123123" was not registered)
Schema.get('mySchema123123123', false); // no error, because flag 'strict' was specified as false
Object Schema Building
Schema::like ( schema [, processor] )
schema: Schema
- required.
processor: function(schema)
- optional.
return: this field (schema)
recursive clone schema
properties and fields.
processor - for manual properties copying.
var sh1 = new Schema('Hello').object(function () {
this.field('hello');
this.field('world');
});
var sh2 = new Schema('World').array(function () {
this.field('some');
});
sh2.field('myField').like(sh1);
// equal
new Schema('World').array(function () {
this.field('some');
this.field('myField').object(function () {
this.field('hello');
this.field('world');
});
});
var sh3 = new Schema('World').array(function () {
this.field('some');
});
sh1.like(sh3);
new Schema('Hello').array(function () {
this.field('hello');
this.field('world');
this.field('some');
});
Schema::validate( [ validation ] )
validation: Mixed
.
This library has no own validator. You can use any lib for validation.
All fields for schema can have many items of validation.
Method validate can be called many times, all items will be added into validations
array; All validation
join as array (not replaced).
If validation
will be a array - this array concat with previous validations array.
var sh1 = new Schema().optional().validate('type object');
var verifier = sh1.verifier();
verifier.verify({}, function (err) {
console.log(err); // null
});
var verifier = sh1.verifier();
verifier.verify(undefined, function (err) {
console.log(err); // null
});
verifier.verify("123", function (err) {
console.log(err instance Schema.ValidationError); // false
console.log(err.rule); // 'type'
console.log(err.params); // 'object'
console.log(err.value); // '123'
console.log(err.path); // []
});
Schema::object( nested )
nested: Function
.
You can build inner fields with a function. the build-function has two arguments (first - this.required, second - this.optional) for compact declaration of this schema field.
You can call this method once, else throws an Error.
var sh1 = new Schema().object(function (r, o) {
r('my1');
o('world1');
});
var sh4 = new Schema().object(function (r, o) {
this.required('my2');
this.optional('world2');
});
var sc1 = new Schema('hello2').like(sh4);
sc1.field('some1');
sc1.field('some2');
var sc2 = new Schema();
sc2.like(sc1); // add sh1
var sc3 = new Schema();
sc3.like(Schema.get('hello2')); // add sc1
sc1.field('inner').like(sh3);
var testSc = new Schema().object(function (r, o) {
r('my1');
o('world1');
r('inner').object(function (r, o) {
r('my2');
o('world2');
r('some1');
r('some2');
});
});
_.isEqual(testSc, sh1); // true
Schema::array( [ nested ] )
nested: Boolean|Function
.
If nested
value will be Function
- behaves as Schema::object. But this method adds the flag isArray
=true to the schema.
If nested
value will be Boolean
or not specified (Nullable) - adds (removes) flag of array to this schema.
isArray
flag says validator how value can be processed. By default (isArray = false) value must be object. If isArray = true - value must be array. If isArray is not compatible with value type - you get ValidationError('type', 'array', value) or ValidationError('type', 'object', value) in dependence of isArray
.
if isArray
= true and nested fields were specified validator will process all value items as specified nested fields, if any item will be invalid - the validator error will be returned.
var sch1 = Schema.create();
sch1.like(sh1).array();
// eq
sch1.like(sh1).array(true);
// eq
sch1.like(sh1).array();
// remove flag
sch1.array(false);
If you use simple array (without fields) in schema and you want to validate each item of value - you should create the validation for value.
Schema::clone()
create absolute clone of this schema
var sc1 = new Schema().object(function () {
this.field('first_name');
this.required('last_name');
this.optional('middle_name');
});
var sc2 = sc1.clone();
_.isEqual(sc1, sc2); // true
SchemaVerifier::verifier([, options])
options: Object
- optional
options.ignoreExcess: Boolean
- optional - default false
returns instance of SchemaVerifier
var sch = new Schema().array().required();
var verifier = sch.verifier(); // create schema validator. can throws error if rules is invalid
verifier.verify({}, function (err) {
if (err instance of Schema.ValidationError) {
// invalid state
}
if (err) {
// has an error
}
// valid state
});
SchemaVerifier::verify( value, callback )
value: Mixed
.
callback: Function
.
Runtime validation of value. Compare with schema and inner validations.
This method must have fast process speed. This method is called synchronously, but it's not a problem if any validation function has async call. Async validation call is supported out of the box. (you have a callback as a second argument).
Options object has a validator
function, by default this function is not specified.
validations: Array
- array of validations.
sh1.verify(value, function (err) {
if (err instance of Schema.ValidationError) {
// invalid state
}
if (err) {
// has an error
}
// valid state
});
err: Error|Schema.ValidationError|null
js error.
Schema::field( name )
name: String
- required - name of inside field.
Create required field (Alias Schema::required)
var sc1 = new Schema();
sc1.field('name');
// eq
sc1.object(function (required, optional) {
required('name);
});
// eq
sc1.object(function (required, optional) {
this.field('name);
});
Schema::required( [ name, [ validate ] ] )
name: String
- optional.
validate: Mixed
- optional.
Create required field.
If called without arguments - add required flag
isRequired
- flag for validation, if this flag is true then validated value must not be undefined
- returned Schema.ValidationError('required', true)
var sc1 = Schema.create();
sc1.field('some');
// eq
sc1.required('some');
Schema::optional( [ name, [ validate ] ] )
name: String
- optional.
validate: Mixed
- optional.
Create optional field.
If called without arguments - removes required flag.
var sc1 = Schema.create();
sc1.field('some').optional();
// eq
sc1.optional('some');
Schema::strict( [flag] )
Add strict mode flag.
If Strict mode was enabled - in this object excess fields can't be pass successfully the validation.
var schema = new Schema();
schema.strict();
// eq
schema.strict(true);
// if you want to disable strict mode of this schema/field
schema.strict(false);
Schema File Loader
Load your schema from YAML (or JS) file.
Use
var Schema = require('node-verifier-schema');
var schema1 = schemaLoader('path/to/file.tml');
// load schema and register as 'nameForThisSchema'
Schema.load('path/to/file.tml', 'nameForThisSchema');
var schema2 = Schema.get('nameForThisSchema');
Function schemaLoader has two params:
schemaLoader(absFilePath [, name]).
absFilePath: String
- absolute path to file that needs to be loaded. Must have js, yaml or yml file extension as default.
name: String
- optional - name to register this schema in Schema.register.
YAML full syntax:
schema declaration must start with schema
, and you can add []
to mark this schema as array type, and ?
as optional
inside fields should not be named as =
, has []
and ?
flags as in schema declaration
attribute name =
means validation
validation must be array, inside validation items you may specify value as you want (inside array, hash, string and so on)
---
schema: # required attribute of schema declaration, if this is array - add '[]', if optional - add '?' on key end
=: # this symbol for declaration of validations of this schema-element
- type object
fio: # inside field 'fio' declaration
=:
- type object
age:
=:
- type number
- max_value 100
- min_value 16
family[]: # this is array field 'family' with objects
=:
- type array
- min_length 2
- each:
- type object ## inside of validation
first_name:
=:
- type string
- min_length 3
- max_length 20
last_name:
=:
- type string
- min_length 3
- max_length 20
middle_name?:
=:
- type string
- min_length 3
- max_length 20
age?: # this is optional field
=: [ 'type number', 'max_value 100', 'min_value 16' ]
education[]:
=:
- type array
- min_length 2
- not empty
- each:
- type string
- min_length 3
- max_length 20
name:
=:
- type string
type:
=:
- type string
classes[]?: # this optional array field 'classes'
YAML short syntax:
if your schema of field has no nested fields - you can specify validation items as array without validation attribute '='
---
schema:
=:
- type object
fio:
- type object
age:
- type number
- max_value 100
- min_value 16
family[]:
=:
- type array
- min_length 2
- each:
- type object
first_name:
- type string
- min_length 3
- max_length 20
last_name:
- type string
- min_length 3
- max_length 20
middle_name?:
- type string
- min_length 3
- max_length 20
age?: [ 'type number', 'max_value 100', 'min_value 16' ]
education[]:
=:
- type array
- min_length 2
- not empty
- each:
- type string
- min_length 3
- max_length 20
name:
- type string
type:
- type string
classes[]?:
Previous YAML examples equal next js code
// js equivalent
var schema = new Schema().validate('type object').object(function (r, o) {
r('fio', 'type object', function (r, o) {
r('first_name', ['type string', 'min_length 3', 'max_length 20']);
r('last_name', ['type string', 'min_length 3', 'max_length 20']);
o('middle_name', ['type string', 'min_length 3', 'max_length 20']);
});
r('age', ['type number', 'max_value 100', 'min_value 16']);
r('family', ['type array', 'min_length 2', {each: ['type object']}]).array(function (r, o) {
r('first_name', ['type string', 'min_length 3', 'max_length 20']);
r('last_name', ['type string', 'min_length 3', 'max_length 20']);
o('middle_name', ['type string', 'min_length 3', 'max_length 20']);
o('age', ['type number', 'max_value 100', 'min_value 16']);
});
r('education', ['type array', 'min_length 2', 'not empty', {each: ['type string', 'min_length 3', 'max_length 20']}]).array(function (r, o) {
r('name', 'type string');
r('type', 'type string');
o('classes').array();
});
})
Schema.ValidationError(rule [, params[, index]])
For rule you should use a valid case name.
For example:
wrong:
rule='excess_field' with rule = $fileName ($fieldName - field, that was excess)
good:
rule='available_fields' with params=['first_name', 'last_name'...]
you should put available fields in this case.
This approach creates one way to process validation errors.
And in future you will not have problems with extension of your API and schema.
And this formulation (in positive) get more information for API user
(in this example you get all available values of field names and can modify them correctly next time).
This system of validation errors has no end message - for multi-language support. You can create simple function for mapping ValidationError to user-friendly message (with current user language).
var messages = {
// rule -> template
required: function (validationError, options) {
return 'field must be required';
},
available_fields: function (validationError, options) {
return 'field must include only this values [' + validationError.params.join(',') + '], "' + validationError.value + '" given';
}
};
Schema.ValidationError(rule [, params[, index]])
rule: String
- required - rule name that failed.
params: Mixed
- optional - all parameters to help user understand where mistake is .
index: Null|Number
- optional - failed item's index.
Schema.ValidationError extends Error.
Destination: for custom validations.
Schema.ValidationError('required');
// eq
new Schema.ValidationError('required', null, null);
after verification all errors have next properties:
error.rule - name of rule. for example: 'type'
error.params - params of rule. for example: 'object'
error.path - json path of failed value. for example: ['hello']['world']['0']['foo']['3']['bar']
error.value - failed value. for example 3
System predefined errors:
Schema.ValidationError('type', 'array',
value
,path
).
returned ifschema.isArray
was not compatible with value type (isArray
= true, but value is not Array).
{ rule: 'type', params: 'array', value:value
, path:path
}.Schema.ValidationError('type', 'object',
value
, null,path
).
returned ifschema.isArray
was not compatible with value type (isArray
= false, but value is not Object).
{ rule: 'type', params: 'array', value:value
, path:path
}.Schema.ValidationError('available_fields',
fields
,value
, null,path
).
returned if value object has field, that not specified in schema.
{ rule: 'available_fields', params:fields
, value:value
, path:path
}.
You can ignore this error, if setoptions.ignoreExcess
=true.Schema.ValidationError('required', true,
value
, null,path
).
returned if value object is undefined and flagschema.isRequired
=true.
{ rule: 'required', params: true, value:value
, path:path
}.
path - json selector - address to current mistake value (Array).