How to validate two fields that depend on each other with Yup
Tags: javascript, yup, yupjs, node
Published: 2020-10-01 20:33:22

Hey guys, let's get straight to the point, I'll show you how to Yup validate two fields that depend on each other.

You may have faced this problem already. If not, you'll face it.

Here is an example of what will happen:

```javascript
const yup = require('yup')
const {
  setLocale
} = yup

setLocale({
  mixed: {
    notType: 'the ${path} is obligatory',
    required: 'the field ${path} is obligatory',
    oneOf: 'the field ${path} must have one of the following values: ${values}'
  }
})

const myNameSchema = yup.object().shape({
  first_name: yup.string().ensure().when('surname', {
    is: '',
    then: yup.string().required()
  }),
  surname: yup.string().ensure().when('first_name', {
    is: '',
    then: yup.string().required()
  })
})

[...]
```
---
`Error: Cyclic dependency, node was:"surname"
    at visit (/home/{yourPath}/node_modules/toposort/index.js:45:13)
    at visit (/home/{yourPath}/node_modules/toposort/index.js:62:9)
    at visit (/home/{yourPath}/node_modules/toposort/index.js:62:9)
   at Function.toposort [as array]...`

---
###**Cyclic error, but how are we going to solve this?**

Fields that **depend on each other to be validated** need to sorted so they are "constructed" in the correct order, e.g. if depend on field **`A`** in field **`B`**, you needs to cast and coerce the value in field **`A`** **_before_** it's handed to **`B`**.

What is happening here is that we are just adding a validation to the condition, so there is really no need to request anything for the validation to happen after everything is constructed already. Due to the flexibility and programmatic nature of **Yup** it can't distinguish between those two cases.

---
**Solution:**
```javascript
const yup = require('yup')
const {
  setLocale
} = yup

setLocale({
  mixed: {
    notType: 'the ${path} is obligatory',
    required: 'the field ${path} is obligatory',
    oneOf: 'the field ${path} must have one of the following values: ${values}'
  }
})

const myNameSchema = yup.object().shape({
  first_name: yup.string().ensure().when('surname', {
    is: '',
    then: yup.string().required()
  }),
  surname: yup.string().ensure().when('first_name', {
    is: '',
    then: yup.string().required()
  })
}, [['surname', 'first_name']]) // <--- adding your fields which need validation

[...]
```
```json
{
    "message": "there was an error validating data",
    "error": [
        "the field first_name is obligatory",
        "the field surname is obligatory"
    ]
}
```
---
**Another example with extra fields:**
```javascript
[...]
const myNameSchema = yup.object().shape({
  first_name: yup.string().when(['surname', 'age'], {
    is: (surname, age) => !surname && !age,
    then: yup.string().required()
  }),
  surname: yup.string().when(['first_name', 'age'], {
    is: (first_name, age) => !first_name && !age,
    then: yup.string().required()
  }),
  age: yup.number().when(['first_name', 'surname'], {
    is: (first_name, surname) => !first_name && !surname,
    then: yup.number().required()
  })
}, [
  ['first_name', 'surname'], // <--- adding your fields which need validation
  ['first_name', 'age'],
  ['surname', 'age']
])

[...]
```
```json
{
    "message": "there was an error validating data",
    "error": [
        "the field first_name is obligatory",
        "the field surname is obligatory",
        "the field age is obligatory"
    ]
}
```
---

I hope I helped you! 😉
Thank you!!

---
My [GitHub](https://github.com/GabrielTerriaga) 😎