How to get first error while using yup to validate object schema

Viewed 11196

I'm using Yup to validate nested data object against validation schema. I want to retrieve the path of the first validation error. I tried it with validate() of yup. It has the option abortEarly which defaults true. So in that case the first error should be returned.

However, I'm always getting the last error. I'm not sure what I'm missing out.

Below is the code that I've tried so far.

  const validationSchema = Yup.object().shape({
    basicDetails: Yup.object().shape({
      firstName: Yup.string().required("Required first name"),
      lastName: Yup.string().required("Required last name"),
      gender: Yup.string().required("Required gender"),
      phoneNumber: Yup.string().required("Required phone number"),
      emailId: Yup.string().required("Email id is required")
    }),
    educationDetails: Yup.object().shape({
      graduationDegree: Yup.string().required("Required graduation degree"),
      postGraduationDegree: Yup.string().required(
        "Required post graduation degree"
      ),
      registrationNumber: Yup.string().required("Required registration number"),
      workExperience: Yup.string().required("Required work experience")
    })
  });

  const dataObject = {
    basicDetails: {
      firstName: "Nik",
      lastName: "Test",
      gender: "male",
      phoneNumber: "9876543210",
      emailId: ""
    },
    educationDetails: {
      graduationDegree: "Degree 1",
      postGraduationDegree: "Postgraduation Degree 1",
      registrationNumber: "",
      workExperience: ""
    }
  };

  const validateSchema = async () => {
    const validationResult = await validationSchema
      .validate(dataObject)
      .catch((err) => {
        return err;
      });
    // this returns last error. however, as per the documentation, it should return the first error.
    console.log(validationResult.errors, validationResult.params);

    const validationResult1 = await validationSchema
      .validate(dataObject, { abortEarly: false })
      .catch((err) => {
        return err;
      });
    // this returns array of all errors in correct order.
    console.log(validationResult1.errors);
  };


Here is the code sandbox link for the minimal sample.

I also tried simple schema but got the same result.

2 Answers

I think you'd need to validate with abortEarly: false and then use the first error. The reason is, abortEarly doesn't do what we think it would. It doesn't stop further validations once one error is found. As the validations are run in parallel per level of the schema.

Hence, I think this is how you'd need to do more or less:

  const validateNestedSchema = async () => {
    const validationResult = await validationSchemaNested
      .validate(dataObjectNested, { abortEarly: false })
      .catch((err) => {
        return err;
      });
    console.log(validationResult.inner[0].path); // gives "basicDetails.emailId" 
  };

the forked sandbox, if needed


FWIW, they seem to be discussing an option for validation chain in this issue but no conclusion/implementation yet it seems.

This may be the solution for you. The current structure of your schema is not nested, but an array of objects. I think if you change the structure to the following, you'll get the results you're looking for:

const validationSchemaNested = Yup.object().shape({
  basicDetails: Yup.object().shape({
    firstName: Yup.string().required("Required first name"),
    lastName: Yup.string().required("Required last name"),
    gender: Yup.string().required("Required gender"),
    phoneNumber: Yup.string().required("Required phone number"),
    emailId: Yup.string().required("Email id is required"),
    educationDetails: Yup.object().shape({
      graduationDegree: Yup.string().required("Required graduation degree"),
      postGraduationDegree: Yup.string().required("Required post graduation degree"),
      registrationNumber: Yup.string().required("Required registration number"),
      workExperience: Yup.string().required("Required work experience")
    })
  }),

});


const dataObjectNested = {
  basicDetails: {
    firstName: "Nik",
    lastName: "Test",
    gender: "male",
    phoneNumber: "9876543210",
    emailId: "",
    educationDetails: {
      graduationDegree: "Degree 1",
      postGraduationDegree: "Postgraduation Degree 1",
      registrationNumber: "",
      workExperience: ""
    }
  }
};

I played around with it like this for a little bit and it seems to work the way you are wanting.

Related