skadi

A validation and object sanitizer based on is-my-json-valid

Usage no npm install needed!

<script type="module">
  import skadi from 'https://cdn.skypack.dev/skadi';
</script>

README

skadi

npm version Dependency Status devDependency Status Build Status Coverage Status Code Climate JavaScript Style Guide

A simple object validator/sanitizer based on is-my-json-valid - supports some useful shorthands as well.

Installation

npm install skadi --save

Usage

// Import
const { createValidator, ValidationError } = require('skadi')

// Create a validator using a JSON-schema.
const myValidator = createValidator({
  type: 'object',
  additionalProperties: false,
  properties: {
    name: {
      type: 'string',
      required: true,
    },
  },
})

// Use it
const input = {
  name: 'Test',
  otherStuffThatIsNotRelevant: 'hehe',
}

const result = myValidator(input)
// << { name: 'Test' }

When used like above, validation errors will throw.

try {
  myValidator({ anInvalid: 'object', because: 'it has no name' })
} catch (err) {
  console.log(err instanceof ValidationError) // << true
  console.log(err.errors) // << [{ field: 'name', message: 'is required' }]
}

Alternative usage:

const context = myValidator.context({})
console.log(context.valid()) // << false
console.log(context.errors) // << [{ field: 'name', message: 'is required' }]

const anotherContext = myValidator.context({ name: 'Skadi', other: 'stuff' })
console.log(anotherContext.valid()) // << true
console.log(anotherContext.pick()) // << { name: 'Skadi' }

// We can add errors manually..
anotherContext.errors.push({ field: 'name', message: 'Too cool, man' })
console.log(anotherContext.valid()) // << false

Use Case: custom, async validation.

Because you can add errors manually, custom validation becomes easy (JSON-schema does not allow this).

Here's an example that makes the validator async.

// Factory function that returns a validator.
// It takes a db so we can look up a user.
function createMyAwesomeValidator(db) {
  const validator = createValidator({
    type: 'object',
    properties: {
      username: 'string!', // shortcut, see https://github.com/yanick/json-schema-shorthand#required-property
    },
  })

  return (objToValidate) => {
    const context = validator.context(objToValidate)

    // Check if username is taken. This is async.
    return db.users
      .findWhere({ username: objToValidate.username })
      .then((user) => {
        if (!user) {
          context.errors.push({
            field: 'username',
            message: 'This username is taken!',
          })
        }

        // .end() will do the same as calling
        // validator(obj) directly: sanitize and return the object
        // if successful, throw if not.
        return context.end()
      })
  }
}

// Create our validator..
const myAwesomeValidator = createMyAwesomeValidator(someDbModule)
myAwesomeValidator({ username: 'Skadi' })
  .then((user) => {
    // Success! We now have a sanitized user.
  })
  .catch((err) => {
    // Could be a validation error.
    console.log(err instanceof ValidationError)
  })

Why not just use validator.filter from is-my-json-valid?

Because it mutates the object rather than returning a new one.

Examples

Check the example/ directory, there's an index.js that you can run with node example/index.js.

Top-level API

The skadi object exports 3 things:

  • createValidator: the meat of the package.
  • createPicker: used internally, but could be useful to you.
  • ValidationError: thrown when you've got too much confidence.

createValidator

Given a JSON-schema, will create a validator function.

The 2nd argument is an options object which are passed to is-my-json-valid, with the exception of createError which can be specified to tell Skadi how to throw a validation error.

additionalProperties in JSON-schema means that validation should fail, but Skadi will rewrite the schema so it won't - instead, we filter out unwanted properties after validating. You don't really have to understand this.

const myValidator = createValidator(
  {
    /* schema */
  },
  {
    // -- the following are default options --

    // tells `is-my-json-valid` to validate as much as possible before failing
    greedy: true,
    // tells `is-my-json-valid` to provide some more info on each error
    verbose: true,
    // tells Skadi to throw the result of this function; gets passed the validation errors.
    createError: (validationErrors) => new ValidationError(validationErrors),
  }
)

// This...
myValidator({ some: 'object' })

// Is the *exact same* as...
myValidator.context({ some: 'object' }).end()

createPicker

Given a JSON-schema, will create a picker function used to filter out unwanted properties. Used internally in createValidator.

The function returned takes an object to pick from, and returns a new object with the unwanted properties filtered out.

const myPicker = skadi.createPicker({
  additionalProperties: false,
  properties: {
    name: {
      type: 'string',
    },
  },
})

myPicker({ name: 'Skadi', other: 'stuff' })
// << { name: 'Skadi' }

ValidationError

Thrown when using validator() or validator.context({}).end().

Contains an errors array.

Validation Context object

When using validator.context(obj), a validation context is returned. This is what you get:

  • errors: An array of { field, message }. You can push and pop from it as you see fit. See the async validation use case for an example of how/why you'd want to do this.
  • valid(): Very simply checks the length of errors, and returns true if there are none, and false when there are errors. Does not throw.
  • pick(): Returns a sanitized version of the object passed to context().
  • end(): If valid() returns false, will throw a ValidationError which will contain the errors array. If everything is smooth, returns a sanitized object (using pick()).

Changelog

  • 2.0.3
    • Add ! variant for every type.
  • 2.0.2
    • Fix additionalProperties TS typing regression. I need to stop noobing it up.
  • 2.0.1
    • Fix required TS typing regression.
  • 2.0.0
    • Bumped Node engine version to >=10.
    • Add JSONSchemaV6 schema type definition.
    • Update packages.
  • 1.6.0
    • Pick props from oneOf, anyOf, allOf and items when sanitizing.
  • 1.5.1
    • Add more fields to Schema typing.
  • 1.5.0
    • Add createError option to customize the error being thrown.
  • 1.4.0
    • Improved TypeScript type defs
  • 1.3.0
    • Added TypeScript definitions.
  • 1.2.0
    • Updated json-schema-shorthand to 0.2.0, which adds support for the type! shortcut.
  • 1.1.2
    • Added support for allOf, oneOf and not picking.
  • 1.1.1
    • Make it actually be greedy by default, dammit.
  • 1.1.0
    • Added support for passing options to is-my-json-valid.
    • Greedy mode on by default.
  • 1.0.0
    • Added support for JSON Schema Shorthands.
    • Switched to StandardJS style guide.
  • 0.2.0
    • First real release.

Author

Jeff Hansen - @Jeffijoe