How Does yup.addMethod() Work? Creating Custom Validation Functions With Yup

Andrew Titenko
2 min readAug 20, 2018

--

Yup’s documentation is pretty vague about creating custom validation functions and the role of .addMethod() in it. This article will break it down for you.

First let’s take a look at Yup’s .test() function. This is the single most important tool that you can use to create your own test functions.

It can be inserted in the validation chain just like any other built-in Yup’s test function:

const schema = {
username: Yup.string()
.min(3, ‘A minimum of 3 characters is required’)
.max(40, ‘Maximum allowed characters is 40’)
.test(‘test-name’, ‘Validation failure message’,
function(value) {
// your logic
})

.required(‘Please fill out this field’);
}

test-name is used by Yup internally

Function has to either return true or false to indicate if testing has failed or not respectively, or a new ValidationError created through this.createError():

.test(‘test-name’, myFailureMessage, function(value) {
const { path, createError } = this;
// ...
return createError({ path, message: myFailureMessage });
})

Keep in mind that ES6 arrow function syntax cannot be used here as it retains the [this] from the enclosing lexical context. You won’t be able to access methods and variables exposed to you by Yup unless you use older, more traditional function() {...} syntax.

Returned ValidationError can then be found in the .inner property of the error object along with the other errors:

schema.validate(…).then(…)
.catch(err => {
const errors = {};
for (let i of err.inner)
errors[i.path] = i.message; // { propName: 'error msg' }
return errors;
});

Finally, we get to the .addMethod(). Yup.js provides this function to allow you to conveniently reuse your test functions:

Yup.addMethod(Yup.type, ‘methodName’, function(anyArgsYouNeed) {
const { message } = anyArgsYouNeed;
return this.test(‘test-name’, message, function(value) {
const { path, createError } = this;
const { some, more, args } = anyArgsYouNeed;
// [value] - value of the property being tested
// [path] - property name,
// ...
return someCondition || conditionTwo || createError(...);
}
);
});const schema = {
name: Yup.string().required('Please enter your name!'),
location: Yup.string()
.matches(/regex/, 'Please enter the correct phone number!')
.methodName(..., 'Custom validation failure message')
.required('This field is required.'),
company: ...,
}

That’s all there is to it! Now you know what both of the functions are there for.

Bonus topic: Writing asynchronous validation functions

For asynchronous test functions you can return a Promise from the .test() function and Yup will wait for it:

Yup.addMethod(Yup.mixed, ‘methodName’, function (yourArgs) {
return this.test(‘methodName’, message, function (value) {
// ...
return new Promise(...)
});
});

An example Promise can look like follows:

new Promise((resolve, reject) => {
// ... test logic
if (testHasFailed) {
reject(this.createError({ path, message }));
}
resolve(true);
});

Please share this article if you have found it valuable and thank you for reading!

--

--

Andrew Titenko

Writing full-stack React.js applications and building cloud solutions on AWS • Follow me @iamarkadyt