Validate Data With Zod in Server Actions Next.js (Client-Side + Server-Side)

Video Statistics and Information

Video
Captions Word Cloud
Reddit Comments
Captions
in this video we're going to see how you can validate data when you work with server actions in XTS so this example is using a server action and if we make a mistake here and I click on ADD we're going to have this toast message that will show us a result error right so we're going to use result to validate the data so let's see how we can do that so here we just have a simple to-do's page it's fetching the to-do's from the database so we have an H1 we have the form I have collapsed the form here and we have the list where we map over all the to-do's now this form is using a server action it's called add to do and I have defined it here in its own separate file now since this is a server component I could also Define it here but for organizational purposes I prefer to put it in its own file with use server at the top so this function will just take the content that's basically what we input here and then it will attempt to insert that in our database and then it will revalidate our path all right so how can we use results here to make it all a bit safer so let's say we want to validate the data both on the client side as well on the server side so I said this is our server side but here on the client before we actually send it to the server before we info the server action we also want to validate the data and right now what we have is only a server action that will immediately get invoked when we submit the form but now we want some client-side interactivity so we're going to have to refactor this form into a client component because you cannot have clients have interactivity in a server component so here in the components folder I'm going to create a component called form and I will just put my form in there RFC form and just paste it here it needs access to my server action so I will just import that here I don't need this and now I can use this form component like this I need to input this and we can remove this import all right so this is still a server component now and now we're going to make this form a client's components so I'm going to use the use client directive at the top of the file and I'm going to save here alright so now we've refactored this into a client's component so we can add client-side interactivity here and the way to do that here with server actions is to have a client action so we have server actions they will only run on the server but we can also have client actions basically just a function here that we give to action in here and that will run on the client so we can actually Define that here we can actually just call that client action and it's going to be an async function that will take informed data and in the client action we can actually invoke the server action here so I'm going to remove it here so the add to do is our server action we can just invoke that here but then we can also have client-side interactivity here so we can for example reset the form what we're going to do here is client-side validation and what we're also going to do here is after we invoke the server action depending on the result of that we may want to Output an error message to the user and we can do other things here as well like be optimistic with the UI for example it's actually a really cool hook use optimistic check out my other videos and server actions right so let me just remove this so these are the two things that we're going to do in this video and I'm using typescript here let me quickly type this as form data and then this is what we can pass here to the action attributes here right so this is all code that will run on the client and then the server action when you call that all of this code will run on the server right client and server actions alright so we're going to use zot here to validate this client side right now before we actually do the client-side validation with Zars we're just going to construct a new to do object that we can then validate so what we can say here is const new to do and these to do objects in my example appear are objects with a Content property and we can get that from the form when we submit the form we have this input here and a button so the only thing we actually have to pay attention to is this input here and it has a name of content and that's basically the text here so we can just grab that with from data dot get content right so if you pass a function here to action you automatically get from data from next.js right so then you can use dot get to get the actual value of that input and these objects optionally have an ID it's going to be a number now typically these IDs get created when we add them to the database right so that will only happen here we actually insert it with Prisma it will actually create an ID so here when we just grab the information from the form it doesn't have an ID yet because it hasn't been inserted in a database yet so we're going to make that optional right so here we're not going to have an ID because we don't have that when we first insert it into the database but later later when you updated to do you do need an ID right so we're going to make it optional and so this is just a very simple new to do so then we want to validate this with zot so I'm going to install zot npm install result okay I've installed this and with that you want to create a schema so we will just Define that up here for now so what we can say is we can say to do schema and then we can use zot so let me import that we just we only have to import Z from zot right so here we were grabbing the actual data from the form and now here we're going to describe how the to-do should look like so we can say well it's an object so we can say Z dot objects and then in the object we will have a Content property and optionally that ID property so let's start with content here so we can just say this content property should be a string so you say C dot string everything else that you want to add here so here we can say it should be trimmed remove the white space It should be at least one character but at most 100 characters right so this is a simple example of how zot works right and what you can then also do is you can provide custom error messages so if the minimum is is not at least one we can say that to do content must not be empty or must be at least one character long imagine if the person goes over the maximum we may want to Output a different message to do content must be at most 100 characters long right so this is our schema now basically the blueprint for to-do's this is what the to-do should look like that's a Content property but we also have an ID property optionally so we can say Z that's going to be a number and it's going to be optional right you can just Define it like this right so now we have our schema now we can use this to validate that new to do so we can say to do schema and we could say dot parse parse this new to do object now if we do it like this and there is actually a problem here for example maybe we forget the content property it will give us an error it will actually throw an error so you would have to wrap it and try catch and there's nothing wrong with that but in practice people do seem to prefer the other way which is safe bars and here what happens if there is an error is there will not be an error thrown we just got a result here and in that result variable we can check if they're actually actually it was an error so if the success was not true we can output an error message right so here we can already output an error message on the client here we're also going to Output an error message if something went wrong on the server alright so let's actually see what we get if we actually do make this mistake of not adding the content to the objects so I'm going to inspect here to my console and here what we can do is we can just lock the results so now what we have is a new to do which is actually just an empty object here but we have just told Zod that this object should have a Content property and optionally an ID right so ID can be left off without problems but content should be there we didn't say that this was optional right so it has to be there and now we have this new to do it's empty so now we're going to use to do schema.save bars on that node to do so this result should not be successful and so we should be able to see some error so let's see what happens when I click add here alright I'm actually going to comment this out because it gives us a red squiggly line let's deal with that later so now we have this new to do with an object it doesn't have the content property and let's say it also has an ID property but it's a string and it should be a number so now we value basically have two mistakes we don't have content and this ID is of the wrong type because ID should be a number if it's there right so now with Zars what's going to happen is we're gonna parse that new to do we're gonna get some result the result will not be successful now and now here we can access the error messages so let me quickly show you how we can access that so you have resulted error and that's going to give you an issue per problem so here we have two problems two errors it's going to create an issue for each one of them so I'm Gonna Save here now I'm going to click add here let's see what we get and we get an array oops we get some other issue but that's not a problem so we get an array here and this array has an object for each issue so here the first one is expected number received string right and we can see path that has to do with the ID and the second issue here is required and it has to do with content right it's received undefined right so we can use the message in here and the path to create one big error message that we can just output as a toast on the page right so let's do that right so what we can do is we can just create a variable here for the error map search it's going to be a string initially empty and then we're going to Loop through all those errors called by not already helping me out so I can say result.error.issues for each issue we want to append that to the error message right so here I get a compiler suggestion and this is not exactly what we want we'll say error message let's see let me make this full screen and let me launch this here so what this needs to be is the previous error message plus the message of that issue plus a period and a space at the end so this message will be for example required so we also want to know the field to what that message is connected to right and salt gives us that here in issue.path and then it's going to be the first element it's an array and it's going to be for example content and then we can add like colon after that so let's see colon space and then the actual message then it's going to be content colon required and then a period and then you have the next error message a little bit it's complicated to work with these result errors result does help you out so they do have two other methods that you can use actually called format to help you format the errors in an easier way to output something on the client this is helpful if you have a complicated format nested objects if you have a more simple form there's also a flatten so if you have no nested objects you can use this now here we have a super simple form and I don't want to dive into the details of zot too much so we're just gonna do it like what we've been doing here this is the error message now and now we want to use a toast message let's say and I'm going to use react hot toast for that so let me install that npm install react hot toast okay so I installed this and the first step is to determine where the toast message should be displayed on the page so basically where should be top right and typically you do want to put that in the layout file so let's go to the layout here this is the root component of our app and what we can do here is we can add that here in the body and it's the toaster component so let me input this and then we can specify the position I think top right is the most common one most people are used to that so I will just use that and that's just the placeholder and now we need to actually in invoke a toast message when we want to actually display a message so here we now have the message constructed now we actually want to Output this so here what we can do is we can use this toast function and we can say dot error and then the message that we want to have displayed in that post message now I need to import this and no help here so let's see import toast from react hot tones actually default export all right so now let's see what we get so now we still have this new to do which has two mistakes it doesn't have the content property because I comment test this out and it has an ID property but the ID property is of the wrong type it should be a number and here it's a string and now it should fill to parse and so we should be able to construct an error message and display that here to the user so now let's try it and see what we get I'm going to write test here press add and we get a very nice dose message here ID expected number receive string content required so now it's very clear to us what went wrong right so now if I fix these mistakes and I add this back and maybe we don't have an ID and ID is optional so we can leave it up so now this should work because now it adheres to this schema from zot so now if I click add you can see there is no error message and because here the save bars will show result that success is true so we don't go into the F block here and here we're just not doing anything yet all right now of course now we actually want to invoke the server action we want to we want to send the new to do to the server so now what do we pass here do we actually use the variable new to do we have just parsed that so it went successfully so we could pass that but zot actually gives us the actual object in the result here as well and it has some benefits of using that one so you can actually use result.data here I think that's a little bit better I'm going to comment this out I'm Gonna Save here so now if I click add here you can see we actually get our to do objects here and it's showing us content test so this is the same as just using new to do right and not entirely true because if we add something here that's not in the schema as something else blah blah so here if we would add something else which is not in a schema right something else the property there is not defined here if we would use new to do here and send that to the server that something else will be on there but if you use this resulted data here if I now click add here you can see it only has contents it doesn't allow this something else to go through as well so it basically strips that information if you didn't specify that for the schema and also has a better typescript support so it's better to continue with the data that we get from zot once we parsed it so that's what we're going to send to the server here so now we're going to invoke the server action we're going to pass result.data and now we get let's quickly line let's see yes and now we have to go to our server action here the server action right now is still expecting form data because that's how we had it defined before we refactor to a client's action so now in the server action we're gonna get a new to do we know what type it's going to be but this is going to run on the server and typically you really shouldn't trust any information coming from the client so I think it's the best that we type this as a noun right we're going to assume this is unknown for now and we need to validate this properly before we can safely assume it is actually of that type to do let me make this a little bit of water right so now we basically want to do server-side validation and it's going to be almost the same as here on the client so we want to have our schema be available in this file as well so we might as well create a lib folder Library folder where we put our types and our schemas so I'm going to create lib and typically you want to have a file where you centralize the types of your project you might as well add our start schemas as well so here I'm going to copy this to do schema and I'm going to paste that here in types now here we need to import this out so I'm going to remove it from here and I'm going to paste it here now we want to export this so we can use it in other files now one other handy thing you can do with SAS is if you want to have a regular typescript type from this you can use zot.infer you can use the schema and infer that time right so we're actually not going to use it here we don't need that here but typically in a bigger application you do need that okay so now we're exporting this to do schema and let's see we are using this here so I'm going to import that right so this is our client action it's quite big this is how it looks like right now in the form component and now we are invoking the server action all right so I'm going to save here we're going to go to our server action here and I'm going to remove remove this so before we try to attempt to insert it in a database we want to validate the data that we get here it's going to be the same as here on the client right so we might as well just copy this paste it right here instead of client side now it's server size validation now we do need our to-do schema it's going to be from that file that we just created now here we're running on the server so let's see if we parse this and it's not successful we go here and we want to construct an error message so this will all stay the same now here we don't want to use toast right we're on the server here we cannot use those messages and so here we don't want to use dust now what we want to do here is we want to return a message to the client and so here we want to return a plain JavaScript object with just an error property and then that message that we construct here so now in our server action if we get something that does not adhere to that to do schema we are going to return a message to the client that's going to be an object with an error property so now we need to go back to the clients right because here we invoke our server action and now it could be something wrong on the server so here we want to assign design a potential response to let's say a response and then we can just check if there is an error on that response object so if response dot error and typescript complaints because response could also be undefined maybe we're not returning anything from the server so I'll use this well if there is an error we can just do the same here we can use those dot error right so toast.error and that's going to be response.error now and so we send an object here with a key of error so we say responsible error that the value of that will be the message right so let me put this here right so now we have client set validation and server-side validation let's quickly test this so I'm going to disable the client search validation here and we're going to pass this new to do right now directly to the server and let's say we make some mistake here so we forget to add content and we add ID but that's going to be a string whereas it should be a number right so now we're gonna we're gonna have two issues just like before so we should technically see the same dose message as before right because now this new to-do has problems we send it to the server action server action going to try to validate that it's not going to be successful it's going to create an error message and it returns that as an object here which we grab here and then output it with a toast message I'm going to save here and I'm gonna press add here let's see what we get and indeed we get a very nice toast message here with a message from the server press let me add this back again right so now we have a really nice setup with very safe validation both client-side as well as server sites I can collapse this so we can see it a little bit better so this looks good so now after we parsed it here on the server we can also use that result object with the actual validated data just like we used before on the client result.data to actually insert it into the database typically you also want to wrap this in a try catch but I have a separate video on that so make sure you check out my other videos and server actions now if you really want to become a professional react next year as developer I have a course on that so make sure you check out the links in the description make sure you've also mastered the fundamentals for JavaScript as well as CSS I have courses on them both hope this was helpful thanks for watching and see you next one bye
Info
Channel: ByteGrad
Views: 21,250
Rating: undefined out of 5
Keywords: nextjs, next.js server actions
Id: tLhcyBfljYo
Channel Id: undefined
Length: 16min 56sec (1016 seconds)
Published: Wed Jul 26 2023
Related Videos
Note
Please note that this website is currently a work in progress! Lots of interesting data and statistics to come.