lx-valid v1.2.4
lx-valid
A JSON schema validator for Node.js/io.js and the client, based on Flatiron Revalidator.
The idea
Nodejitsu's Revalidator is a great JSON schema validator and therefore the backbone of lx-valid. But in practice there is often a need for a more general validator for simple checks that works on the client as well as on the server. The basic concept is to extend Revalidator and create a more complete validation framework that allows for simple checks without the need of defining a JSON schema and that also supports string filtering and data type conversion.
An example
Since lx-valid's core is based on Revalidator, lx-valid is fully compatible to the latter. You can find more information about Revalidator here: https://github.com/flatiron/revalidator. The Revalidator example showing schema validation works exactly the same with lx-valid:
var val = require('lx-valid'),
someObject = {
url: 'http://www.litixsoft.de',
mission: 'change the world',
body: 'Chuck Norris'
},
schema = {
properties: {
url: {
description: 'Company url',
type: 'string',
pattern: '^/[^#%&*{}\\:<>?\/+]+$',
required: true
},
mission: {
description: 'Company mission',
type: 'string',
minLength: 5
},
body: {
description: 'WAT',
type: 'any',
default: null
}
}
},
res = val.validate(someObject, schema);
console.log(res);
Installation
npm install lx-valid
Schema validation
For schema validation lx-valid requires an object
to be validated and a JSON schema.
lx-valid.validate(obj, schema, options)
The validate method returns an object with information on the tested object matching the rules from the JSON schema. In case the validation failed (rules are violated), the returned object also contains an array with the validation errors.
{
valid: true // or false
errors: [/* in case valid: false, an array with validation rule violations */]
}
Available Options
- validateFormats: Enforce format constraints ( default:
true
) - validateFormatsStrict: When
validateFormats
istrue
treat unrecognized formats as validation errors ( defaultfalse
) - validateFormatExtensions: When
validateFormats
istrue
also validate formats defined invalidate.formatExtensions
. This option is used for lx-valid format extensions and additional custom formats. Those are stored in here. ( default:true
) - cast: Enforce casting of some types (for integers/numbers are only supported) when it's possible,
e.g.
"42" => 42
, but"forty2" => "forty2"
for the integer type. Modifies the original object. ( default:undefined
) - unknownProperties: Defines how properties which are not declared in the schema should be handled. ( default:
ignore
)ignore
: The properties are ignored in validation and are not deleted.delete
: The properties are deleted.error
: The properties are treated as error and are not deleted.
- convert: Converts a property by the format defined in the schema. Modifies the original object. ( default:
undefined
) - trim: Trims all properties of type
string
. Modifies the original object. ( default:false
) - strictRequired: Sets validity of empty
string
tofalse
. ( default:false
) - ignoreNullValues: Do not validate properties of type
null
. ( default:false
) - transform: A
function
that runs for each property. ( default:null
) It has one parameter "data" which looks like this:- data.object
{object}
- The object which is validated. - data.value
{*}
- The value of the current property. - data.property
{string}
- The name of the current property. - data.schema
{object}
- The schema of the current property. - data.options
{object}
- The options of the validation function. - data.errors
{array}
- The array with the validation errors which encountered until yet.
- data.object
Validation types
For a property an value
is that which is given as input for validation where as an expected value
is the value
of the below fields.
required
If true
, the value should not be empty.
type
The type of value should be equal to the expected value.
{ type: 'string' }
{ type: 'number' }
{ type: 'integer' }
{ type: 'float' }
{ type: 'array' }
{ type: 'boolean' }
{ type: 'object' }
{ type: 'null' }
{ type: 'regexp' }
{ type: 'any' }
{ type: 'undefined' }
{ type: 'mongoId' }
{ type: 'dbRef' }
{ type: 'minKey' }
{ type: 'maxKey' }
{ type: 'code' }
{ type: 'nan' }
{ type: 'infinity' }
{ type: ['boolean', 'string'] }
Example for types
var val = require('lx-valid'),
typeTest = {
stringTest: "test",
arrayTest: [1, 2, 3],
boolTest: "test"
},
schemaTest = {
"properties": {
"IntTest": {
"type": "integer",
"id": "IntTest",
"required": false
},
"arrayTest": {
"type": "array",
"id": "arrayTest",
"required": false,
"maxItems": 3,
"items": {
"type": "integer",
"id": "0",
"required": false
}
},
"boolTest": {
"type": "boolean",
"id": "boolTest",
"required": true
},
"stringTest": {
"type": "string",
"id": "stringTest",
"required": true
}
}
},
res = val.validate(typeTest, schemaTest);
console.log(res);
The integer value and the array are not required to be present in the tested object,
so there will be no errors concerning the missing intTest property. The boolTest however will fail since the
the expected type is boolean
while the property contains a string
.
Validation rules
pattern
The expected value regex needs to be satisfied by the value.
{ pattern: /^[a-z]+$/ }
maxLength ( string and array )
The length of value must be greater than or equal to expected value.
{ maxLength: 8 }
minLength ( string and array )
The length of value must be lesser than or equal to expected value.
{ minLength: 8 }
minimum ( number )
Value must be greater than or equal to the expected value.
{ minimum: 10 }
maximum ( number )
Value must be lesser than or equal to the expected value.
{ maximum: 10 }
exclusiveMinimum ( number )
Value must be greater than expected value.
{ exclusiveMinimum: 9 }
exclusiveMaximum ( number )
Value must be lesser than expected value.
{ exclusiveMaximum: 11 }
divisibleBy ( number )
Value must be divisible by expected value.
{ divisibleBy: 5 }
{ divisibleBy: 0.5 }
minItems ( array )
Value must contain more then expected value number of items.
{ minItems: 2 }
maxItems ( array )
Value must contains less then expected value number of items.
{ maxItems: 5 }
uniqueItems ( array )
Value must hold a unique set of values.
{ uniqueItems: true }
enum (array )
Value must be present in the array of expected value.
{ enum: ['month', 'year'] }
Example for rules
var val = require('lx-valid'),
typeTest = {
stringTest: "test",
arrayTest: [1, 2, 3],
boolTest: "test"
},
schemaTest = {
"properties": {
"arrayTest": {
"type": "array",
"required": false,
"maxItems": 3,
"items": {
"type": "integer",
"id": "0",
"required": false
}
}
}
},
res = val.validate(typeTest, schemaTest);
console.log(res);
Validation will be successful, since the array contains 3 values, all of integer type.
Validation formats
The value should match a valid format. The format is only validated when the value is of type string
.
{ format: 'mongo-id' }
{ format: 'number-float' }
{ format: 'float' }
{ format: 'integer' }
{ format: 'url' }
{ format: 'email' }
{ format: 'ip-address' }
{ format: 'ipv6' }
{ format: 'date-time' }
{ format: 'date' }
{ format: 'time' }
{ format: 'color' }
{ format: 'host-name' }
{ format: 'utc-millisec' }
{ format: 'regex' }
{ format: 'empty' }
{ format: 'uuid' }
{ format: 'luuid' }
{ format: 'timestamp' }
Example for format
var val = require('lx-valid'),
objForTest = {
UuidTest: "507f191e810c19729de860ea",
floatTest: 3.2,
IntTest: 2,
EmptyEmailTest: ''
},
schemaForTest = {
"properties": {
"UuidTest": {
"type": 'string',
"required": true,
"format": 'mongo-id'
},
"IntTest": {
"type": "integer",
"required": false
},
"floatTest": {
"type": "number",
"required": false,
"format": 'number-float'
},
"ip": {
"type": "string",
"format": ['ip-address', 'ipv6']
},
"emptyMail": {
"type": "string",
"format": ['empty', 'email']
}
}
},
result = val.validate(objForTest, schemaForTest);
console.log(result);
In this case values will be tested against predefined formats. The UuidTest property's value should be a string matching the format of a MongoDB ObjectId. The floatTest value should be a number matching a float. If additional characters are added to UuidTest's value, validation fails. It is also supported to validate an array of formats.
Extension by custom formats
Additionally the predefined formats can be extended by custom ones as the following example shows:
var val = require('lx-valid'),
testFormat = /^Test[0-9]{2}Test$/,
objForTest = {
OwnFormatTest: "Test24Test"
},
schemaForTest = {
"properties": {
"OwnFormatTest": {
"type": 'string',
"required": true,
"format": 'test-format'
}
}
};
try {
val.extendFormat('test-format', testFormat);
}
catch (e) {
console.log(e);
}
var res = val.validate(objForTest, schemaForTest);
console.log(res);
After extending the validator with the custom format it's accessible by any schema anytime. Custom formats can be registered with the validator only once. At this point an exception will be thrown in case a format already exists with the same key. This will be subject to change in future versions.
"Conform" custom validation
Value must conform to constraint denoted by expected value. Conform allows for extensive validation.
A conform validator is a function accepting the value to the validated as single parameter.
The return value of the function must be boolean true
or false
. The conform function is also executed for undefined values.
{
conform: function (v) {
if (v % 3 == 1) {
return true;
}
return false;
}
}
{
name: {
type: 'string'
},
verifiedName: {
type: 'string',
conform: function (actual, original) {
if (actual === original.name) {
return true;
}
return false;
}
}
}
Dependencies
Value is valid only if the dependent value is valid.
{
town: {
required: true,
dependencies: 'country'
},
country: {
required: true,
maxLength: 3
}
}
Nested Schema
We also allow nested schema.
{
properties: {
title: {
required: true,
type: 'string',
maxLength: 140
},
author: {
type: 'object',
required: true,
properties: {
name: {
required: true,
type: 'string'
},
email: {
type: 'string',
format: 'email'
}
}
}
}
}
Custom Messages
We also allow custom message for different constraints.
{
type: 'string',
format: 'url',
conform: function (value, instance) { ... },
messages: {
type: 'Not a string type',
format: 'Expected format is a url',
conform: 'Failed to pass custom validation',
message: 'This can be used as a global message'
}
}
Convert option
Converts a property by the format defined in the schema. Modifies the original object.
var data = {
birthdate: '1979-03-01T15:55:00.000Z'
},
schema = {
properties: {
birthdate: {
type: 'string',
format: 'date-time'
}
}
},
convertFn = function (format, value) {
if (format === 'date-time') {
return new Date(value);
}
return value;
},
res = validate(data, schema, {convert: convertFn});
// birthdate was converted
console.log(typeof data.birthdate === 'object'); // true
Schema validation when updating data
When working with db's, you often update data. Sometimes you send just a part of the data and the the schema validation will be false because some required fields may be missing.
To prevent this there is an option isUpdate
. When set to true
all required fields in the schema which are not part of the data are set to required: false
.
There is a helper function to get the validate function with this update check.
var val = require('lx-valid'),
data = {name: 'wayne'},
schema = {
properties: {
id: {
type: 'int',
required: true
},
name: {
type: 'string',
required: false
}
}
},
valFn = val.getValidationFunction(),
res = valFn(data, schema, {isUpdate: true});
console.log(res.valid); // true
console.log(res.errors.length); // 0
Validation without schema
There is a simple API allowing for testing types, rules and formats without having to define a schema.
lx-valid.formats.<formatname>(value)
lx-valid.types.<typename>(value)
lx-valid.rules.<rulename>(value)
An object is returned that contains information on the tested value matching the rule. In case of rule violation the returned object contains an array with the validation errors.
{
valid: true // or false
errors: [/* in case valid: false, an array with validation rule violations */]
}
Types
The types in lx-valid are additions to the JSON schema types to support all JavaScript value types.
lx-valid.types.<rulename>(value)
// returns the full validation object
or
lx-valid.isType(value)
// returns boolean
JSON schema types
{ type: 'string' }
{ type: 'number' }
{ type: 'integer' }
{ type: 'array' }
{ type: 'boolean' }
{ type: 'object' }
{ type: 'null' }
{ type: 'any' }
{ type: ['boolean', 'string'] }
lx-valid types
{ type: 'string' }
{ type: 'number' }
{ type: 'boolean' }
{ type: 'object' }
{ type: 'undefined' }
{ type: 'integer' }
{ type: 'float' }
{ type: 'array' }
{ type: 'date' }
{ type: 'regexp' }
{ type: 'null' }
{ type: 'mongoId' }
{ type: 'dbRef' }
{ type: 'minKey' }
{ type: 'maxKey' }
{ type: 'code' }
{ type: 'nan' }
{ type: 'infinity' }
All JSON schema types are supported and additionally all JavaScript types.
integer
var res = val.types.integer(123);
val.isInteger(123);
float
var res = val.types.float(12.3);
val.isFloat(12.3);
regexp
var res = val.types.regexp(/^hello/);
val.isRegexp(/^hello/);
date
var res = val.types.date(new Date());
is.isDate(new Date());
There are more examples to be found in test/types.spec.js.
Formats
Just like in JSON schema validation, values can be tested against predefined formats.
lx-valid.formats.<formatname>(value)
{ format: 'mongoId' }
{ format: 'numberFloat' }
{ format: 'float' }
{ format: 'integer' }
{ format: 'url' }
{ format: 'email' }
{ format: 'ipAddress' }
{ format: 'ipv6' }
{ format: 'dateTime' }
{ format: 'date' }
{ format: 'time' }
{ format: 'color' }
{ format: 'hostName' }
{ format: 'utcMillisec' }
{ format: 'regex' }
{ format: 'empty' }
{ format: 'uuid' }
{ format: 'luuid' }
{ format: 'timestamp' }
ipAddress
var res1 = val.formats.ipAddress("192.168.1.10");
ipv6
var res1 = val.formats.ipv6("2001:0db8:85a3:08d3:1319:8a2e:0370:7344");
dateTime
var res1 = val.formats.dateTime("2013-01-09T12:28:03.150Z");
More examples can be found in test/formats.spec.js
Rules
Of course the API also supports testing against rules.
lx-valid.rules.<rulename>(value,ruleValue)
{ pattern: /^[a-z]+$/ }
{ maxLength: 8 }
{ minLength: 8 }
{ minimum: 10 }
{ maximum: 10 }
{ exclusiveMinimum: 9 }
{ exclusiveMaximum: 11 }
{ divisibleBy: 5 }
{ minItems: 2 }
{ maxItems: 5 }
{ uniqueItems: true }
{ enum: ['month', 'year'] }
maxLength
var res = val.rules.maxLength("test",4);
minLength
var res = val.rules.minLength("test",2);
divisibleBy
var res = val.rules.divisibleBy(6,3);
More examples are to be found in test/rules.spec.js
Asynchronous validation
The Asynchronous validation consists of registration of validators and the execution of validators.
lx-valid.asyncValidate.register(function, value)
lx-valid.asyncValidate.exec(validationResult, callback)
lx-valid.asyncValidate.exec
executes the registered validators in parallel.
It is best practice to first execute the schema validation and than execute the asynchronous validation. The result of schema validation is passed to the asynchronous validation.
// json schema validate
var val = require('lx-valid'),
valResult = val.validate(doc, schema);
// register async validator
val.asyncValidate.register(function1, value1);
val.asyncValidate.register(function2, value2);
// async validate
val.asyncValidate.exec(valResult, callback);
More examples are to be found in test/tests.spec.js
Development
The lx-valid validator is currently under development. Scheduled functions are implemented step by step. Please refer to the changelog and roadmap for further information on development progress.
Author
License
(The MIT License)
Copyright (C) 2013-2016 Litixsoft GmbH info@litixsoft.de
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
8 years ago
8 years ago
8 years ago
8 years ago
9 years ago
9 years ago
9 years ago
9 years ago
9 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago