valdat
v0.9.0
Published
A modern day validator for the masses
Downloads
8
Maintainers
Readme
valdat
A modern-day validator for the masses, designed for ease of use. Its design is influenced by prop-type and Joi. Although, not as exhaustive as Joi.
Let's explore an React example first:
class User extends React.Component {
state = {
name: '',
nameMessage: '',
sex: '',
sexMessage: '',
age: '',
ageMessage: '',
languages: [],
languagesMessage: '',
preference: {},
preferenceMessage: ''
}
onSubmit = () => {
// create data object
const {
name,
sex,
age,
languages,
preference
} = this.state;
const data = {
name,
sex,
age,
languages,
preference
};
// create schema object
const schema = {
name: valdat.string().isRequired(),
sex: valdat.oneOf(['Male', 'Female', 'Other']),
age: valdat.number().min(18).isRequired(),
languages: valdat.array().notEmpty().ofType(valdat.string()),
preference: valdat.object().shape({
email: valdat.boolean().isRequired(),
theme: valdat.string().isRequired()
}).isRequired()
};
// check if data matches schema
const {
isValid,
errors
} = valdat.check(schema, data);
// reduce the error messages and update error messages
// this can also clear existing error messages
const errorMsgs = Object.keys(errors)
.reduce((msgs, key) => ({
...msgs,
[`${key}Message`]: errors[key]
}), {});
this.setState({ ...errorMsgs });
// if the data is okay
if (isValid) {
// call api to submit
}
}
}
That wasn't so bad was it? A similar flow can also be create for any of the popular frameworks.
Features
- Ability to curry (chain) multiple assertions
- Assertions fail-fast, meaning it will stop checking if one of them fails
- Returned
errors
object has the same shape as schema, with/without an error message. Very easy to update UI without much hassle - Ability to add custom validator
- Ability to register custom assertions
- Sufficient list of assertions (still growing)
Fail-fast
All the Validate<Type>
classes extends a Validate
base class that provides the following:
stack
: an array into to which the curried assertion functions are pushedrequired
: a boolean which will be settrue
if theisRequired()
curry is calledisRequired()
: if the assertion should expect a value
When we call valdat.check(schema, data)
, each item of schema is an instance of Validate<Type>
class, meaning it has its own this.stack
. valdat.check
will loop through this stack and break as soon as an assertion fails. The assertion function always returns an object with { error: boolean, message: string }
, thus allowing us to do nested assertions, for eg: valdat.array().ofType(valdat.string())
or valdat.oneOfType([valdat.string(), valdat.number()])
Installation
npm install valdat --save
Usage
import valdat from 'valdat';
// to create custom assertion class
import valdat { Validate } from 'valdat';
// for typescript
import valdat, {
Validate,
IValidate,
IValidator
} from 'valdat';
API - utility methods
check
Check the data against the schema. It will return an object with isValid: boolean
and errors: <SchemaShape>{}
.
const {
isValid,
errors
} = valdat.check(schema, data);
custom
Use a totally custom assertion function. There is not isRequired()
in this case. It must be implemented within the custom assertion.
const schema = {
password: valdat.custom((data, key) => {
const regex = /^[\w&.-]+$/;
const value = data[key];
let error = false;
let message = '';
if (!regex.test(value)) {
error = true;
message = 'Should contain: minimum 8 letters, upper & lower letters, numbers, and special characters';
}
// Important for assertion to work
return {
error,
message
}
}),
}
register
Imagine you reach a stage where your custom assertion requires its own currying, or it needs to be reused accross the application. Then you can create a new Validate<Type>
class by extending the base Validate
class and register to valdat
object literal. After that you can use your custom assertion anywhere.
import valdat, { Validate } from 'valdat';
class ValidateCat extends Validate {
constructor() {
super();
}
cat() {
this.stack.push((data, key) => {
const value = data[key]
if (!value || !value.cat) {
return {
error: true,
message: `${key} in data is not a cat.`
}
}
// Important for assertion to work
return {
error: false,
message: ''
}
});
// Important for currying
return this;
}
isLazy() {
this.stack.push((data, key) => {
const value = data[key]
if (value && !value.isLazy) {
return {
error: true,
message: 'The cat is not lazy.'
}
}
return {
error: false,
message: ''
}
});
// Important for currying
return this;
}
};
valdat.register('cat', () => new CatValidation().cat());
const schema = {
prickle: valdat.cat().isLazy().isRequired()
};
API - assertion methods
Each of the following methods are from their own Validate<Type>
classes, which extends the base class Validate
, which provides .isRequired()
. The following assertion methods returns this
, allowing us to curry through the additional methods. The methods are included in valdat
object literal for correct sequential access. Register API does the same thing.
string
Checks if the value of the key in data
is a string.
const schema = {
name: valdat.string().isRequired()
};
string.hasLen
Checks if this string has a length of 5.
const schema = {
name: valdat.string().hasLen(5)
};
string.matchRegex
Checks if this string matches the provided regex.
const schema = {
name: valdat.string().matchRegex(/^Solo$/)
};
number
Checks if the value of the key in data
is a number.
const schema = {
age: valdat.number().isRequired()
};
number.min
Checks if this number has a minimum value of 18.
const schema = {
age: valdat.number().min(18)
};
number.max
Checks if this number has a maximum value of 30.
const schema = {
age: valdat.number().max(30)
};
boolean
Checks if the value of the key in data
is a boolean.
const schema = {
publicAccess: valdat.boolean().isRequired()
};
object
Checks if the value of the key in data
is an object.
const schema = {
preference: valdat.object().isRequired()
};
object.shape
Checks if this object has the exact shape as provided.
const schema = {
preference: valdat.object().shape({
email: valdat.boolean().isRequired(),
theme: valdat.string().isRequired()
})
};
array
Checks if the value of the key in data
is an array.
const schema = {
languages: valdat.array().isRequired()
};
array.notEmpty
Checks if this array is not empty.
const schema = {
languages: valdat.array().notEmpty()
};
array.ofType
Checks if each item in this array contains the specified type.
const schema = {
languages: valdat.array().ofType(valdat.string())
};
oneOf (enum)
Checks if the value of the key in data
exactly matches one the provided enums values
const schema = {
sex: valdat.oneOf(['Male', 'Female', 'Other'])
};
oneOfType (enum)
Checks if the value of the key in data
exactly matches one the provided enums types
const schema = {
nationality: valdat.oneOfType([
valdat.string(),
valdat.array()
])
};