Customize zod Error Messages and Handle zod Errors Effectively

While running zod schema validation agains domain-rich objects, the error message saying that a string is not a number might not be really useful.

Especially, when you need to provide a meaningful feeback to the user, e.g. after they have submitted a form with lots of data.

zod allows to customize messages on a very granular level. We can also extract repeatable customization to zod error maps.

Finally, in order to process an error message further (e.g. display it to the user), we need to handle the zod validation errors effectively. Especially, when a big object validation has failed and we could receive a handful of errors.

That's what we learn in this lesson. 🔥

Share with a coworker

Transcript

[00:00] Initially, our object passes the schema validation over here. So let's just run this file against ts-node to see that the output of the script is empty, since again, the schema validation works correctly. Now let's modify this object. And for this reason, I'm just going to spread it and overwrite one of the properties. So let's say that the number of guests is going to be overridden to zero.

[00:27] Now I want to provoke a situation where the schema fails. So let's run this one back again. We're going to get an error saying that there is a too small value for the type of number and the message is the number of guests. And here we have a pretty generic message saying that number must be greater than or equal to 1. Now, we might want to customize this error message, for instance, when we are validating the input that comes from a user field form.

[01:01] So most of these Zod-based utility functions allow us to customize the error message. And so the min utility accepts the value that is going to determine what is the minimum value, but also what is the message. So let's say in this case, we just don't want it to be greater than or equal to one, what one. So we can write that there must be at least one guest, since this is our domain. Now the same for the maximum value so at most four guests are allowed within a single booking.

[01:43] So let's see how would this error look like right now? So we can see that there must be at least one guest. Now, the same way we can customize the error message for the positive. So let's say that, like, how could a price be negative? Right?

[02:02] So let's again, drop this one and provide the price that would be let's say minus 100 and again let's run this validation we've got the message that is negative price huh So this could be directly used within the form that is displaying the error messages to the user. So you don't have to do it on the level of your React, Angular or whatever are using forms, but directly within the schema itself. So one more thing that we would like to take a look at is that when we define the min and max, they make either the number or strings or other different types more precise. But when we take a look at the options of the string over here Then we will see that we have way more possibilities So params is first of all an option bag so an object that is going to include quite a lot of these So let's take a look at the options and we're interested in four of them. So message is what we've seen so if the value under a certain property is wrong then this is the one that is going to be used.

[03:12] When we require a property and it is not passed in the object that is being validated, then required error is being used instead. Now, we might be using both the message and the required error properties to determine what are the errors, but it could happen that we might want to reuse them across different properties of a given schema or across many different schemas within the same code base. In this case, we might be interested in creating a separate error map object that is going to be just pass as the reference to a certain property or to many different properties within a schema. And the description is the property that is going to describe what is the whole meaning, what is the semantics of this property. So this one is not used directly by the validation itself, but is being used by many different tools in the Zod ecosystem.

[04:09] So now let's put the message and we're in the due date property. So let's say that there is invalid due date. However, what is important in this case is that there is a due date message on the string utility, but we could also provide the date utility. So now the question is on which step does it fail? Does it fail on the step that a string was expected and let's say a number has been passed or was a string passed and it failed on the date formatter validation itself?

[04:44] So let's see the difference between the two. So we can just remove this one and provide that there would be a due date which is not going to be a string so this is going to fail on the Z string and let's see this over here and we would see that yep this is the invalid due date however if we want to provide a separate message when it is a string, however, the strings format is wrong. Now we could write that the date format is wrong over here and we can now provide it as a string and this error message is going to be displayed in this case. And we can already see that, yep, date format is wrong. So as we can see, there is a sequence of validators that are being executed one by one.

[05:36] So there is one more useful feature that we will cover here, and that is processing Zod errors on TypeScript level. So as we can remember, parse is conditionally throwing errors, and for this reason, we're going to wrap it with a try catch block. So let's put the catch E over here and depending on our TypeScript setup, E could be either off type, any or unknown. Here TypeScript is more strict so this is unknown. Now if something is unknown then we don't know what it is hence we don't know what are the properties.

[06:09] So it would be nice if there was a way for TypeScript to figure out to apply a type guard to verify what exactly this error is. And thankfully Zod does provide a certain prototype for us to verify what this error is. So we are running the E instance of and here we are using the Zod error. Now, if this if statement turns out to be true then what we know immediately is that the error is something that could have the issue or issues added, there is a cause etc but most importantly it has the issues itself. So since we do have issues it is possible for us that we can iterate over them.

[06:54] So let's just iterate for and here const Zod issue of the E.issues and here we can walk into the details over here so if you need for any reason to manipulate the messages, to use them, to combine them with your local component state, whatever that is, then we can simply use them directly. So what we can do is to access the Zod issue, the code of the error. So for many of these positive, min, max, email, UUID and many other utilities there is a code. So that's one thing. The message is that you might probably want to display to the user if this is more meaningful than the technical messages.

[07:42] And finally, the path saying which property did fail. So let's quickly see this one in action and we can see that there is an invalid string, date format is wrong and the due date so accordingly the code message and the path. So it could happen that instead of parse you might also want to use the safe parse, the equivalent that doesn't throw an error and stores the result in a boolean variable. So let's do this one. So that would be the safe parse result.

[08:11] Let's call it this way and let's just replace the parse with safe parse and let's just stick to the error that we've had right now And if we want to take a look at what the error is Then again, we can run the type guard over this So SP result and let's check whether the error does exist over here. So here we know that the error does exist so there would be a format over here. So yeah this is the format so let's also console log what this error is going to be. Let's run this in the script and we can see that there is errors for the whole schema, for the whole object, but there is also for the due date that there is an error that the date format is wrong. So this is yet another structure that we can iterate over.