Functional JSON validator
- Minimal, ~200 LoC, no dependencies.
- Simple, validators are just functions, no special DSL to learn
- Powerful, compose basic/custom validators to build complex ones
npm install --save f-validator
const{ string, number, optional, or, objectOf, arrayOf, error }=require('f-validator')constequal=require('assert').deepStrictEqual// Every validator is just a functionconstvalidator1=stringequal(validator1('I am a string'),null)// passed check -> null// Some validator can use other validators// e.g. `or`, `optional`, `objectOf`, `arrayOf`constvalidator2=or(string,number)// To validate object, `objectOf` takes a schema where each field is a validatorconstvalidator3=objectOf({a: or(string,number),b: objectOf({c: optional(string)})})// To validate array, `arrayOf` takes a validator which will be used to check every elementconstvalidator4=arrayOf(validator3)constgood={a: 5,b: {c: 'I am also a string'}}constbad={a: 5,b: {c: 42}}equal(validator4([good,good]),null)equal(validator4([good,bad]),{// an object describing the diffpath: [1,'b','c'],// key-path pointing to the exact field to blameexpected: 'or(null or undefined, string)',received: 42,message: 'Path:\'1.b.c\', Expected: or(null or undefined, string), Received: \'42\''})// Sometimes, you'll need your own validator// any function can be used as a 'Validator' if it has this signature:/** * @param any subject, subject to check * @param []string path, a key-path pointing to the field * @returns * null, if it passes the check * Object error, an error object created with `error` utility */// Example 1: a humble even number validatorconstevenNumber=(subject,path=[])=>{if(typeofsubject=='number'&&(subject%2===0)){returnnull}else{returnerror(path,'an even number',subject)// keypath, expected, received}}// now it can be used by other validatorconstarrayOfEvenNumber=arrayOf(evenNumber)equal(arrayOfEvenNumber([2,4,6,9]),{path: [3],// the 4th elementexpected: 'an even number',received: 9,message: 'Path:\'3\', Expected: an even number, Received: \'9\''})// Example 2: a stringified-json-object-conforming-to-a-given-validator validatorconstjsonString=validator=>(subject,path=[])=>{letparsedtry{parsed=JSON.parse(subject)}catch(e){returnerror(path,`json string of (${validator.name})`,subject)}returnvalidator(parsed,path)// note: `path` should be passed to it}error(path:[]string, expected:string, received:any, message:?string ) - create an error object describing the diff, use it when creating your own validator
object
array
string
number
boolean
date
regexp
Null - note the initial
Undefined - note the initial
empty - valid if value is null or undefined
regex(re:regexp) - valid if value matches regex re
objectOf(schema:object) - valid if value is an object where for every key of schema, value[key] is validated by schema[key]
arrayOf(v:Validator) - valid if value is an array where every element is validated by v
jsonString(v:Validator) - valid if value is an stringified object that is validated by v
not(v:validator) - valid if value is invalidated by v
any - always valid
and(...vs:[]validator) - valid if value is validated by every validator of vs
or(...vs:[]validator) - valid if value is validated by every validator of vs
optional(v:validator) - valid if value is either validated by v or null/undefined
is(ref:any) - valid if value is strictly deeply equal to ref
oneOf(...refs:[]any) - valid if value is strictly deeply equal to any element of refs
like(ref:any) - valid if value has the same type/structure as ref (recursively)
Example:
constreference={a: 42,b: 'I am string',c: {d: ['apple']},e: optional(string)}constvalue={a: 0,b: 'I am another string',c: {d: ['pear','microsoft']},e: 'yep',// functions in ref are taken as validatorsextra: 'I am an extra field, I will be fine'}like(reference)(value)// null (is valid)