@cleverplatypus/tiny-object-validator
v1.0.7
Published
Small library to organise JavaScript/TypeScript objects' validation
Readme
Tiny Object Validator
A small library to handle validation of an object's properties using field rules.
'Tiny' because it only concern itself with providing a mechanism to inspect and object and applying user defined rules rather than duplicating the effort of well established validation libraries such as validator.js.
Typically used in conjunction with the latter to do the actual data validation.
Why I built this library
In a nutshell, I needed a pure JavaScript approach to data validation in Vue3, not relying on any logic held in view components or clunky HTML5 data validation.
Installation
yarn add @cleverplatypus/tiny-object-validatoror
npm i --save @cleverplatypus/tiny-object-validatorGetting Started
Let's say we have a model object populated by our UI for user registration:
const model = {
email : 'test@example',
password : '123456',
acceptConditions : false
}The validation result will be stored in a ValidationState object that we'll need to pass to validate.
const stateObj: ValidationState = {
isValid : false,
fields : {}
}The false value for each field is the validation initial state which means that the field hasn't been validated (yet).
Then, we define the rules:
const fields : ValidationFields = [
{
name : 'email',
emptyFieldMessage : 'Your email address is required'
tests : [
{
fn : isEmail //using validator.js' on isEmail function
message : ({source}) => `${source.email} doesn't seem to be a valid email`
}
]
}, {
name : 'password',
tests : [
{
fn : value => !['123456', 'password'].includes(value),
message : 'Seriously?!?'
}, {
fn : value => value.length >= 8,
message : 'The password needs to be at least 8 characters long'
}
],
stopOnFailure : 'tests'// the first test to fail will stop tests evaluation and set the field's failure message
}, {
name : 'acceptConditions',
tests : [
{
fn : value => !!value,
message : 'Please accept term and conditions'
}
]
}
]Finally, we can validate our data.
The ObjectValidator only needs to be instanciated once. Our app will call the validate method whenever appropriate, either on form input or form submit.
const validator = new ObjectValidator(fields);
const valid = await validator.validate({source : model, stateObj});
stateObj will be mutated atomically at the end of the validation and it will look like this:
{
isValid : false,
fields : {
email : '"test@example" doesn\'t seem to be a valid email',
password : 'Are you serious?!?',
acceptConditions : true
}
}stateObj : ValidationState
This object, passed to validate, gets populated with the validation result.
isValid will be set to true if validation succedes
fields will be filled with all the source data object properties paths to be validated
The value for each fields entry will be:
falseif the field wasn't validatedtrueif the field passed validation- a string with the validation failure description
Deep fields can be declared using dot notation, such as address.postal_code
Note for Vue3 developers
If we're relying on Vue3 reactivity for the UI to see stateObj to give feedback to the client, the fields property must be populated like this:
const stateObj: ValidationState = {
isValid : false,
fields : {
email : false,
password : false,
acceptConditions : true
}
}The context object
An optional object/array can be passed to validate to provide additional data/functions to the validation process.
A context object (ValidationContext) will be passed to:
- tests'
fnandmessagewhen the latter is a function - fields'
skipIf - fields'
emptyFieldMessagewhen it's a function
This could be used to provide additional information/functionality when processing validation.
For instance, it can be used when a field depends on other field's value for validation.
const data = {
locale : 'US',
postal_code : '12345'
}
const fields = [
{
name : 'locale',
tests : [
{
fn : value => ['US', 'AU', 'UK', 'CA'].includes(value)
emptyFieldMessage : 'Please select a country'
}
]
}, {
name : 'postal_code',
skipIf : ({source}) => !source.locale,
tests : [
{
fn : (value, {source}) => isPostalCode(value, source.locale),
message : `This doesn't seem like a postal code for the selected country`
}
]
}
]
validator.validate({
source : myData,
stateObj
});
Interrupting validation
It's possible to prematurely interrupt validation at two levels:
teststhe currently evaluated array of tests for a field will stop and the latest validation result for the field will be usedfieldsthe whole validation will terminate after setting the current field's validation result.
This is handled at field's level using stopOnSuccess (which makes tests behave in a or fashion) or stopOnFailure (which makes tests behave in a and fashion).
const fields = [
{
name : 'ip_address',
tests : [
{
fn : isIP4
}, {
fn : isIP6,
message : 'Please enter either a IPv4 or a IPv6 address'
}
],
stopOnSuccess : 'tests'
}
]