DEV Community

Cover image for 📝 Using JS Object Schemas to Validate Data with Yup, Zod, Joi and more 🤩
Kittisak Ma
Kittisak Ma

Posted on

📝 Using JS Object Schemas to Validate Data with Yup, Zod, Joi and more 🤩

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.


Validex: The Most Complete Validation Collection for React Forms

Just a quick background about what I'm working on. Validex 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

I would be grateful 🥹 if you could take a moment to explore Validex 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 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 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: 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: 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: 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

Yup

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

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);
    })
});
...
Enter fullscreen mode Exit fullscreen mode

You may find the codes in this CodePen link.

Zod

Zod will use string and refine methods to create custom validation

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"
    })
});
Enter fullscreen mode Exit fullscreen mode

CodePen link of Zod validation.

Joi

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

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")
});
Enter fullscreen mode Exit fullscreen mode

CodePen link 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, I have created an example with validate property.

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);
Enter fullscreen mode Exit fullscreen mode

This shows Ajv custom validation without ajv-formats. CodePen link


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.

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);
Enter fullscreen mode Exit fullscreen mode

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. 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!


Validex: The Most Complete Validation Collection for React Forms

Validex - Complete Collection of Form Validations for React

If you feel like this article helped you, please check out Validex. 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! 🙏

Top comments (0)