Skematic
Data model & rule engine for JS objects
Install / Use
/learn @mekanika/SkematicREADME
Skematic
Data structure and rule validation engine. Robust model schema for JS objects.
Universal, ultra fast and lightweight (4Kb!), Skematic enables you to design, format and validate data according to rules and conditions specified as simple config models, for browser and Node.js.
- Design: structure your data models as config objects
- Format: transform, generate and modify data structures
- Validate: check that arbitrary data conforms to rules
A basic example:
// -- Define a simple data structure
const Hero = {
name: {rules: {minLength: 4}, errors: 'Bad name!'},
shouts: {transform: val => val.trim().toUpperCase()},
skill: {default: 3, required: true, rules: {isNumber: true}},
updated: {generate: Date.now}
}
// -- Format some data
Skematic.format(Hero, {shouts: ' woo '})
// {shouts: 'WOO', skill: 3, updated: 1426937159385}
// -- Validate an object
Skematic.validate(Hero, {name: 'Zim'})
// {valid: false, errors: {name: ['Bad name!'], skill: ['Failed: required']}}
Also fully supports Typescript:
interface ISimpleHero {
name: string
}
const SimpleHero: Skematic.Model<ISuperHero> {
name: { required: true },
sOmeJUNKK: {}
}
// Typescript Error:
// Object literal may only specify known properties,
// and 'sOmeJUNKK' does not exist in type ISimpleHero
Install
npm install --save skematic
Import to your project:
// CommonJS modules
const Skematic = require('skematic')
// OR using ES6 Module imports
import Skematic from 'skematic'
To use in a browser:
<script src="node_modules/skematic/build/skematic.min.js"></script>
Compatibility Note:
Skematicis written in ES6 but compiled down to ES5 and works across all modern browsers (IE9+, Chrome, Firefox, Safari evergreens). Please note that the ES5Object.keys()method is not supported by IE7 & 8, so to useSkematicin these fossil browsers, you'll need to install es5-shim (and worship Satan :metal:).
Usage
The API surface is small by design, with two primary methods:
Design
Model configuration
Skematic provides keys to define rules and conditions for your data model. Config keys are all optional.
Format:
- default {any} value to apply if no value is set/passed
- lock {Boolean} disallows/strips value on format (
unlockformat opts to override) - transform {Function} a function to transform a value (not called if value is undefined or null)
- generate {Object|Function} enables computing a value from functions
- show {String|Array} string scopes required to show field on format (hides if not met)
- write {String|Array} scopes required to validate this field being set (fails validation if
scopesaren't matching) - model {Object} declare sub-model defining this value (see "Sub-model")
Validate:
- rules {Object} validation rules:
{rules: {min: 3, max: 11}} - errors {Object|String} error messages for rules
- required {Boolean} flag if property MUST be set and/or provided
Advanced:
- allowNull {Boolean} Accept
nullvalues (no other validation applied) or set tofalseto force a NOT NULL condition (no undefined or null values permitted). Designed to:- a)
false: enables settingrequired(which ordinarily passes fornull) while disallowingnullas a value. - b)
true: enables acceptingnullwithout triggering any other rule validation (ie. 'null' becomes a valid value)
- a)
- primaryKey {Boolean} flag to indicate whether this field is the primary key (id field), used in conjunction with
mapIdFromformat option to allow transposing your datastore id to some other field on your data model (eg. Mongo's_idcan be mapped to the field you setprimaryKey: trueon)
Note: See format()'s order of execution for which formatting changes get applied in what order.
Simple examples
A basic data model:
const Hero = {
name: {
default: 'Genericman',
required: true,
rules: {maxLength: 140, minLength: 4},
errors: {maxLength: 'Too long', minLength: 'Shorty!'}
}
}
// Generate a record by passing null/undefined to `format(Model, null)`
Skematic.format(Hero)
// -> {name: 'Genericman'}
Skematic.validate(Hero, {name: 'Spiderman'})
// -> {valid: true, errors: null}
Skematic.validate(Hero, {name: 'Moo'})
// -> {valid: false, errors: {name: ['Shorty!']]}}
Typically you'll create a more complete data model to represent your application objects, with several fields to format and validate:
const Hero = {
name: HeroNameField,
skill: {default: 0}
}
Skematic.validate(Hero, {name: 'Spiderman', skill: 15})
// -> {valid: true, errors: null}
Skematic.validate(Hero, {name: 'Moo'})
// -> {valid: false, errors: {name: ['Shorty!']}
Rules
Several validation rules are built in. Custom rules are defined as functions that receive the field value and return pass/fail (true/false). Notably, 'required' is passed as a property option, rather than a rule.
Important: rules ONLY run when the value of the field is defined (i.e. NOT undefined). If a value is undefined on your data, no rules are applied. You can force a value to be provided by add the required: true flag to your model.
The other available validators are:
- .min - The lowest permitted number
- .max - The highest permitted number
- .minLength - The shortest string permitted
- .maxLength - The longest string permitted
- .eq - Value must be strictly equal
- .neq - Value must not equal
- .oneOf - Value must be one of the values in the list of elements
- .notOneOf - Value must NOT be in the list of elements
- .has - List of elements contains the value
- .hasNot - List of elements does NOT contain the value
- .isEmail - no parameters: Is the string an email
- .isUrl - no parameters: Is the string a URL
- .isAlpha - no parameters: Checks value is an ALPHA string (abcd...)
- .isAlphaNum - no parameters: Checks value is AlphaNumeric (abc..012..9)
- .isNumber - no parameters: Checks value is a Number (NaN fails this test)
- .isString - no parameters: Checks value is of type String
- .match - String must match regexp
- .notMatch - String must NOT match regexp
- .isEmpty - set to
trueto check the value is empty - .notEmpty - set to
trueto check the value is not empty
Custom rules can be applied by providing your own validation functions that accept a value to test and return a Boolean (pass/fail).
Note: The
requiredrule has a special shorthand to declare it directly on the model:const modelProp = {default: 'Boom!', required: true}
Declare rules key as follows:
const User = {
name: {
rules: {minLength: 5}
}
}
Skematic.validate(User, {name: 'Zim'})
// -> {valid: false, errors: {name: ['Failed: minLength']}}
Skematic.validate(User, {name: 'Bunnylord'})
// -> {valid: true, errors: null}
Custom Rules
You can mix in Custom rules that have access to the rest of the data model via this. For example:
const User = {
name: {
rules: {
// A built in validation
minLength: 5,
// Your own custom validator (accepts `value` to test, returns Boolean)
// Note: MUST use `function () {}` notation to access correct `this`
onlyFastBunnylord: function myCustomCheck (value) {
// See us access the `speed` prop in our check:
return value === 'Bunnylord' && this.speed > 5
}
}
}
speed: {default: 5}
}
// Wrong name
Skematic.validate(User, {name: 'Zim', speed: 10})
// -> {valid: false, errors: {name: ['Failed: minLength', 'Failed: onlyFastBunnylord']}}
// Too slow!
Skematic.validate(User, {name: 'Bunnylord', speed: 3})
// -> {valid: false, errors: {name: ['Failed: onlyFastBunnylord']}}
Skematic.validate(User, {name: 'Bunnylord', speed: 10})
// -> {vaid: true, errors: null}
Custom error messages
Custom error messages can be declared per rule name:
{errors: {'$ruleName': 'Custom message'}}
Provide a default message if no specific error message exists for that rule:
{
errors: {
max: 'Too large',
default: 'Validation failed'
}
}
Usage example:
const User = {
name: {
rules: {minLength: 5},
errors: {minLength: 'Name too short!'}
}
}
// Using a value test:
Skematic.validate(User.name, 'Zim')
// -> {valid:false, errors:['Name too short!']}
// Using a keyed object value test:
Skematic.validate(User, {name:'Zim'})
// -> {valid:false, errors:{name:['Name too short!']}}
Note: You can create error messages for custom rules too. Just use the same key you used to define the custom rule.
{rules: {myCustom: val => false}, errors: {myCustom: 'Always fails!'}}
Rules can be combined, and you can declare a string message on errors to apply to any and all errors:
const User = {
name: {
rules: {minLength: 5, maxLength: 10},
errors: 'Name must be between 5 and 10 characters'
}
}
Generate
Computed values - Skematic keys can generate values using functions referenced in the generate directive.
The simplest
