1
votes

The below schema allows me to have the optionalField required if value is set to option1 but if value is set to option2 then optionalField can be set to anything the user desires. Instead, I want the validation to fail if value is set to anything other than option1 and optionalField is passed in.

const validValues = ['option1', 'option2']

const sampleSchema = new mongoose.Schema({
    value: {
        type: String,
        enum: validValues,
        required: true
    }   

    optionalField: {
        type: Number,
        required: function() { return this.value === 'option1' }
        // validation should fail if this property is passed in when value is anything but option1
    }
})

Joi has an excellent way to achieve this using Joi.forbidden():

const validValues = ['option1', 'option2']

const schema = Joi.object({
    value: Joi.string().valid(...validValues).required(),
    optionalField: Joi.string().when('value', { is: 'option1', then: Joi.required() otherwise: Joi.forbidden() })
})

If I validate with this schema and optionalField is passed in to Joi, the validation will fail unless value is option1.

I am hoping to find a way to achieve the same in Mongoose.

Thank You!

1

1 Answers

2
votes

You can use custom validators like this:

  optionalField: {
    type: Number,
    required: function() {
      return this.value === "option1";
    },
    validate: {
      validator: function(v) {
        console.log({ v });
        return !(this.value !== "option1" && v.toString());
      },
      message: props =>
        `${props.value} optionalField is forbidden when value is not option1`
    }
  }

Sample route:

router.post("/sample", (req, res) => {
  const sample = new Sample(req.body);

  sample
    .save()
    .then(doc => res.send(doc))
    .catch(err => res.status(500).send(err));
});

Input 1:

{
    "value": "option2",
    "optionalField": 11
}

Result 1: (error)

{
    "errors": {
        "optionalField": {
            "message": "11 optionalField is forbidden when value is not option1",
            "name": "ValidatorError",
            "properties": {
                "message": "11 optionalField is forbidden when value is not option1",
                "type": "user defined",
                "path": "optionalField",
                "value": 11
            },
            "kind": "user defined",
            "path": "optionalField",
            "value": 11
        }
    },
    "_message": "Sample validation failed",
    "message": "Sample validation failed: optionalField: 11 optionalField is forbidden when value is not option1",
    "name": "ValidationError"
}

Input 2:

{
    "value": "option2"
}

Result 2: (success)

{
    "_id": "5e031b473cbc432dfc03fa0e",
    "value": "option2",
    "__v": 0
}

Input 3:

{
    "value": "option1"
}

Result 3: (error)

{
    "errors": {
        "optionalField": {
            "message": "Path `optionalField` is required.",
            "name": "ValidatorError",
            "properties": {
                "message": "Path `optionalField` is required.",
                "type": "required",
                "path": "optionalField"
            },
            "kind": "required",
            "path": "optionalField"
        }
    },
    "_message": "Sample validation failed",
    "message": "Sample validation failed: optionalField: Path `optionalField` is required.",
    "name": "ValidationError"
}

Input 4:

{
    "value": "option1",
    "optionalField": 11
}

Result 4: (success)

{
    "_id": "5e031ba83cbc432dfc03fa10",
    "value": "option1",
    "optionalField": 11,
    "__v": 0
}