API Design in Node.js (using Express & Mongo) Types of Middleware
This course has been updated! We now recommend you take the API Design in Node.js, v4 course.
Transcript from the "Types of Middleware" Lesson
>> Speaker 1: Now we're going to talk about the different types of middleware in Express 4. So we're using Express 4.0, which is really awesome. But there five different types of middleware in Express 4. There are third party middleware, which is like body parser, and this stuff, the stuff that we NPM installed, this third party middleware.
[00:00:25] There's router level middleware. We haven't gotten to that, but we'll get to that later today actually. So, we'll get into router level middleware. There's application level middleware, which is what we've been using this entire time. What is this? Because when we do the app.use, we're saying this entire application instance is gonna use this middleware right here.
[00:00:47] It doesn't matter what router, it doesn't matter what. Whenever a request comes in, it's always gonna run through here first, and then it's always gonna go through here second. It's always gonna go through here third, no matter what. No matter what middleware we put down here, on another file, another file, it's always gonna come through here.
[00:01:01] That's application-wide middleware. So body parser is a third party middleware, and it is also registered on an application level, so it's both. Then there is error handling middleware. So, error handling middleware is pretty awesome. So the way that works is, again this can also be application wide or router level.
[00:01:23] We'll talk about router level, but the application or error handling middleware is a little different than regular middleware. Because it is a function, just like all the other middleware. But it takes in one extra argument, and that's an error. As a first argument, you actually have to put that there.
[00:01:41] You don't put there, express won't think that you're making an error middleware. It will just be like, this is regular middleware. If you put error, request, response, and then next. Now, expressing those you're doing errors. So, how do we actually trigger this? I'll show you. So, I'm going to say console.log.
>> Speaker 1: Error. Or actually this doesn't need to change the colour, we're going to say log. And then I'll go another one up here so we can see
>> Speaker 1: So what we can do is, let's say for instance, in Linux. We trigger an error. Instead of doing a response, we can just say you know what, do next.
[00:02:26] And throw an error, so new error nooope. All right so if we do that by passing this error into next, we're saying one go to the next thing in the middleware stack and two it's an error so that's going to trigger our error handling middleware.
>> Speaker 1: Does everybody see that?
[00:02:57] So we're gonna run this. I think I actually have to move this above this but let's see, we'll see. So, we're gonna run this. What was it, slash lions, yeah, so I'm gonna go to slash lions in the browser.
>> Speaker 1: Cool, wait I forgot to put in next right?
[00:03:21] That would help.
>> Speaker 1: Let's try that again. Cool, so there we go, nooope, actually I don't think it's actually triggered, let me go, let me see, no this actually didn't trigger our middleware, it's just like a logging note. Yeah, it's just doing this. So, I think if we want to trigger our middleware, we would have to put this below the stack, so we would have to put this down here.
[00:04:01] Now if you call next, it should trigger our middleware. So, let's try that. Port 3000, nope. There we go. So now, it's triggering our middleware. Because, do you see how I had to change the order of that?
>> Speaker 2: Yeah.
>> Speaker 1: Because I define. At first, the error middleware was up here, and this route was down here.
[00:04:26] So when I called next, there's nothing after this route. These routes are not after this route, they're different routes, they're like siblings, they're not after it. The chain starts up here, here's the first middleware, here's the second middleware, and here's the third middleware, and then each one of these could be the fourth, they're siblings.
[00:04:44] It depends on which route you hit, but they're just siblings. And then the fifth one is down here, yes.
>> Speaker 2: I have two questions. First one, with this error one, does the spelling of error matter?
>> Speaker 1: No, you can do whatever you want.
>> Speaker 2: Okay, so how does it know if it's an error and how does it know if it's an error middleware or just a normal middleware if it doesn't look at the spelling of the first param?
>> Speaker 1: I'm not, I'm guess what they do is they check the length of the arguments.
>> Speaker 2: So if you dropped off next, it would think it's a normal, middleware.
>> Speaker 1: Quite possibly let's check it out. I am not sure about that here remove. So a quick check. So many tabs, okay, yeah it did.
[00:05:32] That didn't know, that freaked out so you need all those in there.
>> Speaker 2: So you need four of them?
>> Speaker 1: Yup
>> Speaker 2: Okay.
>> Speaker 1: I think it says in the documentation why.
>> Speaker 2: Okay.
>> Speaker 1: I just don't want to tell you and be wrong.
>> Speaker 2: Okay, and then my second question is are we able to access that middleware array and like programically drop some middleware or like snap one of our middlewares up to the top or something like that?
>> Speaker 1: No, not by default. You can dig in and maybe, if you want to do stuff like that but by default there is no API.
>> Speaker 2: You just order it any way you want?
>> Speaker 1: You order it, you write it in the order that you want. I haven't been in a situation where I needed to do that, so So again the reason I had to move this down is because the stack middleware starts off as one, two, three and then any of these routes is going to be the fourth one and then the fifth one is this one.
>> Speaker 1: So, this is error middleware step out of these four arguments in here, and this is how you handle error. So, instead of in every single route you'd be like if error throw error, you can just say next and define your error handling here. This is where you would post your error handling service, maybe log to a file, whatever you do with your errors, you would do it in this function right here
>> Speaker 2: So, you just do it in one place. Yeah, Mark you had a question?
>> Speaker 1: There's saying, so whenever you use app.use that's middleware?
>> Speaker 2: Yeah, that's global, app.use is setting global application middleware, yes. That means, I want this app to use this middleware globally.
>> Speaker 1: And then how do you decide which function goes where?
[00:07:05] Like is the error always at the end.
>> Speaker 2: Yeah. So, the other ones are kind of arbitrary. I mean, there is some ordering. Like for instance, express.static. You probably want that to go first, before you set up your api routes. So, for instance, let's say we had a file who's path was /lions.
[00:07:22] That was the files, but we registered this route up top before express dot static. What would happen is when that file for slash lions comes in, it's going to do this instead. When in fact it should went to express dot static and serve this slash lions file. So you want to do express dot static first to get all the files, so the ordering does matter there.
[00:07:42] The URL stuff as long as you do it before your API, because you do it before your API. And as far as your error handling goes, it should be at the end of this stack, because you want to be able to reliably call next in one of these callbacks up here, with an error
>> Speaker 1: And expect your error handling to pick it up, and the only way to guarantee that is to make sure that it's registered beneath it. So this is simple now, but when we start getting into multiple files you might lose track of what's registered where, but I'm gonna show you how easy it is to keep track of that too.
[00:08:13] Because right now everything's in one file top to bottom. We can see what's first what's last, but we're gonna have multiple files with multiple routers. With multiple middleware stacks, and those routes will have their own middleware stacks too. So, it gets pretty crazy.
>> Speaker 2: So, Scott do you have to put next error in every route?
>> Speaker 1: No, so in for an error handling middleware you absolutely have to put these four arguments. So how does the error get to the-
>> Speaker 2: Right, so how does error get here?
>> Speaker 1: Yeah.
>> Speaker 2: So what happened was I threw an error, so if I did a get request/lions, I'm going to call next which means go to the next middleware in the stack.
[00:08:56] So forget about the error right now. Don't worry about the error. But I called next, right. That's automatically gonna look in the stack and see what is registered next. And it's not one of these. These are siblings, they're not lined up next to each other. These are siblings.
[00:09:11] The next thing in the stack is actually this. And then now, if we go back I threw in an error, an object with type of error. Which is passed in right here.
>> Speaker 2: So that's where that came from. Mark.
>> Speaker 1: Could you use app.all and declare the error handler before the routes?
[00:09:33] And then he's saying I'd like to keep all express configs middleware in a separate config file.
>> Speaker 2: Yup, we're gonna get into that. I agree, definitely like to keep all that stuff in a separate config file, that's definitely a good practice. But app.all is, you could use app.all, but that's for something else but you could totally do app.all.
[00:09:53] app.all is like I want to run, I wouldn't put error handling in app.all. But you could do that. app.all is for any request that comes in, or for, I'm sorry, for any verb on this route that comes in, do this. So app.all is like this. app.all, and you can define a request here or a URL.
[00:10:17] And you can write a function so, for any verb to this route put, delete post, run this callback. So, it's a little different than doing app.use. Then you can also not use the route too, you can just use something like that. And that might be closer to app.use, but still it's not the same thing.
[00:10:36] It's not what it's meant to be used for. It's like I want to set up some Common functionality on across all the routes. So for instance, if I wanted to do a log on all the requests through lions, I could just come in here and say req, res.
[00:10:53] Console.log('lions!!!'). And the responsibly make sure I call next
>> Speaker 2: Now what's going to happen is it's going to go here, here, here. And then, because I went to /lions, it will go here first and then it'll go to the appropriate one.
>> Speaker 2: So we can test that, so lets get rid of that Res.json,
>> Speaker 2: Just gonna do nodemon.
>> Speaker 2: So, now if I go back and do lions something's broken. I forgot to do next again.
>> Speaker 2: Next.
>> Speaker 2: There we go. Close enough, you go look at it. Otherwise, there we go. Sorry. Yeah, so it logged alliance and then it kept going.
[00:12:06] Looks like it logged it twice for some reason. I don't know why.
>> Speaker 2: Cool. All right, so back to the middleware notes. So that was me going off on a tangent of error handling, middleware and then there is build-in middle ware which is like express.static. So, express used to have tones of middle ware build-in like bodyParser all of that stuff was like build in then they broke of from connect 4.0 and took it out.
[00:12:36] So, now the only thing that's built in is a few things, Express.static. I can't even remember the other ones, but there's only like two, or three, or four useful ones that are built into Express now. And if you want to access them they're on the Express object. But Express.data is the most useful one.
[00:12:54] So whichever type of middleware, whichever type you use, it's the same thing. It's still a function with the exemption of the error handling middleware, which has to have that error argument. But every other type of middleware, it doesn't matter how you use them, third party, router level, application level, error handling built in.They're still just a function.