Transcript
  • Node.js
  • Tutorial

HTTP Middleware With Connect

In a standard Node.js HTTP server, what we do is provide a callback function that takes a request and response as parameters. And so each time a client, or a browser, connects to our server, this function here gets called. And normally, what we do is return a text response back to the browser.

So in this case, I'm writing hello world as the text body back to the browser. And this server is listening on port 3000. And we just print out this nice message to let the user know. Now, to make this a little bit more clear, let's take this function and move it out into its own variable. I'm going to write this in a slightly different form.

So I'm going to create a variable called app and then take this and just move it inside. And now I'm going to call the create server method and pass the app variable instead. So it's just a slightly different form of what we had previously.

What if we want to extend our application, our HTTP server, with additional functionality? In order to do that, we'd have to write more code inside of this function. We have no way, currently, to allow another author, for example someone who writes a package, to plug in functionality that we can use in our HTTP server.

There's a technique that has evolved in a number of different frameworks called middleware. And the idea here is to be able to plug together a bunch of functions, or classes, that each can take its turn at doing something with the request. And the most famous of the middleware frameworks for Node.js is called Connect.

Connect is used in a couple of different higher level web frameworks, including meteor and express. To install connect, you can type in NPM install connect inside of your project folder. Or, if you're using a package.json file, you can just add it to the package.json file, like we've done here in the dependency section. So for this tutorial, I'm using connect version 3.4.0.

Now while we're here in the console, I'd like to install one more MPM package that's going to allow us to debug Node.js programs on the server. It's called node inspector, and we can install it by typing in NPM install and using the -g flag to install it globally. And we'll install the node -inspector package.

Now, depending upon your system privileges, you might get an error here, in which case you need to run this command with pseudo. I can use pseudo and then bang bang to run the previous command with pseudo privileges.

That might take a couple of seconds to complete. But once it does, we can hop back to our code editor. I'll start off by getting a reference to the connect module and storing it in the connect variable. So I'll require connect, just like so. Then I need to create a connect application. I'm going to call that app. And I can create it by calling the connect function.

So what gets returned when we require the connect module is a function. And we can call that function like we're doing here. Let's comment out our old app variable for now. And right underneath this new app declaration, I'm going to put a debugger statement. That'll pause the program in the debugger on the server.

Now, to start out this program, I'm going to say node debug and then pass the name of the file that I want to run, which is server.js. So I'm going to use this command instead of NPM start. That's going to start this up in the debugger and automatically drop us into a chrome debugger, which is debugging our server. Pretty cool.

In the upper right, if I press the Play button, it'll take us to our next the debugger statement. And so the app variable should be declared at this point. The reason I've done this is I want to show you what the app variable looks like. So in scope variables, open up the local tree and take a look at the app property.

Notice that app is actually a function. And this function takes a couple of parameters. The first is a request. And this is the same request object that we were getting previously in our regular Node.js HTTP server. And the same goes for the response object.

But it's added one additional parameter, which is called next. I'll show you how this works in a second. But the key thing to remember here is that our app is actually a function. That means we can pass it right in to create server, like we were doing with our previous app function.

It looks pretty much the same, except it has that extra parameter called next. Now the primary purpose of connect is to allow us to chain together a bunch of middleware functions. So when you hear the word middleware, you should be thinking about a function, just like this one. And what we want to be able to do is to create an array of these functions. So, for example, we might have F1, F2, and so on, as many as we want.

And for each request, each time a browser connects to the server, we want to pass that request and response through all of these functions. So this is a nice way to be able to bring in a bunch of different functionality, potentially from different authors.

To add a middleware function, I can call the use method of our new application and just pass a function. I'm going to name this so that we can see it. I'm going to call it middleware1. And it'll take the requests, the response, and then this extra parameter called next.

And we can add as many of these middleware functions as we want. So I'll add another one called middleware2. And I'll add even a third one, which I'll just call the app itself. Let's take the code that we had previously written inside of our old app function and move that up into the new app function. And we can get rid of this commented out code.

Now there's literally hundreds of different use cases for middleware functions. But to give you a couple of examples, a middleware function could serve as a logger to log all requests to the console. Or you might be using a router that plugs itself in by using middleware.

In our examples here, let's just create a couple of silly middleware functions, which will just log the name of the middleware function to the console. So I'm going to say console.log, middleware1, and then, down below, I'll say console.log, middleware2.

Now the request and response parameters are the same ones that we usually get with our HTTP server. But this third parameter, the next parameter, is a function. And what it allows us to do is to call the next function in the chain. That way, we have complete control over when the next function gets called, even if we're using some kind of asynchronous function.

For example, in the first middleware, let's set a timeout that we'll complete in two seconds. So, in two seconds from now, that's when we want to call the next function in this chain. You could imagine that this set timeout is simulating some kind of asynchronous function, like reading a file from disk, or even reading an HTTP post form off the wire.

And I'll move this middleware1 into that timeout, so we have to wait a couple seconds before we see it. In middleware2, I'll just call next right away. And that'll drop me into the final piece of middleware, which is going to write the header and their response, hello world, to the response.

Looks like I have one mistake here. I just need to change this to RES for response because it's their response that we're writing the header and the body to, not there request. To try this out, I created two new consoles. And in the top one, I'm going to start my server using NPM start. That's going to run nodeserver.js.

And down below, I'm going to use the curl client so that we can see the response in the console. So I'll say curl, http://localhost::3000. That'll send an HTTP request up to my server.

Now, when I press Enter, notice that there's going to be a little bit of delay before anything happens. So press Enter and notice that nothing happens quite yet. But then, two seconds later, we see middleware1 printed to the console. And then, right away, middleware2 printed to the console.

So we start off in this middleware function and then move to the next middleware function. And then, finally, we write back the Hello World string as the body of the response. So this gives us a really nice way of packaging up functionality that can be reused across multiple different applications by creating a simple API of request, response, and the next function.

Now many popular packages also have corresponding connect middleware packages. For example, we looked at how to upload a form using busboy. And busboy has a corresponding connect middleware package called connect-busboy. In order to use it, you can just install the connect-busboy package. And if you scroll down, you can see an example of how it works.

So notice, instead of calling all the busboy functions manually, like what we did in a previous tutorial, we just pass it into the use method as a function call. And it becomes a piece of middleware. And what that middleware will do looks kind of like what this example here is showing you. But it's much simpler just to do this.

And if you scroll down a little bit more, you can see that we can even pass an options object into the busboy function to change the behavior of how busboy works with HTTP form posts. So this is just one example of a package that has been created to work hand in hand with connect act as a middleware function.