
Lesson Description
The "API Server Schema Validation" Lesson is part of the full, Fullstack TypeScript, v2 (feat. Zod) course featured in this preview video. Here's what you'd learn in this lesson:
Steve creates a schema validation for the request body sent to the Express server. Partial schemas are also created for routes where the id property needs to be omitted. This lesson also includes a demonstration of inline Zod schema validation.
Transcript from the "API Server Schema Validation" Lesson
[00:00:00]
>> Steve Kinney: Let's start with request body, that seems like a good fun one to arguably the most useful ones. That seems like a place where we know that we shouldn't have an idea yet, really like, what is technically coming in, we can stare at the UI and get a sense, Which is a required title, which should definitely be there, maybe a description, right?
[00:00:27]
The Product Manager hasn't come to me yet, saying they should be able to add a test that's completed yet, why would you do that? Sometimes I do to make myself feel good. Where I'll put it to-do-list I'll check off the first things that are already done. Or in sprint planning, we do that sometimes.
[00:00:39]
But we can basically, one, start by in the absence of a contract, we need to start reverse engineering a contract, right? And so, the contract here is gonna be what can both the client expect from the API and what can the backend service expect from the client, right?
[00:00:56]
And so, we can start here with the request body because in this case, we know that it is a required title and an optional description, right? So we'll just start by putting in this file, I'm gonna have to move it out in a little bit. But we'll start by putting here, where we might say that we have a we could say we have a task schema, so we've got this object, right?
[00:01:21]
And we're gonna say that a task itself has got the ID, those numbers not optional, tell you that much. We're gonna say the string, yeah, cuz an empty string, I think, doesn't count, we'll see. I also don't know what my database respects now that I think about it, can I store a title with an empty string?
[00:01:45]
Does that, I don't know. Description is optional and completed is a bullying that will default to false, that seems about right. So we have that task schema, we have then the, like we said before, we derive our other types as we saw in the previous exercises that we did, right?
[00:02:05]
So we could say export, const CreateTaskSchema, which is going to be, we'll say, I don't need the ID in that case. Unclear if that's gonna bite me, we'll find out together. And then, the update test schema is gonna be a partial, and I don't wanna have an idea in there, cuz I wanna be able to spread everything knowing that I'm not, gonna over write the ID.
[00:02:45]
Other things that I might need is, this is a tricky one, but I don't feel strongly about this one. Like as a task list or task array, as an array thereof, they're sometimes like, don't make a TypeScript type that is just the array version of it. But then you've gotta type array other places.
[00:03:09]
So your mileage may vary, I'm gonna do it right now. And so, now I've got these schemas, they don't do me any good cuz they're not being used anywhere, right? But I can begin to go in. And this is the part where, towards the end, we'll look at how to generate whole sets of schemas and types and database queries all automagically.
[00:03:29]
But if you're an existing code base, that's probably not gonna work for you, right? You can't immediately sometimes just whole hog generate everything, you have to start at the painful part, and begin to move towards the better lifestyle. Cuz in some cases, you are gonna have to look at the actual API contracts and find out.
[00:03:48]
So we'll start with that approach. So in this case, if we went down, there's lots of areas that we can do this, and I'll do some of them, and I'll make you do some of them, and it'll be great. But let's start with this one, cuz this one feels the most egregious, this is one I'm gonna try to put into the database, and I should verify that it is what I think it is, right?
[00:04:12]
And this is where we get to see some of the stuff we were doing those exercises in practice, where I can take this CreateTask.
>> Steve Kinney: Schema, and just parse the body with it,
>> Steve Kinney: Right? And now my task is what I think it ought to be, right? It is no longer any, it has all the correct typing.
[00:04:38]
And if for some reason, something comes in and it's not that, right? Then in that case, we will get to the point where it will blow up, and we can figure out how to handle that, right? We can choose to look at the errors object, if it's theoretically, the one that there's no title, right, we could theoretically throw the 400 there.
[00:05:02]
I'm not gonna go to that level right now in the very beginning, but we can like deal with the fact if it doesn't match up. In this case, this one will almost always happen because it will have blown up beforehand, theoretically, this will also, I would have to change the variables, but yeah, this will definitely confirm that it is what we think it is.
[00:05:23]
And now all the way through, it's a task, look at that. The title is the string, everything works the way we think it's supposed to work. Life is good, right? And this approach is solid, right? We can use this all over the place, we can try it out in update as well, right?
[00:05:45]
Cuz right now, okay, that pram is a string. Previous is any, the updates are any, and then I merge them together and I just write over stuff in the database. What could possibly go wrong? Anything, I regret that joke already, and it just left my mouth. So we can do a very similar idea here as well.
[00:06:11]
And we're gonna have some purposeful edge cases that I built in here, did ID need to be a number? No, did I choose to do that cuz it made things are slightly harder for me, so I could show off, I did. So we'll deal with that in a little bit, but we do need to figure out, for at least some of these, how to validate them.
[00:06:33]
And again, we'll start with a somewhat tedious, noisy way, and then we'll get progressively better about this over our time together. So, I can do something very similar here where again, we could have the like UpdateTaskSchema.parse. And now I know that it is an optional set of things that should not include the ID, right, cuz the ID should come from the URL, I can even pop this await in here as well, right?
[00:07:05]
And I can do something like this should be a full task.schema.parse because it's coming from the database. And now, the previous task that we're updating, and the updates have the correct type. And because they have the correct type, again, they answer question that came up earlier, which is, once you have defended the walls of this part of your application, normal type safety should apply.
[00:07:34]
You do not need to go parsing everything all the time. It is only when either you took a lucky guess that it was what you thought it was, or if it ended up as any or unknown for other reasons, yeah, parse it. But generally speaking, once you've done that, you do not need to get egregious and go everywhere and do it all the time, cuz I did this workshop on performance before, and I'll tell you my golden rule of performance.
[00:08:02]
Not doing stuff is faster than doing stuff, right? And so, if you start parsing all the time all over the place, you'll create a performance bottleneck because you're doing things. Those things might not be that slow, but they are slower than not doing them, right? And so, we'll do it at the gates where it's appropriate, but then, we are well within our rights.
[00:08:25]
We can see, this is the title, this is potentially description, this is definitely a Boolean, right? This is a string, I have, in case you ever, get nervous about stuff, this the API does have 100% test coverage. And you can see that there are some issues here, and I know what they are already.
[00:08:47]
Unfortunately I just send a 400 no matter what. But one of the things that we can do is part of it is Sqlite. Stores Booleans as ones and zeros, so that is a place where we might choose to coerce it into what we think it ought to be.
[00:09:12]
Are those ints or strings? Ints, I think, I don't remember the rules, JavaScript and the string zero. I'm not doing a JavaScript pop quiz right now. Unclear, but yeah, so now theoretically, you can kind of do some of those things and the nice part is, you'll get the bullying now no matter what, through the act of parsing it.
[00:09:34]
Again, we could choose to do the same thing here as well, right? Because query params are gonna treat an ID as a string. The path param will treat as a string, I don't wanna deal with that. So if it is something that can go back to its number format, go ahead and do that as well.
[00:09:52]
So, yeah, and we can keep doing this various places. We'll figure out how to make this, a number or something. But even if we wanted to make sure it was a number right now, Sqlite doesn't really care. We could do something even like,
>> Steve Kinney: You can do it in line if you need to.
[00:10:29]
>> Steve Kinney: Right, and now, despite the fact that all path params are numbers, we did coerce this one into a number, and I would probably pull this one out and reuse it. But mostly to make the point at this point. So now it is a number, it will pass, and so on and so forth.
[00:10:45]
Let's go play around with the UI for a moment.
>> Steve Kinney: Cool, we have it.
>> Steve Kinney: It moves to completed when I check it, there it is, great, everything works.
Learn Straight from the Experts Who Shape the Modern Web
- In-depth Courses
- Industry Leading Experts
- Learning Paths
- Live Interactive Workshops