nswbmw / another-json-schema

Another JSON Schema validator, simple & flexible & intuitive.
48 stars 8 forks source link
json-schema schema validator

another-json-schema

Another JSON Schema, simple & flexible & intuitive.

NPM version Build status Dependency Status License Downloads

Install

$ npm i another-json-schema --save

Usage

Simple

const AJS = require('another-json-schema')

const userSchema = AJS('userSchema', {
  name: { type: 'string', required: true },
  age: { type: 'number', gte: 18 },
  gender: { type: 'string', enum: ['male', 'female'], default: 'male' },
  email: { type: 'string', trim: true, isEmail: true }
})

// test `required`
console.log(userSchema.validate({ age: 18 }))
/*
{ valid: false,
  error:
   { Error: ($.name: undefined) ✖ (required: true)
     validator: 'required',
     actual: undefined,
     expected: { type: 'string', required: true },
     path: '$.name',
     schema: 'userSchema' },
  result: { age: 18 } }
*/

// test `default`
const data = { name: 'nswbmw', age: 18 }
console.log(userSchema.validate(data))
/*
{ valid: true,
  error: null,
  result: { name: 'nswbmw', age: 18, gender: 'male' } }
*/
console.log(data)
// { name: 'nswbmw', age: 18, gender: 'male' }

// test `enum`
console.log(userSchema.validate({ name: 'nswbmw', age: 18, gender: 'lalala' }))
/*
{ valid: false,
  error:
   { Error: ($.gender: "lalala") ✖ (enum: male,female)
     validator: 'enum',
     actual: 'lalala',
     expected: { type: 'string', enum: ['male', 'female'], default: 'male' },
     path: '$.gender',
     schema: 'userSchema' },
  result: { name: 'nswbmw', age: 18, gender: 'lalala' } }
*/

// test `gte`
console.log(userSchema.validate({ name: 'nswbmw', age: 17 }))
/*
{ valid: false,
  error:
   { Error: ($.age: 17) ✖ (gte: 18)
     validator: 'gte',
     actual: 17,
     expected: { type: 'number', gte: 18 },
     path: '$.age',
     schema: 'userSchema' },
  result: { name: 'nswbmw', age: 17 } }
*/

// test `isEmail`
console.log(userSchema.validate({ name: 'nswbmw', email: 'myEmail' }))
/*
{ valid: false,
  error:
   { Error: ($.email: "myEmail") ✖ (isEmail: true)
     validator: 'isEmail',
     path: '$.email',
     actual: 'myEmail',
     expected: { type: 'string', trim: true, isEmail: true },
     schema: 'userSchema' },
  result: { name: 'nswbmw', email: 'myEmail', gender: 'male' } }
*/

Nested

const AJS = require('another-json-schema')

const userSchema = AJS('userSchema', {
  _id: { type: 'string', pattern: /^[0-9a-z]{24}$/, required: true },
  name: { type: 'string' },
  age: { type: 'number', gte: 18 },
  gender: { type: 'string', enum: ['male', 'female'] }
})

const commentSchema = AJS('commentSchema', {
  _id: { type: 'string', pattern: /^[0-9a-z]{24}$/, required: true },
  user: userSchema,
  content: { type: 'string' }
})

const postSchema = AJS('postSchema', {
  _id: { type: 'string', pattern: /^[0-9a-z]{24}$/, required: true },
  author: userSchema,
  content: { type: 'string' },
  comments: [commentSchema]
})

const post = {
  _id: 'post11111111111111111111',
  author: {
    _id: 'user11111111111111111111',
    name: 'nswbmw',
    age: 100,
    gender: 'male',
    pet: 'cat'
  },
  content: 'lalala',
  comments: [{
    _id: 'comment11111111111111111',
    user: {
      _id: 'wrong_id',
      name: 'user1',
      age: 100,
      gender: 'male'
    },
    content: 'sofa'
  }]
}

console.log(postSchema.validate(post))
/*
{ valid: false,
  error:
   { [Error: ($.comments[].user._id: "wrong_id") ✖ (pattern: /^[0-9a-z]{24}$/)]
     validator: 'pattern',
     actual: 'wrong_id',
     expected: { type: 'string', pattern: /^[0-9a-z]{24}$/, required: true },
     path: '$.comments[].user._id',
     schema: 'userSchema' },
  result:
   { _id: 'post11111111111111111111',
     author:
      { _id: 'user11111111111111111111',
        name: 'nswbmw',
        age: 100,
        gender: 'male' },
     content: 'lalala',
     comments: [ [Object] ] } }
*/

Custom Error Message

const AJS = require('another-json-schema')

const userSchema = AJS('userSchema', {
  name: { type: 'string', required: true },
  age: {
    type: 'number',
    gte: 18,
    _customErrorMsg: {
      gte: '您未满 18 岁'
    }
  }
})

// test `_customErrorMsg`
console.log(userSchema.validate({
  name: 'nswbmw',
  age: 17
}))
/*
{ valid: false,
  error:
   { Error: 您未满 18 岁
     validator: 'gte',
     path: '$.age',
     actual: 17,
     expected: { type: 'number', gte: 18, _customErrorMsg: [Object] },
     schema: 'userSchema' },
  result: { name: 'nswbmw', age: 17 } }
*/

Register validator

const AJS = require('another-json-schema')

AJS.register('adult', function (actual, expected, key, parent) {
  return expected ? (actual > 18) : (actual <= 18)
})
const adultSchema = AJS('adultSchema', { type: 'number', adult: true })

console.log(adultSchema.validate(19))
// { valid: true, error: null, result: 19 }
console.log(adultSchema.validate(17))
/*
{ valid: false,
  error:
   { Error: ($: 17) ✖ (adult: true)
     validator: 'adult',
     actual: 17,
     expected: { type: 'number', adult: true },
     path: '$',
     schema: 'adultSchema' },
  result: 17 }
*/

Custom type validate function

Custom ObjectId validator, check whether ObjectId then wrap _id string to ObjectId.

const AJS = require('another-json-schema')
const validator = require('validator')
const toObjectId = require('mongodb').ObjectId

function ObjectId(actual, key, parent) {
  if (!actual || !validator.isMongoId(actual.toString())) {
    return false
  }
  parent[key] = toObjectId(actual)
  return true
}

const postSchema = AJS('postSchema', {
  commentIds: [{ type: ObjectId }]
})

const user = {
  commentIds: [
    '111111111111111111111111',
    '222222222222222222222222'
  ]
}

console.log(postSchema.validate(user))
/*
{ valid: true,
  error: null,
  result: { commentIds: [ 111111111111111111111111, 222222222222222222222222 ] } }
*/

//validate specific field
console.log(postSchema._children.commentIds.validate('lalala'))
/*
{ valid: false,
  error:
   { Error: ($.commentIds[]: "lalala") ✖ (type: ObjectId)
     validator: 'type',
     path: '$.commentIds[]',
     actual: 'lalala',
     expected: [ [Object] ],
     schema: 'postSchema' },
  result: 'lalala' }
*/

Internal type validator

What's difference between number and AJS.Types.Number ? number only check type, AJS.Types.Number will try to convert value to a number, if failed then throw error.

const AJS = require('/Users/nswbmw/work/GitHub/Node.js/another-json-schema')

const postSchema = AJS('postSchema', {
  commentIds: [{ type: AJS.Types.ObjectId }]
})

const user = {
  commentIds: [
    '111111111111111111111111',
    '222222222222222222222222'
  ]
}

console.log(postSchema.validate(user))
/*
{ valid: true,
  error: null,
  result: { commentIds: [ 111111111111111111111111, 222222222222222222222222 ] } }
*/

//validate specific field
console.log(postSchema._children.commentIds.validate('lalala'))
/*
{ valid: false,
  error:
   { Error: ($.commentIds[]: "lalala") ✖ (type: ObjectId)
     validator: 'type',
     path: '$.commentIds[]',
     actual: 'lalala',
     expected: [ [Object] ],
     schema: 'postSchema' },
  result: 'lalala' }
*/

Ignore validator

const AJS = require('another-json-schema')

const userSchema = AJS('userSchema', {
  _id: { type: 'number', range: [1, 100] }
})

const user = {
  _id: 0
}

console.log(userSchema.validate(user))
/*
{ valid: false,
  error:
   { Error: ($._id: 0) ✖ (range: 1,100)
     validator: 'range',
     actual: 0,
     expected: { type: 'number', range: [Array] },
     path: '$._id',
     schema: 'userSchema' },
  result: { _id: 0 } }
*/
console.log(userSchema.validate(user, { range: false }))
// { valid: true, error: null, result: { _id: 0 } }

NB: type validator cannot ignore by passing false.

API

AJS([name], schema)

Constructor.

AJS.register(name, fn)

Register a validator. eg:

AJS.register('adult', function (actual, expected, key, parent) {
  return expected ? (actual > 18) : (actual <= 18)
})

ajs.compile([name], schema)

Compile a schema. The following two ways are the same:

const userSchema = AJS('userSchema', {
  _id: { type: 'string', pattern: /^[0-9a-z]{24}$/ },
  name: { type: 'string' },
  age: { type: 'number', gte: 18 },
  gender: { type: 'string', enum: ['male', 'female'] }
})
const newSchema = new AJS()
const userSchema = newSchema.compile('userSchema', {
  _id: { type: 'string', pattern: /^[0-9a-z]{24}$/ },
  name: { type: 'string' },
  age: { type: 'number', gte: 18 },
  gender: { type: 'string', enum: ['male', 'female'] }
})

compiledSchema.validate(data, [opts])

Use the compiled validator to validate an object. it will modify the original object and return it:

opts:

Built-in validators

More examples

see test.

Test

$ npm test (coverage 100%)

License

MIT