Sometimes the creation of an object is a complex process, often with multiple steps and additional conditions. Such objects include validators implemented in the object style.
Validation is the process of checking data for correctness according to a set of specified conditions. And a validator is something that performs this check.
In programming, validation occurs at every step. In one way or another, we work with forms or data sent via API. All this data sent by users or external systems are automatically considered untrusted. Before you work with this data, you first need to make sure that it's “valid”, i.e., it meets our requirements. For example, if we're talking about a user's email, for most registration forms, the field mustn't be empty and must match the format of emails.
The easiest way to do the validation is to perform a manual check:
if (data.email === '') {
errors.push('email empty');
}
// hypothetical format check function
if (!hasEmailFormat(data.email)) {
errors.push('the email is in the wrong format');
}
It's not hard to imagine how this code would grow for a large form with many rules. There are other problems with this method. It doesn't make it easy to reuse rules for different forms and projects. In addition, you will either have to implement the checks yourself, or look for ready-made libraries for them. For example, to check the correctness of links, addresses and other things.
Yup
In JavaScript, the yup library is very popular for performing validation. Look at the example equivalent to the checks above:
import * as yup from 'yup';
const schema = yup.string().required().email();
schema.validateSync(''); // ValidationError: this is a required field
schema.validateSync('wrongemail'); // ValidationError: this must be a valid email
schema.validateSync('support@hexlet.io'); // returns the value itself
The core principle of this library is as follows. A validation scheme is formed using a chain of methods. This chain includes various conditions that must be met according to the principle of union. That is, all conditions must be met for a successful validation. The conditions themselves depend on the type of data being checked, so first you call the method that defines the type, and then you call the methods that refer to that type of data:
// positive() and integer() only the number has methods
const schema1 = yup.number().required().positive().integer();
// the array must contain between 5 and 10 elements
const schema2 = yup.array().min(5).max(10);
In all these situations, our task is to define the appropriate schema for a specific task. This schema can then be used many times on different data. This way of forming a schema object is called a build, and the object itself is called a builder. By and large, this is a slight variation in the use of a fluent interface. How is it convenient? With it, it's easy to build even complex checks using the very clear mechanics of combining calls. The code is simple and straightforward.
Another plus is the availability of ready-made error messages for all built-in checks. If necessary, they can be changed for a specific schema by simply passing the text to the check method:
// The substitution is only for this schema
const schema1 = yup.string().required('Empty!');
const schema2 = yup.string().required(); // here will be the default value
Yup supports not only different texts, but also different locales, which is important for multilingual sites.
But what do we do in a situation where we're working with an object where each property has its own checks? Yup solves this problem very elegantly using recursive definition:
// object().shape() allows you to set the object structure
const schema = yup.object().shape({
name: yup.string().required(),
age: yup.number().required().positive().integer(),
email: yup.string().email(),
website: yup.string().url(),
createdOn: yup.date().default(() => new Date()), // default value
});
const data = {
name: 'jimmy',
age: 24,
};
schema.validateSync(data);
https://repl.it/@hexlet/js-object-oriented-design-buider-yup-object#index.js
But that's not all, yup allows you to add additional checks that weren't originally included. You can read more about this feature in the documentation.
Recommended materials
Are there any more questions? Ask them in the Discussion section.
The Hexlet support team or other students will answer you.
For full access to the course you need a professional subscription.
A professional subscription will give you full access to all Hexlet courses, projects and lifetime access to the theory of lessons learned. You can cancel your subscription at any time.