Object validation in JavaScript can be tedious if you don't have a good framework in place. In this post, we'll use the Array.reduce
method to make object validation feel like a treat!
Manual Validations
Let's say we have a user
object and several criteria that need to pass to make it valid. Here are the properties and the criteria that they must meet:
prop | criteria |
---|---|
name | Longer than 2 characters |
password | Longer than 8 characters |
confirmPassword | Matches the password |
age | 18 or greater |
If we were to take a somewhat manual approach to validation, we might write something like this:
const user = {
name: "Bob",
password: "kaBob123",
confirmPassword: "kaBob123",
age: 19,
};
const errors = [];
if (user.name.length < 2) {
errors.push("User's name is too short");
}
if (user.password.length < 8) {
errors.push("User's password is too short");
}
if (user.password !== user.confirmPassword) {
errors.push("Password and confirmation do not match");
}
if (user.age < 18) {
errors.push("User must be at least 18 years old");
}
const isValid = errors.length === 0;
Our errors
array would get populated with any validation errors and, if the array had a length greater than 0, our isValid
variable would be false
.
Creating a Validation Framework
While this works alright for a small number of validations, I tend to prefer organizing larger sets of rules in an array, and using the reduce
method to determine if there are errors:
// Validation rules
const rules = [
{
test: (user) => user.name.length > 2,
message: "User's name is too short",
},
{
test: (user) => user.password.length >= 8,
message: "User's password is too short",
},
{
test: (user) => user.password === user.confirmPassword,
message: "Password and confirmation do not match",
},
{
test: (user) => user.age >= 18,
message: "User must be at least 18 years old",
},
];
// Test object against rules
const errors = rules.reduce((errs, rule) => {
const result = rule.test(user);
if (result === false) {
errs.push(rule.message);
}
return errs;
}, []);
const isValid = errors.length === 0;
Now, we have a consistent interface and can add rules just be adding additional objects to our array!
Creating a Reusable Validation Function
To extend the utility of our validator, we can create a function that takes an object, a set of rules, and returns errors and validation status. Let's create that function.
const validate = (obj, rules) => {
const errors = rules.reduce((errs, rule) => {
const result = rule.test(obj);
if (result === false) {
errs.push(rule.message);
}
return errs;
}, []);
return {
errors,
isValid: errors.length === 0
}
}
Now, we can use this function wherever we need to validate an object! Let's try with our previous example and use a user object that's not quite valid:
// Invalid user object
const user = {
name: "Bob",
password: "kaBob123",
confirmPassword: "kaBob12",
age: 17,
};
// Validation rules
const rules = [
{
test: (user) => user.name.length > 2,
message: "User's name is too short",
},
{
test: (user) => user.password.length >= 8,
message: "User's password is too short",
},
{
test: (user) => user.password === user.confirmPassword,
message: "Password and confirmation do not match",
},
{
test: (user) => user.age >= 18,
message: "User must be at least 18 years old",
},
];
// Validation function
const validate = (obj, rules) => {
const errors = rules.reduce((errs, rule) => {
const result = rule.test(obj);
if (result === false) {
errs.push(rule.message);
}
return errs;
}, []);
return {
errors,
isValid: errors.length === 0,
};
};
// Testing our object
const result = validate(user, rules);
// {
// errors:
// [ 'Password and confirmation do not match',
// 'User must be at least 18 years old' ],
// isValid: false
// }
I hope you enjoyed this exploration of using Array.reduce
to make our object validations just a bit more consistent and enjoyable.
Happy coding!