Valivar
Mostly derived from eivindfjeldstad/validate; Valivar provides dynamic schemas for modern javascript and typescript validation and sanitization. In Valivar you can use catch-all keys $ and * to define any array or object respectively, letting you define a single schema that can apply consistent rules for growing and changing datasets.
Install
Node.js or Browser
npm install valivarDeno
import valivar from "https://deno.land/x/valivar/mod.ts"Import in HTML
<script type="text/javascript" src="dist/valivar.js"></script>From CDN
<script type="text/javascript" src="https://unpkg.com/valivar"></script>Examples
For a full list see the examples directory (coming)
Play with it
Reademe rewrite in progress…
Usage
Define a schema and call .validate() with the object you want to validate.
The .validate() function returns an array of validation errors.
import { Schema } from 'valivar'
const user = new Schema({
username: {
type: String,
required: true,
length: { min: 3, max: 32 }
},
pets: [{
name: {
type: String
required: true
},
animal: {
type: String
enum: ['cat', 'dog', 'cow']
}
}],
address: {
street: {
type: String,
required: true
},
city: {
type: String,
required: true
}
zip: {
type: String,
match: /^[0-9]+$/,
required: true
}
}
})
const errors = user.validate(obj)Each error has a .path, describing the full path of the property that failed validation, and a .message describing the error.
errors[0].path //=> 'address.street'
errors[0].message //=> 'address.street is required.'Custom error messages
You can override the default error messages by passing an object to Schema.message().
const post = new Schema({
title: { required: true }
})
post.message({
required: (path) => `${path} can not be empty.`
})
const [error] = post.validate({})
assert(error.message = 'title can not be empty.')It is also possible to define messages for individual properties:
const post = new Schema({
title: {
required: true,
message: 'Title is required.'
}
})And for individual validators:
const post = new Schema({
title: {
type: String,
required: true,
message: {
type: 'Title must be a string.',
required: 'Title is required.'
}
}
})Nesting
Objects and arrays can be nested as deep as you want:
const event = new Schema({
title: {
type: String,
required: true
},
participants: [{
name: String,
email: {
type: String,
required: true
},
things: [{
name: String,
amount: Number
}]
}]
})Arrays can be defined implicitly, like in the above example, or explicitly:
const post = new Schema({
keywords: {
type: Array,
each: { type: String }
}
})Array elements can also be defined individually:
const user = new Schema({
something: {
type: Array,
elements: [
{ type: Number },
{ type: String }
]
}
})Nesting also works with schemas:
const user = new Schema({
name: {
type: String,
required: true
},
email: {
type: String,
required: true
}
})
const post = new Schema({
title: {
type: String,
required: true
},
content: {
type: String,
required: true
},
author: user
})If you think it should work, it probably works.
Naming conflicts
Validate will naively assume that a nested object where all property names are validators is not a nested object.
const schema = new Schema({
pet: {
type: {
required: true,
type: String,
enum: ['cat', 'dog']
}
}
});In this example, the pet.type property will be interpreted as a type rule, and the validations will not work as intended. To work around this we could use the slightly more verbose properties rule:
const schema = new Schema({
pet: {
properties: {
type: {
required: true,
type: String,
enum: ['cat', 'dog']
}
}
}
});In this case the type property of pets.properties will be interpreted as a nested property, and the validations will work as intended.
Custom validators
Custom validators can be defined by passing an object with named validators to .use:
const hexColor = val => /^#[0-9a-fA-F]$/.test(val)
const car = new Schema({
color: {
type: String,
use: { hexColor }
}
})Define a custom error message for the validator:
car.message({
hexColor: path => `${path} must be a valid color.`
})Custom types
Pass a constructor to .type to validate against a custom type:
class Car {}
const user = new Schema({
car: { type: Car }
})Chainable API
If you want to avoid constructing large objects, you can add paths to a schema by using the chainable API:
const user = new Schema()
user
.path('username').type(String).required()
.path('address.zip').type(String).required()Array elements can be defined by using $ as a placeholder for indices:
const user = new Schema()
user.path('pets.$').type(String)This is equivalent to writing
const user = new Schema({ pets: [{ type: String }]})Typecasting
Values can be automatically typecast before validation.
To enable typecasting, pass an options object to the Schema constructor with typecast set to true.
const user = new Schema(definition, { typecast: true })You can override this setting by passing an option to .validate().
user.validate(obj, { typecast: false })To typecast custom types, you can register a typecaster:
class Car {}
const user = new Schema({
car: { type: Car }
})
user.typecaster({
Car: (val) => new Car(val)
})Property stripping
By default, all values not defined in the schema will be stripped from the object.
Set .strip = false on the options object to disable this behavior. This will likely be changed in a future version.
Strict mode
When strict mode is enabled, properties that are not defined in the schema will trigger a validation error. Set .strict = true on the options object to enable strict mode.