purity v1.0.3
Purity
Purity is a JSON data validation and transformation tool for web browsers and node.js
It can be installed via:
Purity, Inspired by mongoose, exposes a schema-based validation API allowing you to apply constraints and transformations to the data in your application.
For usage with express, please see the express-purity package on npm.
Quickstart Example
Most of the action happens through the Schema object.
import { Schema } from 'purity';The following schema expects:
title- A non empty string.tags- a comma separated string of tags which will be lowercased and split into an array.comments- An array of objects with properties:likes- a non negative number which defaults to 0.content- a non empty string not exceeding 255 characters.
let toLowercase = v => v.toLowerCase();
let split = v => v.split(',');
const blogPostSchema = Schema({
title: { $type: String, $required: true },
tags: { $type: String, $transform: [toLowercase, split] },
comments: [{
likes: { $type: Number, $default: 0, $gte: 0 },
content: { $type: String, $required: true }
}]
});Validate some data against the schema, using the callback api...
let dirtyData = {
title: 'Stuff about lions',
tags: 'Lions',
comments: [
{ likes: 123, content: 'lions r cool' },
{ content: 'w/e' }
]
};
blogPostSchema.validate(dirtyData, (err, res) => {
assert.equal(err, null);
assert.equal(res, {
title: 'Stuff about lions',
tags: ['lions'],
comments: [
{ likes: 123, content: 'lions r cool' },
{ likes: 0, content: 'w/e' }
]
})
})Or the promise api...
let dirtyData = {
title: 'Spiders',
tags: 'HAIRY,LEGS',
comments: [{ content: 'creepy' }]
};
blogPostSchema.validate(dirtyData)
.catch(handleError)
.then(handleSuccess);Errors have properties to help you make meaningful messages:
let dirtyData = {
comments: [{ content: 'this one will fail' }]
};
blogPostSchema.validate(dirtyData, function (err, res) {
assert(err instanceof purity.ValidationError);
let message = `The blog ${err.path} is ${err.type}`;
// message -> 'The blog title is missing'
})API reference
purity.Types
An object containing the aliases of built in types
Types.Any- a relaxed type with no formal type checksTypes.BooleanTypes.StringTypes.NumberTypes.Date
purity.Schema(definiton , options)
Create a re-usable schema to validate data against.
Arguments
definitionA definition (usually an object) representing the expected data. Detailing type and value constraints, with optional defaults and transformations. See detailed description below for more information.options- schema optionsoptions.cast {Boolean}- when true, purity will attempt to cast all properties to their expected types before applying constraints.
Returns
A new Schema instance.
Schema definition
The schema definition is a representation of the constraints and transformations you would like to apply to your data.
Basics
The definition is plain Obejct with the following format:
const definition = {
/**
* $type is the only required field.
* It can be one of purity.Types or an alias for a custom type.
*/
$type: String,
/**
* $required flag defaults to false.
* Setting this to true will generate errors when
* the property is null|undefined or an empty string.
*/
$required: true,
/**
* $cast flag determines whether purity will attempt to cast
* the property to it's declared $type. this overrides schema
* level cast options and defaults to false.
*/
$cast: true,
/**
* $default can be a static value or a function which yields
* a default value when the property is null|undefined or
* an empty string.
*/
$default: Date.now,
/**
* $transform is a mapping function or array of mapping functions
* which will transform the data.
*/
$transform: [v => v + 1, v => v - 1, v => 'unnecessary']
/**
* More options are available for the built in purity.Types
* and for custom types which declare constraints
*/
};
const schema = Schema(definiton);However if you only wish to enforce a type, the following is sufficient:
const schema = Schema(String);constraints
The following additional constraints can be applied per type:
purity.Types.String
{
// {Number} assert a minimum string length
$minlength: 123,
// {Number} assert a maximum string length
$maxlength: 456,
// {Number} assert an exact string length
$fixedwidth: 789,
// {RegExp} assert a regex .test()
$regex: /(?:lions|tigers|bears|)+\soh my$/i
}purity.Types.Number
{
// {Number} assert greater than
$gt: 1,
// {Number} assert greater than || equal
$gte: 2,
// {Number} assert less than
$lt: 3,
// {Number} assert less than || equal
$lte: 4,
// {Number} assert equal
$eq: 5,
// {Number} assert not equal
$neq: (act, opt) => act !== opt
}purity.Types.Date
{
// {Date|Number} assert greater than
$gt: new Date(),
// {Date|Number} assert less than
$lt: Date.now()
}Arrays
To declare an array, simply wrap the definition in square brackets [].
const schema = Schema([{ $type: Date, $required: true }]);Nested Objects
For nested objects ...nest them
const schema = Schema({
id: Number,
some: {
deeply: {
nested: {
data: { $type: Boolean, $default: true }
}
}
}
});purity.createDataType(options)
Declare a data custom data type to be used in a schema.
Arguments
options {Object} - with properties:
- required:
aliases
- optional:
checkcastconstraints
options.aliases {Any|Any[]} - Identifier(s) fot this data type. The alias(es) declared here will be looked up when used to alias a $type in a schema definition.
options.check {Function (value)} - a function which is passed the value of the property currently being processed and should return a Boolean indicating whether or not it is valid. By default this returns true.
options.cast {Function (value)} - a function which maps the value of the property currently being processed to your custom data type. Only invoked when the schema level cast or type level $cast flag is set. By default this is an identity transformation v => v.
options.constraints {Object} - an object mapping type specific constraint names to predicate functions which enforce them.
Important notes: options.check is invoked before any casting is applied. The incoming value will always be a primitive javascript value. Use this opportunity to reject data that cannot be casted.
Examples
Creating a data type
Here is an example of how you might create a data type to handle mongodb ObjectIds.
For those who don't know, an ObjectID is simply a 24 character hex string.
import { ObjectID } from 'mongodb';
purity.createDataType({
// give the type some aliases so we
// can refer to it in a schema definition
aliases: [ObjectID, 'objectid'],
// implement a quick and dirty check
// to see if it's a valid ObjectID
check: v => typeof v === 'string' && /[a-zA-Z0-9]{24}/.test(v),
// provide a method to cast incoming data
cast: v => new ObjectID(v),
// A contrived example of adding constraints
// we're going to check if the ObjectID
// ends with a particualr sequence
constraints: {
endsWith: (value, option) => {
// value is the incoming data.
// option is the character we're looking for
return value.toString().endsWith(option);
}
}
});Now let's use it in a schema
const schema = Schema(ObejctID, { cast: true });
schema.validate('abcdef0123456789abcdef01', function (e, r) {
assert.equal(r, ObejctID(abcdef0123456789abcdef01));
});using the endsWith constraint we defined...
// we can use the other alias we provided as well
const schema = Schema({ $type: 'objectid', endsWith: '02', $cast: true });
schema.validate('abcdef0123456789abcdef01', function (e, r) {
// the endsWith constraint causes an error
assert(e instanceof purity.ValidationError);
});Bugs and Features
Please log any issues or feature requests on github's issue tracker.
Version history
- 0.x - initial release
- 1.0.0
- Breaking changes, please consult the new api to migrate.
- removed array options
- removed many mutations in favour of sequential mapping functions
- removed
cleansealias forSchema#validate - added Date type
- added ability to validate primitives and arrays as root level data
Todo:
- Travis CI
- browser testing
- improve docs
- customisation around error messages
- add some support for array options
- unique
- option to ignore errors and remove elements instead
- constraints on array length