Zod Tutorial - All 10 places for Zod in your React / Next.js app

Video Statistics and Information

Video
Captions Word Cloud
Reddit Comments
Captions
when you're creating an application you're going to have some instances where you're going to get external data into your app for example you may be fetching some data from a different server and the data that you get back may not be of the shape that you expect it to be and that can cause bugs in your app and fetching data is just one example but there are about 10 instances where you want to be extra vigilant when it comes to getting external data into your app because the data that you get may not be of the shape that you expect it to be so in this video we'll talk about what those 10 places are and I'll also show you why typescript alone isn't enough and you probably want to use a schema validator like zot so let's think about what we're doing when we're creating a full stack application so let's think about validating external sources of data and in this video I'm going to focus on react and nextjs but it will work the same in other Technologies as well all right so let's say we're creating a full stack nextjs application we're going to have a front end part right so we're going to have front end and I'm actually never sure how to spell this is this two words or is this just one word let's assume it's one word and we're also going to have a backend port porion so here we'll have back end all right so let's start with the front end here so where does a front end typically get external data from well very typically the front end May fetch some data from the back end right from our own back end so here the back end may send some data to our front end so from the perspective of the front end there's external data coming into the app so let me describe this a little bit by the way this cool tool I'm using is called eraser. they are the sponsor of this video so the front end will make an API request to get data from the back end and let me make this a little bit smaller right so from the perspective of the front end we're going to receive some external data from making API requests to our backend if we're creating an e-commerce website or application we may want to fetch product data from our own back end for example but we can also get data from third party apis maybe we want to customize the website based on the user's location and maybe we use a third party API for that so we could also receive external data from third party apis and the data that we're going to get from a third party API may not be of the shape that we expect it to be and typescript alone will not protect us against that so we may want to use something like zot to validate that the data that we get back from these API requests is actually of the shape that we expect it to be and we we'll look at that in a second but let's just finish this diagram here so where else is our front end going to get external data from well probably also from the user itself right so the user can fill out a form so that's another source of external data right form data and let's see where else on the front end are we going to get external data from well we could also have local storage right so we do not manage local storage ourselves the browser manages that and there's also session storage and other storage but for Simplicity here we'll just focus on local storage we may store something in local storage and then later we want to grab it out of local storage again but we don't know for sure what we're getting back from that local storage it's an external source of data and before we deal with whatever we get back from local storage we may want to validate that it actually has the shape of the data that we expected to have all right so we have four now there's one more that's also a major source of external data and that's actually the URL right so maybe you've seen my other video about storing data in the URL for example search parameters right so if we have a URL like this domain.com and we may store some data in here maybe an ID maybe a color parameter and later we may read this data back into our application but the URL is also an external Source we do not manage the URL ourselves the browser does that right the user could be changing things in here so when we read data from the URL we may want to validate that what we get in our application is actually the shape that we expect it to be because if it's a different shape it may cause issues and it's not only search parameters could also be path parameters but these are the five major sources of external data that your front end is going to receive and because they are external sources there is less certainty that the data that we get from those sources is of the shape that we expect it to be and typescript alone does not help us out against that and I'll show you an example of why typescript alone is not enough so we want to validate that what we get back from these sources is actually the shape we expect it to be so that's why people use schema validators in this video we'll use zot but there is also yup and they all have the same purpose validating that the data is actually the shape that we expect it or want it to be all right now before we continue with the backend let's actually take a look and see how this would work on the front end so let's go over each one here and see why typescript alone is not enough and why we need a schema validator like Zod to validate the incoming data so let's start here with the most common ones that's it's actually going to be API requests so you're going to make API requests to your own back end and this will be very similar as with third party apis on the left side now I have a simple product component and this will fetch some data from our own back end and let me make this a little bit more compact I will also put this one here and the URL here as well all right so let's go over each one so we make an API request now I'm using nextjs here these days we have client and server components and maybe you've learned that you can fetch data in server components and that's totally true but there are still cases where you want to fetch data from the client and just as a simple example you could do that with use effect right you've probably already done that in the past right so with use effect here we can fetch data right so what we could do here is fetch let's say we make an API request to our own back end right so to this route now in nextjs it's very easy to quickly spin up your own backend route so here I just created an API folder and then you can create a route. TS file and what this function will do is just return a product to our front end so here we have a product object it has a name that's a string and a price that's a number and that's what we're going to send back right so the back end here in this example we'll send data to our front end so that's what we're going to receive here on the front end at least that's what we would expect right so here when we fetch then we will eventually get a response the response will be in Json format so we're going to convert it from Json format to a normal JavaScript format and then here we get the actual data we can call that data or product and then here we can do something with that product for example we may want to log the name of the product so we could say product. name and maybe we want to uppercase the product name so on a string you can call two uppercase right so this is a method that's only available on a string now when we do this we don't get any warning from typescript because by default typescript types this return value from apis as any and any means anything goes so basically no type safety and why is that a problem because we know it's going to be a name as a string right and since we know it's going to be a string we should be able to call two uppercat so if I save here and I go to my application here I already opened this up now if I reload the page we should see something in console lock we see cool jeans so there is no issue here this is all working perfectly fine now in the real world you may have different front end and backend teams or you yourself may be changing things on the back end the point being is on the back end you could be changing the shape of the data or maybe somebody else that's also working on this project may change the shape of this product data so instead of of having a name maybe this gets changed to having an ID let's say so now we have no name in the product so if I save here now and now here when we try fetching this data and accessing to uppercase we will run into major issues right so here our application is crashing now because we are trying to access two uppercase on name but name does not exist on our product object right it existed before but for whatever reason and this happen sometimes in the real world the shape of the data was changed and now we have an ID and not a name and typescript here did not warn us about this and you could say oh well that's because this is an any type maybe we should type this as a product right maybe you've learned don't have any's in your code base we should actually type this as something else so maybe we we want to type this as a product but this doesn't solve the issue because if we have type product here we could say well this is going to be an object with a name and a price that's going to be a number right because that's what we had before here we had a name that's a string right and a price that's a number so you could say well okay so this product here that we're going to get back from the back end that's going to be of this shape if we indeed return an object like that here when I reload we don't get any errors we still log it to the console but again in the real world the back end may get changed over time so instead of sending back a name we may be sending back an ID and actually an ID is probably going to be a number so maybe we are removing this name property and instead we're going to be returning an ID so now if I save here even though here on the front end we have type this as product if we now refresh you can see we get an error our application will crash in practice even though we have typed this with typescript right so typescript alone does not help us out here because typescript doesn't exist during runtime it doesn't actually check what's going to be in this variable here because this all gets compiled into normal JavaScript right so here I'm working in a TSX file this is typescript it will be compiled and it's continuously being compiled to normal JavaScript and then the application gets run right after compilation typescript does not exist anymore it doesn't actually look into the variable to see what's there during run time right so typescript is helpful more for when we are actively developing here where it's doing some static checks but it doesn't help us out during runtime when when the actual data is being received from the back end and here we are fetching data from our back end but it works the exact same when we are fetching data from a third party API right so you can type this with typescript all you want but during runtime in reality you don't know exactly what the shape of this data is going to be and because of that you may accidentally crash your application and that can be very damaging in practice so that's not what we want to do we want to build robust web apps that don't crash when they're getting some external data now you could say we can solve this very easily with optional chaining so if we do this if I now reload you can see we don't get any error and this will indeed stop our application from crashing in case the name property isn't here but what if the entire product isn't there what if we actually don't return anything and then we try to access name on product the product is empty let's say so then also here you need to not forget to also have optional chaining here and this will only prevent our application from crashing but what if we want to Output a nice error message to the user this does not help us with that and it's also just messy code another problem with optional chaining is it doesn't really protect you from when the value actually does exist but it's of a different shape so here we may want to log the price let's say but we only want to see the last two decimals so if there are more decimals we just want to cut it off at the second one right so this two fixed is a method that we can use on number types not on other types so when we return this price and it's a number if we now refresh here you will see we don't get any errors this will work and we get undefined because we're not returning a name but we don't get any crashes so okay that's better but what if this is actually a different type so it actually exists the price exists but it's using a method that does not exist on a string right so two fixed is a method that you can use on the number type even though we have used optional chaining here we now have crashed our application once more so to fix this you could go down the road of checking that the type of the product price is a number right type of product price is number and then you can try to use this right so you could go down this route but you can already sense that this is very messy code there's no logic or system in it and therefore this is not a robust solution for double-checking that the data that we get here is actually of the shape that we expect it to be so this is where a schema validator comes into place and a popular one now is called Z but there are other ones as well like yup and they all have a similar function which is to Simply validate the data so whenever we have an external source of data whether it's from the back end or a third party API we may want to use zot so let's take a look at how we can Implement zot here to verify that the product that we get here is actually of the shape that we expect it to be so I'm going to install zot here mpm install Z and this is a runtime dependency right so this is not a depth dependency we will actually use this during runtime so I'm going to close here and then here we can use zot to validate the product so the way Z works is you specify a so-called schema which is basically your expectation or how you want the data to be so let's say we expect this data to be an object so we say z. object and in that object we expect there to be a name and also a price and the name needs to be a string and the price needs to be a number and you can be more specific than that with Zod you can say it should be negative positive number let's keep it a little bit simple here it should just be a number let me import Z from Zod and now we can run whatever we get back here through that schema essentially and Z will tell us if it's the correct shape or not so we can say product schema. pars and then we pass in that product you use this variable the schema that we just created the expectation of what we expect the shape to be and you can call Dot pars on that and pass in whatever you want to check according to that schema now if this whatever we pass in here does not adhere to that schema so it turns out to be not the shape that we expect it to be with the parse method it will throw an error we can wrap that in a try catch and we can catch the error that way or what what people seem to prefer is to use the other method here which is called save bars here if this whatever we pass in does not adhere to that schema it will not throw an error it will just return a result here and that result will have a success property so we can check the success property so if there is no success we just want to log the error because Zod will also give us nice error messages and then we probably want to return here to return out of this function and otherwise there was a success so it is actually the shape that we expected to be and therefore we can now safely continue with this variable so after this check of success it's important that you continue with the variable that was actually checked by Z right so this product variable is unsa this one has not been checked yet but this validated product variable has been checked so here we can safely use it to for example access the name now when you parse it with zot they will actually put it on a data property so here if you want to access name it's dat. name right or price and we actually get some nice auto complete here with Sal as well so we see that we can access name and price and these are typed properly so if I try to do two fixed which is something you can do only on numbers you can see I get a warning here right so this is now properly typed as well right so here if I try to access name and we don't send name from the back end right so here on the back end we are sending back an ID and price so name is not on here so previously we were crashing our application and but now if I refresh here right so you can see our application doesn't crash and we should get an error from zot because zot will check if name is actually in there so if I open up our console here we can indeed see that Z is warning us here we get a nice error message here from zot so here Z will let you know which key there was a problem with so here that's with path so here the path is going to be the key of this object so here for name there was a problem it expected a string and what we got back is undefined because we're not returning name here right if I do return name here let's try that so now we are returning name instead of ID and let's say this price right now as a string but we are expecting a number so let's actually make this a number and see if Z will parse this properly so now it's the correct shape right so now we do have a string for name and a number for Price which is also what we codified here in this schema so now we should be able to access this data. name without any errors so let's try this I'm going to refresh here and now if I open up the console we don't see any errors and in the console we indeed see our name here right so now this is how we can validate incoming data from our own back end but also from third party apis right so if you make a fetch request to a third party API it's even less secure because we don't know what the third party API is going to send back at least with our own backend here we still have control over this now now this was a little bit of a contrived example just to show you how everything works here and the logic behind it in practice if you get external data with typescript you don't just want to type it and assume that it's going to be that shape so here the better type would actually be unknown and then typescript will warn you if you try to access something without properly validating it first and Z just makes it very easy to validate and so we wouldn't even type this in typescript ourselves right so this type product we wouldn't even have this so this would be a more realistic example so now we don't have that product type in typescript anymore but what if you do need that product type because it's likely that we're going to work with this product in others instances as well and we may need that product type for example we may have a helper function get price from product which takes in a parameter product of type product and then we just want to return the product. price and so here we want to type we want to type the parameter as a product so we can only pass in a product so now should we create a separate typescript type type products basic basically just what we did before and now we are basically duplicating ourselves because now here I'm essentially specifying the same as here right so by adding Z here it now looks like we're going to have a bunch of duplications basically every time we have our own typescript type we're also going to have a z schema which basically describes the same and the good news is we don't have to do that we we only have to specify it once here with Z make Z the one source of Truth and then if we do need a tab script type for example for a helper function or somewhere else we can easily infer the type type from that zot schema so now here we have we've already defined this here with our schema so then we can take type of product schema pass that to z. infer and that will give us the typescript type right so here if I hover this type you can see this is the same as what we had before right so then we can use that in other places as well and just make sure that our one source of Truth Zod accurately describes the data that's also helpful because let's say you change the schemer maybe it also needs a description if we would also have a separate type we would have to manually change that as well to make sure they they stay in sync but now because we are inferring it here whenever we make a change here it will automatically be updated here in the typescript type as well right so that's really handy now in practice when you are fetching data like this from an external Source usually you're going to use something like react query to help you out with so react query does not do validation like this right react query is for caching and retrying and things like that so you may want to use zot in conjunction with something like react query and if you're making API requests to your own back end there is an even more robust solution which is called trpc so if you control your own API routes you may want to use trpc which does something similar under the hood now with trpc it is a little bit more of a setup so you really have to buy into that system so if that's too much you could just use zot and if you don't control the back end with third party apis for example you cannot use trpc because you don't control that back end API right so here let me type that here if we're going to get data from a third party API I want to run it through zot first before we actually work with that in our application and let me make this medium actually right and the same is true for API requests to our own backend whatever we get back I want to run it through zot or I want to get an even more robust solution with trpc which does require a little bit more setup all right let's take a look at these other external sources on the front end so very commonly we want to get input from the user right so we're going to have form data and that's also external data for example we may have a form here a checkout form and the user will fill out data here name and email their address city state ZIP code right so you have a bunch of different types of data an email is a string but it can be even more specific it's an email string and a zip code may be a number checkbox is on or off so whatever the user is giving us here we can run it through zot and if there is a problem with the data we can immediately give feedback to the user so they can fix it now in practice when you work with forms you want to use a solution like react hook form or formic to manage your forms because they help out with a lot of issues that you're going to run into with forms for example react hook form will help you out with form validation but also with error and loading States and resetting the form basically form state in general and also with performance so it will prevent unnecessary rerenders so react hook form will help you out with validation so you may say why do we even need Z there well because we're going to submit this form data to our backend from the perspective of the back end that will actually be incoming data and we'll look at that in a second so then when the back end receives that data it may want to validate that data as well because you cannot really trust anything coming from the client and since that's going to be the same validation as here on the front end it would be nice if we could reuse some schema so we can use it both on the client side as well on the back end and with react hook form well that's only on the client side right so if we do form validation with react hook form well that's just going to be here on the client side we cannot reuse that on the back end however if we do form validation with zot we can use that schema here both on the client side as well as here on the back end when we get that incoming form data and I actually did a tutorial on react hook form so check out that video if you're interested in that but just to give you a quick example from that tutorial so with react hook form if I scroll down here we have a form and we want to use react hook form for easily managing form States so with errors and loading States and resetting States and also with performance so we still want to use react hook form for those reasons right so react H form I can use the use form hook here it will give us a way to deal with form State error and loading or submitting States resetting the form and it will give us that performance but the form validation we want to do that with zot so here we can connect Zod to react hook form with a so-called resolver so Z resolver and then while with Zod you always have a schemas so basically a description of what you expect or want the data to be like and this example has a sign up schema right so with Z we can then uh specify what that should look like so there should be an email and with zot we can also be more specific than typescript by the way way so if we have some email field we can say it should be a string but we can be even more specific it should specifically be an an email string and then with password for example we can say it should be a string but also at least 10 right so you cannot do that with typescript and then here with Z we can also have a nice custom error message so if there is a problem with that well we can customize the error message and with zot you can also do more advanced validation for example make sure that confirm password and password are the same so here you have the refine method which will give you access to all the data from the form in this case and then you can check if the data. password is strictly equal to the confirm password and then here you can specify a custom error message and then we can also infer a typescript type from this schema and why is that beneficial well if we scroll down here we can also specify a type here for react hook form and we can specify that this form is going to be of this type so with email password confirm password so then here when we register this input for react hook form and I make a mistake here typescript will help us out here we get a warning here and we can fix our mistake and then also when the form gets submitted here on submit we get data here and we know that this is going to be of that type so here we can also specify that type so then we can easily work with this data because we know what fields are going to be on there this has been validated through Zod before we actually run this function and the real benefit is now that when we actually submit this form right so from the front end to the back end from the perspective of the back end that's external data coming in right so actually there should also be an arrow going from the front end to the back end so it's a two-way street the back end will also get data from the front end and we want to do the same validation on the back end as well so we could put this schema in our library and then import this on the back end as well we don't have to duplicate ourselves we can just have Z one source of Truth we can infer typescript types as well as use it both on the client and on on the back end which is really nice because now if we change a field in our form maybe we want to add address if we just change it here and we import the same schema on the back end it will stay in sync because we have one source of Truth so our typescript type will be in sync our back end will be in sync and that's much more robust than if you would do it in a different way so also when we're getting data from a form we also want to run it through zot before we do anything else with it in our application so let's take a look what else do we have we also have local storage and let's say we have some kind of cart component we want to store the cart information in local storage so then if the user comes back we can quickly grab it from local storage again so what we can do here is we could say cart well we can do local storage get an item and that's going to be in Json format so we want to convert it from Json into a normal JavaScript object so we do json.parse it could be that the user cleared their local storage and therefore this get item here could return null so if it's null we could also have an empty array let's say or an empty object all right so then we have a card object at least that's what we expect so then you may want to do something like console log card. total price perhaps right maybe the card object you expect that to have some total price property and here we don't get any warning from typescript because if we hover card typescript types this as any which means anything goes we can do whatever we want with this right I can now access anything even blah blah let's say because with with any anything goes so so we could type this a little bit better so we could say maybe we expect this to be some type card but typescript does have a better type for when you don't know what data is actually going to be and that's the unknown type and when you type it as unknown typescript will actually warn you when you try to access some property because here it will say well cart may not exist right cart is of type unknown so you cannot just start accessing total price so now what we would have to do is the same as what we saw before so now we have to make sure cart exists that's so if card in here we can do something with card but now we don't know if total price is going to exist because it could also be an empty object and so we could add more checks here and then try to access the stuff that we need right so that technically does work it's not a clean structured way of doing it so here whatever we get back from local storage we also want to run it through Z which will make all of this much more ergonomic and so what we could do here is we could create a card schema so we could say card schema is z doob and it could also be an array by the way right so maybe we're just storing an array of products in that case if the cart is empty we probably just want to have an empty array and then this wouldn't be an object we also have array in zot so here I can import Z and then what we can do is in the array we want to have a bunch of objects so we can say in the array we have z. object right so we have an array of objects and then every object has an ID so z. number let's say and maybe also a quantity so we could say the quantity is going to be a number but we can be more specific so what we can do what we cannot do with typescript but what we can do with Z here is we can say it should be an integer and specifically a positive integer right so with Z you also get more fine grained control than with just typescript and then we can run this variable through that schema so we could say validated card we're going to parse that card on to see if it adheres to that schema if that's not the case so if there's no success well what we want to do probably is remove whatever was in that local storage because it's outdated right so realistically this could happen if for example the cart that you stored in local storage maybe they were still storing data from a long time ago and Meanwhile your app has updated to have a different shape of cart right so here when you when you grab the cart from local storage it may still be a different shape an outdated shape so if that's the case we may as well just remove it from local storage and then whatever we want to do here right return and if the card is of that shape so if success is true we can now continue safely knowing that validated cards here this variable right so not this one we need to use this one will now have a data property on here from zot which will then have this shape right so the schema so you can see here I get a suggestion here to map and I can use map here because we have validated that it is indeed an array and map is an method on arrays only and I don't get any warning here because it's already validated whatever you get back from local storage run it through Z and then you can work with it in your app and let's quickly take a look at the last example here for the front end so sometimes we want to store our data in the URL this has many benefits I highly recommend you check out my other video on this and once it's in the URL we want to get it into our app so we want to read from the URL so we're going to get data from the URL into our app so here in nextjs we can use the use search perams hook and that will give us search perams and for the same reasons as before we don't know what exactly is going to be in here we may have some idea but we want to be sure we want to validate before we continue working with this so we can quickly create a schema here with z just called search perams schema and that's going to be an object and in there we may expect an ID right so here we would expect an ID from the URL uh so we could say z. number I will import zot now one other cool thing in zot is that you can also coer this to be a number because if we read something from the URL right so if I would add ID is five here if we now read that what we get back is actually a string type because everything in the URL is going to be a string but we may want to work with this ID as a number so we can immediately when it when we run it through zot we can immediately transform that into a number we can make sure that it's going to be a number if it's already a number well it will it will stay a number but if it's a string zot will coers this into a number right that's not a powerful feature of zot and maybe we expect a color as well in there this is just going to be any string but we can also be more specific than that with zot so we can say it's an enum and it can only be red green and blue right so then whatever we get back here here we can run that through Zod again now here what we get is actually not just an object so here we do need to convert that into an object first so search perams object so that would be object. from entries search perams and then we can run it through zot right so whatever we get back from the URL we want to run it through zot and then we can do something if there's no success right so if there's no success we want to do something and otherwise we can safely assume that this data property on here right so beginners always make this mistake you should not continue with this object this is pre validation right so this is not safe it's what Z returns here will actually be save it will have a data property with the validated data on there so then here we could be sure that we get an ID and that it's also a number on the number you can do two fixed right so here this works we don't even get a warning here because this will also be properly typed right so here I also get autoc complete so I get ID and color right so this is another reason for using zot all right so these were the major sources of external data on the front end now let's take a look at the back end so let's continue with a nextjs example so in nextjs if you're creating a full stack application you can also very easily add backend functionalities so we do that with these API route handlers so we have these API uh route handlers but these days we also have server actions and we also have server components so these are react components that only run on the server so let's think about where this back end will get external data from well we already saw that here when we submit something from the front end to the back end from the perspective of the back end that's incoming data and generally speaking you cannot trust anything coming from the client so here when we receive data from the front end we want to run it through Zod or a more robust setup even is trpc right so form data for example we're going to get that here on the front end and we run it through Zod and if during parsing there is an ISS issue with the form data we can immediately give feedback to the user so they can fix it and if everything is good we will gladly accept it into our front end application and probably eventually submit it to our server and then here on the server side when we receive that data we're going to parse it again and with Z you can reuse these schemas so you only have to create it once and you can use it on both the client side as well on the server side and if it validates again we can very safely work with that here on the back end now typically when you submit form data for example in next JS you're going to work with that in these API route handlers or in server actions right so you don't submit something directly to server components and I have a complete react and nextjs course so check out the links in the description but there are other sources of external data for the back end as well for example the back end may also make third party API requests themselves right so the back end can also make third party API requests and let me make this a little bit bigger not that big right so when the back end gets data from the third party API whatever ever we get back we may have some idea what it's going to be but we don't know for sure so we want to run it through Z first we cannot use trpc because we don't control the back end of that third party API and let me make this a little bit bigger and another source for external data for a back end is web hooks so if you're working with payments for example if somebody successfully completes a payment with stripe let's say stripe will send you a message they will let you know that somebody successfully completed a payment and they will include data in there so they include data of who paid and how much they paid for which product and so before we actually work with that data you can already guess it we're going to run it through zot first right so these are all Network requests so we get a network request from the front end third party apis as well as web hooks but there are other sources of data for a back end as well for example you may be loading environment variables into your app right so I will say environment variables and there are actually many things that can go wrong with loading environment variables into your app so typically when you work with environment variables you also want to make sure that they are loaded properly into your app and you can do that with zot as well and I'll show you an example of all of them in a second right so here this is also something we can run through Zod first and environment variables you can load them anywhere here so in these API route handlers in server actions as well as server components and for the other ones by the way so a web hook will not get submitted to your server component or server action so a web hook will only get submitted to your route handle and the same for a third party API all right so what next where else is a backend going to get external data from well maybe you're reading data from your file system right maybe you're loading some file or data in a file into your application right the file system is an external system and there are things that can go wrong there we can run that through zot so what else can we get data from on the back end well we can also read data from the URL so on the back end the same as on the front end actually so so we can actually just copy this I can copy everything here because it actually works almost the same on the back end so here I also want to run it through zot now typically you're also going to have a database and typically these databases are a separate server so you would have a separate server here for a database let's say we have some postgress database and here on the back end we may be getting data from that database right so here is another flow of data into our back end now typically you use an OM for this so typically there is an omm in between here so that could be something like Prisma or drizzle or if you're using mongodb it would be mongus for example and they are sitting in between your back end and the actual database server and getting data from a database you can do that in all of them so you can even fetch data from your database in your react components these days as long as they are server components and all of them can also interact with the file system and they can also all deal with the URL let's quickly go through them and see an example of them so very commonly we're going to get API requests from the front end and this will be very similar for all of these three right so these are all Network requests that may have some data for us so we want to run that through zot so here on the left side let's say we have some API route Handler that will receive a post request so that the front end can submit something let's say the form that we were talking about before so here is where we're going to receive that form data so here we're in a route Handler and it's going to receive some data here so here let's see we will get a request and we want to see the body of that request so we can parse that body we can we can take the request and convert it from Json into normal JavaScript object and this body is now going to be type any that's the default in typescript a better type here would be unknown as we've seen because we don't really know what it's going to be so we want to restrict ourselves before we try to access anything for example maybe we expect the body to have some kind of body body do address maybe we expect there to be an address property on the body and when you type it in typescript as unknown typescript will warn you that we don't know what what it's going to be so you cannot just access that here right so as we've seen to prevent that we would have to make all of these if statements and then also fify that if they exist they are of the right type and you could go down that route but it it becomes very messy it's not really a structured way of validating that something exists and that it is of the type that you want it or expect it to be now this is again a place where we want to run it through zot now as we mentioned before since we are going to do the same validation as on the front end here is probably where you want to reuse a schema you you want to repeat yourself by defining another schema here so here is where you would import a schema that you have already used somewhere else so what you can do is you can create a library folder and in there you can create a file where you're going to keep all of your schemas so you could call this validations TTS and maybe you also want to have a file for your typescript types and then here in validations is where you would have the schemas that you want to reuse so we could have for example we want to export that a checkout form schema checkout form schema and and that's going to be an object and that could have a bunch of things in here let's see if co-pilot can suggest some interesting things well actually I get a name here and you can see how fine grained we can get with aad so we can say minimum custom error message a maximum custom error message and even optional and we can even say an email a phone number right that's also very helpful an address right and again imagine you have to do all of this with a bunch of if statements so you would have to check if the string is an email does it have a certain length is the phone number a certain length and if there's anything wrong with them we would have to create a custom error message right it would be so messy to do ourselves so Z is actually just a very ergonomic way of doing this right maybe payment method some enum and let's say that this is how we expect our checkout form to be shaped now we can import that on the front end and use it to validate the incoming form data and now we can also import that here on our back end so we can say par form is going to be checkout form schema which we can import from our validations file and we can do save pars and just run the body through there and if that's not the right shape we want to return a next response let's say we'll send back some Json we can send back the errors right so Z will give us nicely shaped errors and we can we can change the aror format and we can also send back a status code of let's say 422 which means the client submitted a male form data and otherwise if it was successful we can continue here with this data Maybe stored in a database or Pro process it further and return some success response perhaps and the nice thing here is let's say we want to change the shape of the checkout form since we're using it on both the client as well as here on the server you may expect there to be issues but here we won't have issues because we have centralized this in one place we have one source of Truth so if we change something here maybe the name should be at most 25 now immediately on the front end as well as on the back end is the validation updated and also if we derive a typescript type from this right so maybe we we do need a type in our application so then here in our types file we could say z. Inver and here we need to pass type off and then it's that checkout form schema which we can just Import in this file and then we have a checkout form type which we can then export from here right and now if we make a change here or let's say we want to remove the cash option here from payment methods I can update it here and now it's immediately synchronized with both front end as well as back end and synchronized with our typescript type right so it's really nice to have one source of truth right so this was an example of how to parse incoming Network requests on the back end since we control the front end and back end ourselves we can use something like trpc to make it really robust or you can just use Zod like what we've done here now if you're getting incoming data from a third party API we don't control that side of things so we cannot use trpc there so here is where we where we should actually use something like zot and if you have incoming web hooks it's the same story as with third party API we do not control that side of the equation so here we want to use Z to validate the incoming payload so with web hooks if stripe sends you a web hook you do want to verify that the incoming web Hook is actually coming from stripe so in this example we were submitting form data from the front end to our API broud Handler now these days you can also submit data with server actions but fundamentally nothing has changed here so you still can't trust what you get from the client so also in a server action you would validate the data and you don't submit data to a server component so that's not relevant here all right so then let's see what else we have we also have environment variables so a lot of things can go wrong with environment variables so one approach you can take here is to create a simple typescript file let's call that .ts and here you can actually just create a schema for what you expect the environment variables to be so we can say z. object it's an object because what we're going to validate is the process. EnV right so in in a nodejs environment you're going to get process. EnV which will be an object with all the environment variables right so we want to validate this variable so that's going to be an object and this may have things like database URL and we we want this to be a string now maybe importantly we expect this to be a non-empty string so this should be at least one characters you can have things like a port a port may actually be a number and we have maybe some third party API key also a string with a minimum of one right and then we can say par en andv and we can parse we can take that schema and we can say uh run that process. EMV through that schema and now this could be a good use case for the parse method because if there is something wrong we actually may want to throw an error and we can immediately fix it as developers so instead of using save parse you may want to consider using the parse method here and then we get the pars environment variables here which we can export here so then if you want to use environment variables somewhere else and that can be anywhere in the route Handler server actions server components let's say in some route Handler here let's say we want to use one of those environment variables instead of using process. EMV we can use this exported variable par EnV parv we can import that from there and then we even get autocom complete here so we we can see which ones we have so database URL and third party API key that autocomplete is not what we get with process. EMV right so if I use process. EMV I don't get autocomplete here I can't see database URL here but we do get that here and now we also know that it has been loaded properly right so that's another a good use case for Z all right so then let's take a look at file system so we could also have let's say Json data in our local file system so maybe in our library we have some Json here let's call data. Json and that's just going to be some data like this right and maybe we want to get access to this somewhere and that can happen in any of these right so let's say it's in our route Handler again and so now here let's say we want to get that data instead of hardcoding our product here we may want to get that data from that Json file and that would look something like this so here we would get the Json directory I will import the path module and then we can read the file content there with fs. read file and we need to import the the promises version from the file system module so then we get the file contents but again we don't know for sure whether that's the shape that we expect it to be so then we can run this through some product schema that we may have which will just make sure that has this ID name and price and it also has and also for example whether price is and number right so we would create a product schema like we've done before and then we can be rest assured that what we got from the file system is indeed the shape that we expected to be all right so let's take a look at the URL next so also on the server we can read data from the URL so let's say we have a server component so this is a server component and server components get access to also the search params so we can say search perams and here we get it as props so before here on the on the front end we got it through the use search perams hook but here we get it through props and so we can read data from the URL in a server component and next to s does give us a type for this so here we can type the props and then we can say search per Rams should not be any but search per Rams itself is an object and nextjs gives us this type so it's going to be an object and then each key is going to be some string and the value for each key is going to be string or an array of strings or undefined but when we read data from a URL we want to make sure that it has a particular shape so for example we can say search prams schema we want to make sure that what we get is some object with an ID let's say and here what we can do is we can coers it to be a number again because everything you get from a URL is going to be a string so here with an ID for example we may want to transform it to a number and also color that it's only going to be red green or blue these search params we can run it through our schema so we take search Pam schema save pars pass in whatever we want to validate and then this is what we get back and this is then what you want to use right so any further processing this is a typical mistake people sometimes make the mistake of then using search perams this is not validated right this is unsafe it's this variable now that we get back from zot that we want to use and zot will give us a data property with with all the data so it will have an ID and color in the success case or if there's an error Z will give us that in do error all right so then let's take a look at the last one and this is actually a major one because we often get data from a database of course in our back end and we can get data from our database in route handlers in server actions as well as server components so let's say we have some product page here this is a server component so these days what we can do is we can fetch data here from the server right in a server component so here I'm using let's say Prisma as an omm and here we attempt to find some product where the ID is one and then here we get data product when I hover of this you can see this is actually typed as something so Prisma does type this so do we still want to validate data that we get from our own database well here I would say it's a little bit less necessary because Prisma is essentially acting as an intermediary here so with Prisma we are also creating a schema in which we describe what the data needs to be so for example for a product here we will say that needs to have an ID name and price and so if we ever try to insert something in the in the product table that's not of this particular shape Prisma will prevent us from doing that and also when we get back data from this table Prisma will will essentially guarantee that it's going to be of this shape so then do we really need to parse this whatever we get back from Prisma here well technically of course it's always possible that what you actually get back over the wire is something different than what you would expect so to really exclude any Edge case you may want to run this through a Zod schema as well and I would say especially if you're doing raw SQL queries is where you want to do validation because whatever you pass here Prisma will transform this into a SQL query for you and that's a little bit safer than doing it yourself so if you're going to do it yourself with SQL queries there's a higher degree that what you get back is different from what you expect but if you're doing it this way with the Prisma apis you know you can make the argument that it's not necessary here now that being said it's possible that you're simply passing the wrong query here so you make some mistake here maybe some logical mistake here so it's always possible that what you get back here is different from what you expect so you may want to run it through a Zod schema as well and now you could say Okay so we're going to duplicate ourselves because we also create a schema with Prisma so now we would also have to create a schema with zot right which is essentially going to be the same as here with Prisma however there are mpm packages that can take a Prisma schema and convert it to a Zod schema so that you're not repeating yourself so then for any database related validation you can make Prisma the one source of Truth and then if you do want to do that edge case validation with Zod you can just use a package like Zod Prisma types which will create those Zod schemas for you from the Prisma schema all right so that was a lot to take in now in my reacting nextjs course we also talk about this and you will see in real world applications how to Implement all of this so check out the links in the description I hope it was helpful I hope you learned something thanks for watching and I hope to see the next one bye
Info
Channel: ByteGrad
Views: 50,662
Rating: undefined out of 5
Keywords:
Id: AeQ3f4zmSMs
Channel Id: undefined
Length: 49min 3sec (2943 seconds)
Published: Tue Oct 03 2023
Related Videos
Note
Please note that this website is currently a work in progress! Lots of interesting data and statistics to come.