loading...
Cover image for Using Array.Reduce to Streamline Your JavaScript Object Validations

Using Array.Reduce to Streamline Your JavaScript Object Validations

nas5w profile image Nick Scialli (he/him) ・3 min read

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!

Posted on by:

nas5w profile

Nick Scialli (he/him)

@nas5w

Husband, dog dad, software engineer, coffee monster. Working in civic tech!

Discussion

markdown guide
 

Note: the name and password length validation can be done with HTML alone. No need for JavaScript. See the minlength attribute.

 

Good point - but i d like to think that this is also for server side validation. My rule of thumb: validate on the frontend for comfort , validate on the server for security .

 
 

HTML and JavaScript validation can be bypassed easily using the Developer Tools. If you plan on sending any data from the frontend to the server-side, you have to perform layered validation.

With the HTML then the JavaScript and finally the server-side. If they get past the first two, unless there is a hole in the server-side validation they are not getting past that.

Better still use a whitelist and deny any input that is not on this list.

 

Frontend form validation is not done for security, it's for user experience.

Backend validation is security, ideally frontend validation should be so good that non-malign users will mostly never hit those backend validation errors.

Imho frontend and backend validations serve completely different purposes and cannot directly be compared.

Frontend form validation is not done for security, it's for user experience.

Frontend form validation is a mixture of good user experience with security in mind.

Respectfully, frontend validation has absolutely zero to do with security.

 
 

I like this one a lot, thanks for sharing Nick!

 
 

I like this! You should flesh out the idea a bit more and publish an npm package 😃