yeval v0.3.0
yeval
Dead simple JavaScript schema validation.
Example
// Require the main `validate` function and the rules you'll need
const {
validate,
rules: {
isString,
minLength,
sameAs,
},
} = require('yeval');
// Run the `validate` function with the rules against the target object to get the errors object
const errors = await validate(
{
userName: [isString, minLength(1)],
password: [isString, minLength(6)],
repeatPassword: [isString, minLength(6), sameAs('password')],
},
{
userName: 'Mark',
password: 'somePassword',
repeatPassword: 'someOtherPassword',
}
);
console.log(errors); // { repeatPassword: 'Must match password' }Concept
Any validation rule is a function that returns an error string in case validation fails. Any validation function is provided with three arguments:
value-- value of an attribute this rule is declared fordata-- the whole object that is being validatedpath-- path indatato currently validated value
Here's the simplest validation rule possible:
const required = value => {
if (!value) {
return 'Required';
}
};
// Let's apply our rule against a null value:
const error = required(null);
console.log(error); // we get a string 'Required' as a resultYeval is just a tool that runs such validation functions for you and allows to combine them in an eloquent fashion!
Some aspects
yevalis Promise-based meaningvalidatefunction always returns a Promise. Even for synchronous rules.- Successful validation resolves with
undefined. - Failed validation resolves with a plain object that contains error string for every attribute that failed a validation.
- Rejection of promise occurs only on runtime errors.
yevalreturns only the first error for each object's attribute.- Validation of the next object's attribute will start only after the previous attribute was validated.
- Rules for each attribute are executed from left to right. Each next rule waits until the previous one finishes. Even if rule is asynchronous.
- All built-in rules assume value is of proper type for that rule. Build your validation rules list in order of
increasing strictness. i.e. validating
nullagainstisEmailwould result in runtime error sinceisEmailassumes value is a string. Proper validation for this case is[isString, isEmail].
More examples
- Conditional validation
Use when to build conditions whether to apply any validation rules.
const {
validate,
util: {
when,
},
rules: {
isBoolean,
isEmail,
},
} = require('yeval');
const optedInForNewsletter = (value, data) => data.optedInForNewsletter === true;
const errors = await validate(
{
optedInForNewsletter: isBoolean,
email: when(optedInForNewsletter, isEmail),
},
{
optedInForNewsletter: true,
}
);
console.log(errors); // { email: 'Must be a valid email address' }- Custom validation rules
Writing your own validation rules in the simplest way possible. Just define a function.
const {
validate,
rules: {
isEmail,
},
} = require('yeval');
const isGmailAccount = (value) => {
if (value.slice(-9) !== 'gmail.com') {
return 'Sorry, we only accept gmail accounts';
}
};
const errors = await validate(
{
email: [isEmail, isGmailAccount],
},
{
email: 'jesus@christ.com',
}
);
console.log(errors); // { email: 'Sorry, we only accept gmail accounts' }- Custom error messages
Use msgFor for custom error messages if rule fails.
const {
validate,
util: {
msgFor,
},
rules: {
isEmail,
},
} = require('yeval');
const errors = await validate(
{
email: msgFor(isEmail, 'We need your email address. We really do.'),
},
{
email: 'notAnEmail',
}
);
console.log(errors); // { email: 'We need your email address. We really do.' }- Validation of nested objects
Supply an object as a rule for an attribute if you want to validate nested object
const {
validate,
rules: {
isString,
oneOfArray,
},
} = require('yeval');
const errors = await validate(
{
car: {
make: [isString, oneOfArray(['BMW', 'Mercedes', 'Audi'])],
},
},
{
car: {
make: 'Boeing',
},
}
);
console.log(errors); // { car: { make: 'Must be one of: BMW, Mercedes, Audi' } }- Async validation
Any validation rule can be a promise that resolves with an error string in case of failure.
const {
validate,
rules: {
isEmail,
},
} = require('yeval');
const isUniqueEmail = (value) => {
return User.where({ email: value }).exists().then(exists => {
if (exists) {
return 'Email you supplied is already registered';
}
});
};
const errors = await validate(
{
email: [isEmail, isUniqueEmail],
},
{
email: 'already.existing@email.com',
}
);
console.log(errors); // { email: 'Email you supplied is already registered' }- Optional validation
All built-in rules assume the value is defined by default. So to optionally apply any rule you can use when(isDefined, rule) construction.
const {
validate,
util: {
when,
isDefined,
},
rules: {
isString,
isEmail,
},
} = require('yeval');
const errors = await validate(
{
email: when(isDefined, [isString, isEmail]),
},
{}
);
console.log(errors); // undefined- Compose rules in any way you need.
Since rule is just a function you can easily compose them in no particular order.
const {
validate,
util: {
when,
isDefined,
msgFor,
},
rules: {
isString,
isEmail,
oneOfArray,
}
} = require('yeval');
const validAudiModels = ['A1', 'A2', 'A3', 'A4', 'A5', 'A6', 'A7', 'A8'];
const isAudi = (value, data) => data.car.make === 'Audi';
const errors = await validate(
{
email: when(isDefined, [msgFor(isString, 'Hey, we need a string!'), isEmail]),
car: when(isDefined, {
make: isString,
model: when(isAudi, msgFor(oneOfArray(validAudiModels), 'This is not a valid audi model!')),
}),
},
{
email: 'speedy@gonzales.com',
car: {
make: 'Audi',
model: 'A3',
},
}
);
console.log(errors); // undefinedDocs
Docs are available here.