If have this validation schema that i use to validate a youtube video id as part of a multi page form wizard:
const firstPageSchema = yup.object().shape({
youtubeId: yup
.string()
.min(11, 'has to be 11 characters')
.max(11, 'has to be 11 characters')
.matches(
/^[a-zA-Z0-9_-]{11}$/,
'allowed characters are letters, numbers, underscore and dash'
)
.test(
'youtube-api',
'video not found or not embeddable',
async value => {
const parts = 'snippet,contentDetails,status'
const response = await fetch(
`${YOUTUBE_API_V3}/videos?id=${value}&key=${YOUTUBE_API_KEY}&part=${parts}`
)
const body = await response.json()
return body.items.length !== 0 && body.items[0].status.embeddable
}
)
.required(),
})
Whenever i validate a string that has less than 11 characters, the .test is executed and tries to fetch information about the video from youtube.
I would like the .test to only run when .min, .max and .matches validation did not produce an error.
How would i do that?
I tried when on the same field, but that seems to be forbidden.
Hey, this is not really possible at moment because validation s are run in parallel. There are few issues in the (one this week) covering it. We are open to prs!
Okay, i'm going to need your help with this. Let me have a quick shot at it!
Implemented a version of sequentual validation. A schema to validate my example would look like this:
const firstPageSchema = yup.object().shape({
youtubeId: yup.string().sequence(string => [
string
.required()
.min(11, 'has to be 11 characters')
.max(11, 'has to be 11 characters')
.matches(
/^[a-zA-Z0-9_-]{11}$/,
'allowed characters are letters, numbers, underscore and dash'
),
string.test(
'youtube-api',
'video not found or not embeddable',
async value => {
const parts = 'snippet,contentDetails,status'
const response = await fetch(
`${YOUTUBE_API_V3}/videos?id=${value}&key=${YOUTUBE_API_KEY}&part=${parts}`
)
const body = await response.json()
return body.items.length !== 0 && body.items[0].status.embeddable
}
),
]),
})
It's pretty likely that i missed something or this will have incompatibility issues with existing features. After all, i've had a look at the code of this module for the last hour or so. But maybe everything is fine - please have a look!
My example works fine, at least.
Have there been any updates with this? Sequential validation would definitely be useful.
Also, I'm not sure but it seems like .sequence is something that was custom-built for the previous example and not part of the official yup API. Is that correct?
Edit: Should have looked at the PR created by @codepunkt before asking the question. My bad!
This would be pretty valuable. For anyone coming here wondering if they can do the same thing, you could save the ID format schema and call it within your other schema.
const youtubeIdSchema = yup.string()
.length(11, 'has to be 11 characters')
.matches(
/^[a-zA-Z0-9_-]{11}$/,
'allowed characters are letters, numbers, underscore and dash'
);
const firstPageSchema = yup.object({
youtubeId: youtubeIdSchema
.test(
'youtube-api',
'video not found or not embeddable',
async value => {
if (await youtubeIdSchema.isValid(value)) {
const parts = 'snippet,contentDetails,status';
const response = await fetch(
`${YOUTUBE_API_V3}/videos?id=${value}&key=${YOUTUBE_API_KEY}&part=${parts}`
);
const body = await response.json();
return body.items.length !== 0 && body.items[0].status.embeddable;
}
return true;
}
)
.required(),
});
That would double-validate the first schema, but those are simple validations so that's probably okay.
Just my two cents on this...
const firstPageSchema = yup.object().shape({
youtubeId: yup.string()
.test('youtube-api', 'video not found or not embeddable', async (value) => {
await yup.object.shape({
youtubeId: yup.string()
.required()
.min(11, 'has to be 11 characters')
.max(11, 'has to be 11 characters')
.matches(/^[a-zA-Z0-9_-]{11}$/, 'allowed characters are letters, numbers, underscore and dash');
}).validate({ youtubeId: value });
const parts = 'snippet,contentDetails,status';
const response = await fetch(`${YOUTUBE_API_V3}/videos?id=${value}&key=${YOUTUBE_API_KEY}&part=${parts}`);
const body = await response.json();
return body.items.length !== 0 && body.items[0].status.embeddable;
})
})
By doing this, if the string validation fails inside the .test method, it will throw a ValidationError which will prevent the api call from running and also replace the error message from .test
Edit: Be careful with type casting, which is not performed by .test(). That's why I used string().test() instead of mixed().test() in this case.
Most helpful comment
Have there been any updates with this? Sequential validation would definitely be useful.
Also, I'm not sure but it seems like
.sequenceis something that was custom-built for the previous example and not part of the official yup API. Is that correct?Edit: Should have looked at the PR created by @codepunkt before asking the question. My bad!