📝 Using JS Object Schemas to Validate Data with Yup, Zod, Joi and more 🤩
Tags: webdev, javascript, howto, json
Published: 2024-03-26 13:30:00

I’m sure many of you who have developed interactive web applications have experience handling data validation. In fact, it’s probably a common topic. You could create custom validations or you may choose to validate via JavaScript Object Notation (JSON) Schemas . When I need to use JSON Schema library, I tend to use Yup. Having validate data with JSON Schema libraries and without one across different project scales, they are effective in their own ways.  

Today, I want to dive deeper into the world of JSON, exploring popular libraries as well as other notable libraries in the ecosystem. Additionally, I will mention how you can do custom validations without using a JSON Schema library. And finally, I will compare both methods. 

Please note that I’m using code examples using JavaScript without basing on a JS framework such as React or Vue since you can apply both methods to any JS framework. 

<img width="100%" style="width:100%" src="https://media.giphy.com/media/v1.Y2lkPTc5MGI3NjExMGdzbWNyMzNsMDV5YzVzOTY2ejBqc2c1bmUyaHJveDBqenk4aGE3byZlcD12MV9pbnRlcm5hbF9naWZfYnlfaWQmY3Q9Zw/xThtalkAxYnZd94uB2/giphy.gif">

---

## Validex: The Most Complete Validation Collection for React Forms

Just a quick background about what I'm working on. [Validex](https://www.validex.dev/?utm_source=dev.to&utm_content=using-json-for-custom-validations) can help you validate React forms in minutes. It has over 36 validators ranging from string, password, credit card, email, file, image and more. Each validator has TypeScript and JavaScript codes, RegEx and Non-RegEx options and unit tests.

![Validex - Complete Collection of Form Validations for React](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/zpc9mt2h1jzvzgetnmbt.png)

I would be grateful 🥹 if you could take a moment to explore [Validex](https://www.validex.dev/?utm_source=dev.to&utm_content=using-json-for-custom-validations) out. It would motivate me to create even more contents.

---

## What is JavaScript Object Notation Schema?

JSON Schemas define the structure and validation rules for JavaScript objects. They provide a way to specify the expected shape of an object and enforce validation rules on its properties. This helps ensure that data is in the correct format and meets certain criteria.

Object schemas can be particularly useful in scenarios where user input needs to be validated before it is processed. For example, validating user registration forms, API request payloads, or form fields in a web application.

---

## Introducing Yup and Zod

Yup and Zod are two popular libraries in the JavaScript ecosystem that provide powerful and flexible schema validation capabilities. Both libraries offer rich features and strong type support, making them ideal choices when working with TypeScript.

### Yup

[Yup](https://github.com/jquense/yup?tab=readme-ov-file#yup) is a schema validation library that focuses on simplicity and ease of use. It provides a fluent API for defining object schemas and supports a wide range of validation rules out of the box. With Yup, you can easily define schema validation rules such as required fields, minimum and maximum values, regular expressions, and more.

Yup also supports asynchronous validation, allowing you to validate data against external data sources or perform complex validation logic. It also integrates well with popular frontend frameworks like React, making it a versatile choice for both server-side and client-side validation.

### Zod

[Zod](https://github.com/colinhacks/zod?tab=readme-ov-file#zod) is another powerful schema validation library for JavaScript and TypeScript. It takes a pragmatic approach to validation and aims to provide a seamless development experience. Zod's API is inspired by Yup but comes with some unique features and optimizations.

With Zod, you can define complex object schemas with ease. It supports various validation rules and provides a robust type inference system that ensures type safety throughout your codebase. Zod also includes features like partial and pick helpers, allowing you to create more flexible validation schemas.

### Other Libraries in the Ecosystem

While Yup and Zod are popular choices, there are several other libraries available in the JavaScript ecosystem for schema validation. Some of these include:

- [Joi](https://github.com/hapijs/joi?tab=readme-ov-file#joi): Joi is a powerful schema validation library that focuses on server-side validation. It provides a comprehensive rule set and supports complex validation scenarios.

- [Ajv](https://github.com/ajv-validator/ajv?tab=readme-ov-file#ajv-json-schema-validator): Ajv is a fast and powerful JSON Schema validator that supports a wide range of validation keywords and features. It is highly extensible and widely used in the JavaScript community.

- [Superstruct](https://github.com/ianstormtaylor/superstruct?tab=readme-ov-file#why): Superstruct is a lightweight and minimalist validation library that leverages TypeScript's powerful typesystem to provide compile-time validation.

These libraries offer different features and tradeoffs, so exploring the options and choosing the one that best fits your needs is recommended.

---

## How to Utilize Each Library in a Customized Manner

In this section, I will explore how to implement custom email validation using libraries such as Yup, Zod, and other popular options like Joi and Ajv. Email validation is a common requirement in many applications, and different libraries offer different approaches and features to handle this task. Let's dive into the examples and explore how each library can be used to validate email addresses in a customized manner.

### Email Validation Rules

Here are some commonly used email validation rules that I will be incorporating in the examples:

1. **Email is required**: The email field must not be empty or null.

2. **Email must include @ symbol**: The email address should contain the "@" symbol.

3. **Email must include the domain**: The email address should include the domain name (e.g., example.com).

4. **Email must include the top-level domain**: The email address should have a valid top-level domain (e.g., .com, .org, .net).


![Email Validation with JS Object Schemas](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/gs4f6gg3z0myz3mcftqa.gif)

#### Yup
For an email validation, Yup will use `string` and `test` methods to create custom validation. Here is an example.

```js
import { object, string } from "yup";
const formSchema = object({
  email: string()
    .required("Please provide an email address")
    .test("email-symbol", "Please include an `@` symbol", (value) => {
      return value.includes("@");
    })
    .test("email-domain", "Please provide an email's domain", (value) => {
      return /@[a-zA-Z]{2,}/gi.test(value);
    })
    .test(
      "email-top-level-domain",
      "Please include top level domain such as .co .org",
      (value) => {
        return /\.[a-zA-Z]{2,}$/gi.test(value);
      }
    )
    .test("email-format", "Invalid email format", (value) => {
      const emailRegex = /^[A-Z0-9._%+-]+@[A-Z0-9.-]+\.[A-Z]{2,}$/i;
      return emailRegex.test(value);
    })
});
...
```

You may find the codes in this [CodePen link](https://codepen.io/9haroon/pen/xxBvqPb).

#### Zod
Zod will use `string` and `refine` methods to create custom validation

```js
import { z } from "zod";

const formSchema = z.object({
  email: z
    .string()
    .refine(
      (val) => !!val,
      () => {
        return {
          message: "Please provide an email address"
        };
      }
    )
    .refine((val) => val.includes("@"), {
      message: "Please include an `@` symbol"
    })
    .refine((val) => /@[a-zA-Z]{2,}/gi.test(val), {
      message: "Please provide an email's domain"
    })
    .refine((val) => /\.[a-zA-Z]{2,}$/gi.test(val), {
      message: "Please include top level domain such as .co .org"
    })
});
```
[CodePen link](https://codepen.io/9haroon/pen/JjzgQEZ) of Zod validation.

#### Joi
Joi will use `any` and `custom` methods to create non-primitive data validation.

```js
import Joi from "joi";

const validateEmailFn: Joi.CustomValidator<string> = (value, helpers) => {
  console.log("validateEmail", value);
  if (!value) {
    return helpers.message("Please provide an email address");
  }
  if (!value.includes("@")) {
    return helpers.message("Please include an `@` symbol");
  }
  if (!/@[a-zA-Z]{2,}/gi.test(value)) {
    return helpers.message("Please provide an email's domain");
  }
  if (!/\.[a-zA-Z]{2,}$/gi.test(value)) {
    return helpers.message("Please include top level domain such as .co .org");
  }
  return value;
};

const { object, string } = Joi.types();
const formSchema = object.keys({
  email: Joi.any().custom(validateEmailFn, "test")
});
```
[CodePen link](https://codepen.io/9haroon/pen/RwObbjy) of custom email validation with Joi.

#### Ajv
For Ajv, you have to use `addKeyword` method with `code`, `compile` or `validate` properties to custom how to validate your JSON schema. Referring to its [documentation](https://ajv.js.org/keywords.html), I have created an example with `validate` property.

```js
import Ajv, { ErrorObject, JSONSchemaType } from "https://esm.sh/ajv";
interface Form {
  email: string;
}
// Create an instance of Ajv
const ajv = new Ajv({ allErrors: true });
ajv.addKeyword({
  keyword: 'emailFormat',
  type: 'string',
  errors: true,
  validate:
    function customValidate(schema: boolean, value: string) {
      let message = '';
      if (!value) {
        message = 'Please provide an email address';
      } else if (!value.includes('@')) {
        message = 'Please include an `@` symbol';
      } else if (!/@[a-zA-Z]{2,}/gi.test(value)) {
        message = 'Please provide an email\'s domain';
      } else if (!/\.[a-zA-Z]{2,}$/gi.test(value)) {
        message = 'Please include top level domain such as .co .org';
      } else {
        return true
      }
      customValidate.errors = [{keyword: 'emailFormat', message}];
        // Use a regular expression to validate email format
        // const emailRegex = /^[A-Z0-9._%+-]+@[A-Z0-9.-]+\.[A-Z]{2,}$/i;
        // return emailRegex.test(value);
      return false;
    },
})
const formSchema: JSONSchemaType<Form> = {
  type: "object",
  properties: {
    email: {
      type: "string",
      emailFormat: true
    }
  },
};
// Compile the schema
const validate = ajv.compile(formSchema);
```
This shows Ajv custom validation without `ajv-formats`. [CodePen link](https://codepen.io/9haroon/pen/qBwWdda)

<img width="100%" style="width:100%" src="https://media.giphy.com/media/v1.Y2lkPTc5MGI3NjExYnA1ZjNhOWU2dzBocTAzZDV5bndseno1emc4enV2aWx6MDg4NXQwbSZlcD12MV9pbnRlcm5hbF9naWZfYnlfaWQmY3Q9Zw/5RFGTA8W0m3KxJfKTj/giphy.gif">

---

## Custom Validation Without JSON Schema Libraries

When it comes to data validation, an alternative approach to using JSON schema libraries is to implement custom validation logic. This involves writing code specifically tailored to validate the data based on the requirements and constraints of the application. Here is the example of the Email validation without any JSON schema library.

```js
import { debounce } from 'https://codepen.io/9haroon/pen/GRLgddJ.js';

function validateEmail(email: string) {
  if (!email) {
    return { message: 'Please provide an email address' }
  }
  if (!email.includes('@')) {
    return { message: 'Please include an `@` symbol' }
  }
  if (!(/@[a-zA-Z]{2,}/gi.test(email))) {
    return { message: 'Please provide an email\'s domain' }
  }
  if (!(/\.[a-zA-Z]{2,}$/gi.test(email))) {
    return { message: 'Please include top level domain such as .co .org' }
  }
  return { message: null }
}

const form = document.querySelector("#form");
const errorElem = document.querySelector("#form > p");
const emailElem = document.querySelector('[name="email"]');

const submitCallback = (event) => {
  console.log('Start Validation')
  errorElem.innerHTML = "";
  const { message } = validateEmail(emailElem.value)
  if (message) {
    errorElem.innerHTML = message;
  }
};

form.addEventListener("submit", submitCallback, false);
emailElem.addEventListener("input", debounce(submitCallback, 500), false);
```
The codes has an event listeners for the form submission event and the email input event. When the form is submitted, the `submitCallback` function is executed. Similarly, when the user inputs text in the email field, the `submitCallback` function is executed after a debounce of 500 milliseconds (to prevent rapid validation). In `submitCallback` function. It clears any previous error messages, calls the validateEmail function to validate the email value entered by the user, and displays the error message (if any) in the `errorElem` element.

Here is the [CodePen link](https://codepen.io/9haroon/pen/JjVEPpb). Feel free to explore it further.

---

## Comparing Using V. Without JSON Schema Libraries

Based on various technical factors, I have made a side by side comparison summary below:

| **Aspect**                   | **Using JSON Schema Libraries**                                       | **Without Using JSON Schema Libraries**                               |
|--------------------------|---------------------------------------------------------|---------------------------------------------------------|
| **Standardized Schema Definition** | Provide a standardized way to define schemas for data validation | Require manual implementation of validation logic without a standard schema format |
| **Validation Features**          | Offer a wide range of validation features | Need to be implemented for each validation requirement |
| **Schema Reusability**           | Can be reused across the application or in other projects | May not be easily reusable and may result in code duplication |
| **Data Integrity**               | Enforce validation rules to ensure only valid data is accepted | Rely on discipline to maintain data integrity through custom validation logic |
| **Learning Curve**               | Require time and effort to learn the syntax and best practices of the library | Require understanding of JSON schemas and validation rules |
| **Dependency Management**         | Add a new dependency that needs to be managed and updated | No additional dependencies |
| **Performance Overhead**          | May introduce performance overhead based on validation complexity and data size | Can be optimized for specific requirements to potentially reduce performance overhead |
| **Flexibility** | Provide a structured way to define and enforce validation rules | Offer flexibility without the constraints of a library |
| **Simplified Setup**	| Require an integration of the library and learning its features	| Quick and easy setup for simple validation requirements or small-scale projects |

Considering the pros and cons outlined above, the decision to use or not use JSON schema libraries for data validation depends on factors such as the project requirements, complexity of validation rules, familiarity with JSON schemas, and the trade-off between standardization and customization in data validation processes.

---

## Conclusion

In this article, I explore data validation various JavaScript Object Notation (JSON) Schemas libraries and show how you can use them to implement validation rules for schema validation in TypeScript. JSON Schema offers a formidable mechanism for defining and enforcing validation rules on objects, thereby ensuring data integrity and consistency.

While JSON Schema libraries provide powerful tools, sometimes custom validation tailored to specific project requirements becomes necessary. When opting for custom validation, consider the following steps:

1. **Define Custom Validation Functions**: Create custom validation functions that directly inspect the object properties and enforce your desired rules. These functions can be as simple or complex as needed.
2. **Integrate Custom Validation Logic**: Incorporate your custom validation logic into your application code. You have the flexibility to check individual properties, cross-validate fields, or apply domain-specific rules.
3. **Error Handling and Feedback**: When validation fails, provide meaningful error messages to guide users or developers. Clear feedback ensures better understanding and smoother debugging.
4. **Unit Testing**: Rigorously test your custom validation functions to ensure they behave as expected. Unit tests help catch edge cases and maintain reliability.

By embracing custom validation, you can tailor your validation rules precisely to your project’s needs. 

As summarized above, both methods have their own merits. In fact, depending on your requirements, you can even apply custom validation method to validate data via JSON Schema. What are your thoughts on this matter? Feel free to leave your comments in the comment section. Much appreciated!

<img width="100%" style="width:100%" src="https://media.giphy.com/media/v1.Y2lkPTc5MGI3NjExN2gxYWtndGpiMTR6MWx6emQ1dHdoY3ZsMWkzMXo1cms1bW5yMGlsbyZlcD12MV9pbnRlcm5hbF9naWZfYnlfaWQmY3Q9Zw/zRKJFP8sOSL6Zu7I7j/giphy.gif">

---

## Validex: The Most Complete Validation Collection for React Forms

![Validex - Complete Collection of Form Validations for React](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/zpc9mt2h1jzvzgetnmbt.png)

If you feel like this article helped you, please check out [Validex](https://www.validex.dev/?utm_source=dev.to&utm_content=using-json-for-custom-validations). It has over 36 validators (such as string, password, credit card, email, file, image and more) so that you can validate React forms in minutes.

It would encourage me to continue creating even more contents. Thank you in advance! 🙏