3.1 Writing Middleware
An Express app is really nothing more than a chain of middleware that processes the request. In this lesson, we'll write our own middleware to log requests to the console.
1.Introduction3 lessons, 20:17
2.Building a Web App6 lessons, 1:01:23
3.Middleware and Routers2 lessons, 16:36
4.Conclusion1 lesson, 01:31
3.1 Writing Middleware
In this lesson, we're going to start looking at middleware and more specifically, we are going to write some middleware so that you can understand really what middleware is and how it works. So what we're going to do is log the request. So for any request that's going to be handled by our application, we're going to log it to the console. So we will see more than just listening at HTTP, blahbity, blah, blah, blah. We will see the URLs that we request, and so let's get started. We will create a new file inside of Source, and we'll call it logger. Now, you can make the argument that we need to organize this inside of another folder like middleware, but that gets a little confusing because a lot of middleware is used within an express application. And I think that just putting it here directly inside a source is going to be fine in our case. So middleware is really nothing more than a function. And that function accepts the request, the response, and then a third parameter that is the next middleware in the pipeline, because remember what I said a few lessons ago. Middleware is used to process a request, and our application is just a pipeline of middleware. So a request is received by our application, it send that to the first middleware, which processes it. And then it may or may not pass that request on to the next middleware, which will process it. And then it may or may not pass it on to the next one, and so on and so forth. So that's what this next is, it's the next middleware in the pipeline. And it's important, because if you don't handle it correctly, then you can just really screw everything up. So what we are going to do is have a function that is going to use our request object and build a URL so that we can see that in the console. Because unfortunately, express doesn't really give us an easy way of getting the URL that was requested. We have it in different pieces but we are going to have to build the URL from those pieces. And we can do that with a module from the node API, just called url. And this has a method called format. And then we pass in an object that has different pieces like the protocol, so that's the first thing we need. And we have the protocol from the request object. It is called protocol. The second is the host. Now we don't have a property for the host. But we do have a method called get. This is for getting a single value. It's not for setting up a route or anything like that. And so in this case, we want to pass in host because we want to get the value of the host. And then finally, we need to specify the pathname. And we have this from the request object, but it's called originalurl. And then using those three pieces, we're going to build a URL that we can then display in the console. So let's call this request = url, and then we will simply write it to the console with console.log. All right, so with that in place, all we have to do is then register this with our application. So that's done inside of init. We need to require that. Let's call it just logger. And that is / or ./logger. And then we simply add it by using app.use. You pass in logger and that's it. So notice here that we are passing in a function object, we're not executing it. Now we executed the static function as well as the URL encoded function. The reason being because of those accept different options. They in turn build a function object that is then passed to the use method. In our case, we don't have any options, we just have the function, so we're just passing that along for middleware. So with that in place, we should be able to make this work. And, well, let's scroll up there so that we can see the URLs, and let's just refresh the page. Now if we look at the console, we see the URL so the logger worked, it did what it was supposed to do. But let's go back to the browser. Notice what's going on, the spinner is still spinning and that's because the browser is still waiting for the server to send all of the response, because our middleware didn't do that. We retrieved the URL, or rather the pieces of the URL. Then we logged into the console but then we didn't do anything else. And with a web application, we are sending a response from the server to the client. So if we don't have anything else, then one thing that we could do is just in the response. So whenever we go back and make that request, well, nothing seems to happen. If we look at the console, we are still logging the URL. And if we make a request for another endpoint, like guitars, well, we get the same thing. There's nothing on the screen, if we look at the source code there's nothing there. But if we will look at the console, then yes, we did log the URL. So here what's going on, we are ending the response so that as far as the server is concerned, it's saying okay, I'm done. And the browser is saying, okay, thanks. And that's it. And we didn't pass the request on to the next middleware. So if we look at a nit, we have three middlewares set up. The first is the static file handler. The second is the body parser. And then the third is the logger. And this is the order in which the request is going to be processed. But wait, what happens after the logger? Well, if we go back and look at app, then we set up our routes. Because we already set up our init stuff up here with line 1, we then set up our routes. So by not calling the next function inside of our middleware, we're not passing that request on to the router, so that it will then route the request to the appropriate handler function. So in our case, we don't really want to end the response. We want to pass it on to the next. And so if we go and look at the browser, we can refresh. We then see what we would expect to see and our logger is still doing its job. We see the URL. Now one thing I do want to point out, let's go back, let's get rid of next and let's end the response. None of the URLs appeared to work. But let's look at this. If we make a request for img/guitars/1.jpg, that request worked. And the reason is because we set up our static file handler as the first midlleware in the pipeline. So before our application even gets to log the request, it's going to not reach the logger. Because if the static file exists, it's going to return that file to the browser and then it's going to return the response. And it's not going to keep processing that request because the static handler has already done its job. So, this may or may not call next. Now the urlencoded, you know the body parser, this should always call next. Because this middleware's job is to parse the body into something that can be used later on down the pipeline. So this should always call next. And the same is true for our logger. Our logger is simply there to log the URLs, it should not stop the processing. So this should always call next. So let's go back to our middleware, let's call next, and we will be good to go. Now just talking about the order in which our middleware is going to execute. If we make a request for a static file, we aren't going to see that logged here. Because as far as the pipeline is concerned, the static file handler did its job and then stopped processing the request. So the logger never even got to execute for a static file. So if we wanted to change that, All we would have to do then is put the logger in front of the static file handler. I don't know if that's something that we wanna do because then our log would just be flooded with all of the static assets that we would then make a request for. So I'm going to put it back after the static file handler and the body parser. But that's it, that is middleware. It's a function that works with a request and the response if needed. And then it's response will for either stopping the processing of a request or passing the request on to the next middleware in the pipeline.