Trava stands for TRAnsform and VAlidate. Inspired by struct.
The main goal of the library is to provide highly extendable and customizable way of JavaScript entities validation. Validation often goes along with parsing or transforming values. So coercion feature is included by design into Trava.
Features
- easy to use
- no external dependencies
- supports all major browsers and IE11+
- customizable and extendable
Install
Install from npm:
npm install trava
And import or require:
import Trava from 'trava';
or use CDN:
<script src="https://unpkg.com/trava"></script>
For modern browsers es201X builds are available (trava.es.js
and trava.es.min.js
).
Getting Started
Let's imaging the most obvious and simple validator. Probably it will look like:function validate (value) {
// any checking...
if (!check(value)) return false;
return true;
}
But in real scenarios we'd also like to get some error details, e.g.:
function validate (value) {
if (!check1(value)) return 'ERROR_1';
if (!check2(value)) return 'ERROR_2';
return true;
}
We may stop here and probably you don't need any library to do validation this way, may be just some primitives for common cases. But there is one more feature we might want to get and when Trava could help. While using JSON it's often needed to parse or convert values after validation. Code for parsing looks pretty similar to validation: its just validate
being replaced with parse
. In Trava these steps are united together. To support both validation and transformation we need to distinguish error from transformed value returned from validator. Probably we could use js Error
, but it works only with string messages. Fortunately Trava has own ValidationError
to support complex errors.
So Trava validator looks like:
function validate (value) {
if (!check1(value)) return new Trava.ValidationError({ code: 401 });
if (!check2(value)) return new Trava.ValidationError({ code: 405 });
return parseOrTransform(value); // apply some parse or transform
}
Then use validator:
const result = validate(data);
if (result instanceof Trava.ValidationError) {
// note using `data` property to extract error data
console.log('This is error!', result.data);
} else {
const goodTransformedValue = result;
}
That's all you have to know to start using Trava. It is very simple. The second advantage why to use Trava is a collection of helpful operators out of the box. See examples to learn how to use them.
Operators
Despite operators itself are just functions, it's convinient to use Trava
to build validators or validate data directly:
import Trava from 'trava';
let validator = Trava(validators);
let values = validator(data);
// or validate directly
values = Trava(validators, data);
Compose
Compose is used to combine several validators into one. E.g.:
const validator1 = n => n < 10 ? n : new ValidationError('BAD NUMBER');
const validator2 = ...;
const validator3 = ...;
const composedValidator = Trava.Compose([validator1, validator2, validator3]);
// then `composedValidator` could be used just like simple validator
Validation goes consequently from left to right. When an error occurs, validation stops and an error is returned immediately.
When used inside other operators explicit call Trava.Compose
could be omitted. E.g.:
const composedValidator = Trava.Required(Trava.Compose([v1, v2, v3]));
// is same as
const composedValidator = Trava.Required([v1, v2, v3]);
Required
Required
is a guard to check if a value is defined (!== undefined
):
const validator = ...;
const requiredValidator = Trava.Required(validator);
let value;
console.log(requiredValidator(value)); // ValidationError('Value is required')
value = 'any';
console.log(requiredValidator(value)); // `Required` is bypassed
Custom error message can be set by providing it as a second argument:
const requiredValidator = Trava.Required(validator, 'My custom error message');
Or set the default for all validators:
Trava.Required.ErrorMessage = 'My default required error';
Error message also could be a function which should return error data and will be called with same arguments as validator when error occurs.
const validator = Trava.Check(v => v > 0, v => `${v} is not positive number!`);
Optional
Optional
checks if value is not defined then returns value or default value which can be provided as a second argument:
const optionalValidator = Trava.Optional(validator, 'default value');
Nullable
Nullable
is just like Optional except it also checks if value is not null
.
Check
Check
is helper to reuse validators which return boolean:
const myExistingValidator = (v) => v < 10;
const travaValidator = Trava.Check(myExistingValidator);
console.log(travaValidator(20)); // ValidationError('Incorrect value')
Custom error message can be set by providing it as a second argument:
const checkValidator = Trava.Check(n => Boolean(n), 'My custom error message');
Or use the following to set the default one:
Trava.Check.ErrorMessage = 'My default check error';
Enum
Enum
checks if value exists in enum:
const enumValidator = Trava.Enum(['a', 'b']);
console.log(enumValidator('c')); // ValidationError('Incorrect value')
Just like Check
it accepts an error message as a second argument.
Const
Const
checks if value equals:
const constValidator = Trava.Const('a');
console.log(constValidator('c')); // ValidationError('Incorrect value')
Just like Check
it accepts an error message as a second argument.
Each
Each
is usefull for validating uniform data structures (so far works only with arrays). Errors are aggregated in object by keys (or indices):
const elementValidator = n => n < 10 ? n : new ValidationError('BAD NUMBER');
const arrayValidator = Trava.Each(elementValidator);
console.log(arrayValidator([1, 15, 7, 5, 20]));
// ValidationError({2: 'BAD NUMBER', 4: 'BAD NUMBER'})
Keys
Keys
is used to validate Objects
:
const objectValidator = Trava.Keys({
a: Trava.Check(a => a >= 0),
b: Trava.Required(Trava.Check(b => b.startsWith('nice'), 'HEY, think positive!')),
});
console.log(objectValidator({
a: -1,
b: 'bad wrong error'
}));
// ValidationError({
// a: 'Incorrect value'.
// b: 'HEY, think positive!',
// })
When used inside other operators explicit call Trava.Keys
could be omitted. E.g.:
const objectValidator = Trava.Required({
a: Trava.Check(a => a >= 0),
b: Trava.Check(b => b < 0),
});
Some
Some
is like Compose but tries to find first non error.
const someValidator = Trava.Some([
Trava.Check(isString, 'Not a string'),
Trava.Check(isNumber, 'Not a number'),
]);
console.log(someValidator(1)); // 1
console.log(someValidator('a')); // 'a'
console.log(someValidator({})); // ValidationError('Not a number') <-- latest error
Examples
const t = require('trava');
const { Required, Optional, Each, Enum, Check, Keys, Some, ValidationError } = t;
const isString = s => typeof s === 'string';
const isEmail = s => /^\S+@\S+\.\S+$/.test(s);
const validateForm = t({
username: Check(un => isString(un) && /^\w{3,30}$/.test(un), 'CUSTOM ERROR: INVALID USERNAME!'), // required by default
password: Optional(Check(pwd => isString(pwd) && pwd.length >= 6)),
access_token: Optional(Some([isString, Number.isInteger]), 'default token'),
birthyear: Optional(Check(b => Number.isInteger(b) && 1900 <= b && b <= 2018)),
email: Optional(Check(isEmail)),
contacts: Optional(Each({
name: Enum(['phone', 'email']),
value: Check(isString),
}))
});
const values = validateForm({
username: 'HelloTrava',
password: 'secretoversecret',
birthyear: 1990,
});
if (values instanceof ValidationError) {
console.error('FORM ERRORS', values.data);
} else {
console.log('FORM VALUES', values);
}