Check out a free preview of the full API Design in Node.js, v4 course
The "Adding Validation to Routes" Lesson is part of the full, API Design in Node.js, v4 course featured in this preview video. Here's what you'd learn in this lesson:
Scott adds the express-validator module to the API and uses it as middleware to check the existence of the name property on the body during a product update.
Transcript from the "Adding Validation to Routes" Lesson
[00:00:00]
>> Let's get started then. So what we need to do is we need to install express-validator. So let's do that. There we go. And luckily for us, we already learned what middleware was and is. Because express-validator, one of the ways that you can use it to have input validations is with middleware.
[00:00:26]
So as a quick refresher, middleware are basically just functions, kind of like handlers really, that just run before a handler would run. And they have the ability to have control flow on what middleware is, and whenever they're done, so they can pass it to the next middleware. They have that ability, but they have the same power that a handler does.
[00:00:46]
They can respond. They can log. They can enhance the request object. They could do all different types of things. So we're gonna use middleware as validators for the posts and the puts for all the routes that we already created. So, let's do that. So let's walk through this list here.
[00:01:10]
First one that I see is we have a put request for a product with an ID of whatever the ID is gonna be. So we wanna make sure we validate the input. So first thing first is we need to figure out what are we gonna allow a user to update on a product, right?
[00:01:26]
So if we go look at the schema, we've got a product. What would we allow a user to update a product? And what would we not allow a user to update on the product?
>> You can just name and belongsToUser. I mean, what user it belongs to.
>> Yeah, definitely name, we want them to be able to update the name and things like that.
[00:01:51]
belongsTo, yeah, if we had a feature where we let you transfer the product to another user, then yeah, then we would allow you to update that. Otherwise, that would just get set when you created the product. So yeah, it really depends on what features we have. But I would say, at minimum, name is definitely something we want to allow a user to be able to update.
[00:02:12]
So our update route is checking for validation on that, and it's gonna be up to us whether or not we require that. Because is it the fact that you have to pass us every single possible combination of updates or it has to be one of these? So we have to figure out what that is.
[00:02:32]
See how product only has one field that we can update for name, then that's the only one that we're gonna require. We won't do this because we're not gonna have a feature that allows you to change it at all. We gotta do some other stuff on the schema to get that to work.
[00:02:49]
So lets do that. So the way this works is we look at the stuff that I have here. Let me pull this up. We can just import from express-validator. So I'm just gonna input right here in the router. And we can go down to our put. And because when someone does a put request, a post request, a patch, or something like that, they're probably gonna be setting up json, which is gonna be attached to the body.
[00:03:25]
We're gonna use this body import from express-validator like this as middleware. So we can say body, that's a function, and then we can say the name of the field on the body object that we want to enforce. So what this is saying is req.body, which is an object, should have a field on it called name.
[00:03:51]
That's what this is saying, right? req.body, which is an object, should have a field on it called name. And if it doesn't, we'll know. Does that makes sense, right? Okay, we can test this out real quick. We'll just make a little quick handler here say req, res, just like that.
[00:04:17]
And then really cool option is that we get to control when we wanna do that validation. So the middleware itself won't actually respond with any errors or anything like that. Because express-validator wants us as the creators to have control of how we wanna send those errors back, which I think is really cool.
[00:04:40]
So we can say, let's get those validations, right? So we can say, or let's say, errors. We can say errors = validationResult, and you can just pass the whole request object like that. So the validation was the result given the request object is just going to try to validate based off of whatever we put here.
[00:05:03]
So the way this works if you think about how you know middleware works, what's probably happening here is that this middleware is attaching something to this request object. That validationResult then looks at to determine what validations it needs to check for, right? It could be an array of like here are the validations, you gotta check for a body.name, and it looks for that.
[00:05:26]
So that's what this middleware is doing, it's enhancing the request object. And then what we can do is we can check to see if there's errors or not. If there are errors, then we can send those back, and if not, we can continue doing what we were gonna do anyway.
[00:05:40]
So let's do that. We can say if not errors.isEmpty. So there aren't any errors, as in if it's not empty, that means there are errors. Then we wanna do is we wanna send back an error message. So I just send back a 400 here. 400 status code just means bad requests, which typically means you didn't send the right thing.
[00:06:11]
That's typically what the 400 status code means. Like, yeah, it's your fault, you didn't send the thing. And then I'm just gonna send back some json with an errors field on it, that will convert all the errors to an array. Okay, so let's try to trigger this. Let's see what happens.
[00:06:48]
But remember we have authentication now, so it's not gonna be so easy just to hit it. So we gotta sign in, sign up. We gotta do all that stuff, just to try it. So let's do it. I'm gonna start my server. Okay, And then I don't think I reset my database so I think I still have all that stuff.
[00:07:17]
Let's see. It's gone now. I've gotta do it again. Let me sign in. So I'm gonna sign in. If you didn't reset your database, you can go to Thunder Client and you can sign in with the same thing, and you should just be able to get a JWT.
[00:07:33]
So I'm gonna do that. I just can't remember if I reset my database or not. I did, awesome. Cool, so our JWT here, And then now I'm going to make a new request for a put. So I'm gonna say, http://localhost:3001 in my case, /api/product, and I had to put some ID here.
[00:08:14]
It's actually not gonna get to this point where the ID matters. We'll get to that when we get to the handlers. But if I don't put this ID here, it's gonna try to do a PUT request to /api/product. And I don't think we have that route, right? We don't have a route for put to /api/product.
[00:08:34]
It's /api/product/:id. So we will get a 404 uf I didn't put ID there, because there's no route for that. Does that make sense? We only have a route for a GET request to /product without a PUT request to /product. So we will get a 404. So to trigger this route, I must put something after product.
[00:08:57]
In this case, it's called id. I just typed in something random, because we haven't gotten to the point down here where we're actually using that ID. So it doesn't matter. But when we write the handlers, we will be using that ID. Okay, so let's try that. I'm gonna go to Auth, gonna click on Bearer, go and paste in that token, get rid of my string.
[00:09:27]
And let's run that. I didn't actually do the thing that I said I was gonna do. There we go. Run that and let's see what happens. So right now, it looks like it's just hanging, which means it's not responding to something. So let's see what's going on here.
[00:09:48]
API products, wait. Let's look at the body. Okay, there we go. So let's go in here and add a log to our routes. Here we go. Let's add a log in here and see what the errors are doing cuz I would imagine this would not be hanging. And instead we should see error.
[00:10:10]
So maybe something isn't right, let's see. So run this again. See what happens, go check out the log. Wait, it's there. It's just no errors. Okay, so it looks like everything here is mostly good except now with express-validator. You have to be explicit with the actual validation that you want when using the middleware.
[00:10:42]
So in the case of body name, this no longer is actually gonna trigger an error. Because there is no actual strict enforcement on it, so now you have to be explicit and say something like, this should be a string. So now if I say body must have a property on it called name, which is type string, this will now trigger the error.
[00:11:05]
Okay, so if we go run this back in the Thunder Client, and I run it, you can see that now we get back that object with the array of errors in the message. So invalid value, looking for a parameter called name on the body. So you can take this and format it however you want.
[00:11:26]
It's up to you. It really just depends on what you gonna do with those errors. If this is an API for the front-end, and the front-end is just gonna consume the errors and put them on the screen, this is really cool. But sometimes the front-end is more sophisticated and wants to do its own errors.
[00:11:40]
But either way, I think it's nice just to be able to send back all the errors that happened. And then the front-end can format that whatever way that they want, just like they do with any other data. Okay, any questions on express-validator and how to use it to validate or invalidate inputs for requests like posts and put?
[00:12:08]
Yep.
>> This is the code we're going to write in every handler. I mean, we can abstract out this in-
>> You're thinking so ahead, and I love it. Yeah, I think ideally you would take all the input validators and put them in their own module. And then that way, it's just like, here's the input validators.
[00:12:29]
That way you can reuse them if you wanted to, cuz some of them might have the same, right? And you had to write them twice. So yeah, you probably would just abstract these out, put them in the file, bring them in whenever you need them. Maybe even combine a bunch of them because it's just middleware, right?
[00:12:44]
So you can put this in an array. And you can have as many of these as you want, and you can store that in some variable somewhere. And you can call this new user input validator or whatever. And every time a new user is created, you can use that.
[00:12:58]
So yeah, there's no one way to do it, and there's a lot of different ways to abstract it out. But yeah, to keep it simple, I'll probably just be writing them inline here on the routes, that way we're not adding any more overhead of organizing all the stuff.
[00:13:20]
Yes.
>> Is the errors.array on line 19 coming from express-validator?
>> Yeah, so this errors string is an object, it has a bunch of functions on it. And it has an array method on it that just converts in formats all the errors to an array for you to consume.
[00:13:42]
Yeah, this is not some built-in native thing in JavaScript. It's from the validation result.
Learn Straight from the Experts Who Shape the Modern Web
- In-depth Courses
- Industry Leading Experts
- Learning Paths
- Live Interactive Workshops