Book Image

Building Scalable Apps with Redis and Node.js

By : Joshua Johanan
Book Image

Building Scalable Apps with Redis and Node.js

By: Joshua Johanan

Overview of this book

Table of Contents (17 chapters)
Building Scalable Apps with Redis and Node.js
Credits
About the Author
About the Reviewers
www.PacktPub.com
Preface
Index

Using middleware in Express


One of the greatest things about Express is that it is easily extended, which is achieved by using middleware. Every request makes its way through the middleware layer. In fact, our routes are just the final middleware function. We return a response, which means that at this point, the request is done and no more middleware functions are executed.

Creating our own middleware

To create our own middleware, all we have to do is create a function that accepts the parameters req, res, and next. Inside of this function, we should have access to request and response, and the ability to tell Express to move on to the next piece of middleware.

To add a middleware layer to Express, we use app.use(), which allows us to take a middleware function. So, let's create our own 404 middleware function! Create a directory named middleware and a file called errorhandlers.js, before putting the following code in the file:

exports.notFound = function notFound(req, res, next){
  res.send(404, 'You seem lost. You must have taken a wrong turn back there.');
};

Now, update app.js and put this code right after the initial block of variable declarations, but before we define routes. This should be line 4. This is shown as follows:

var errorHandlers = require('./middleware/errorhandlers');
app.use(errorHandlers.notFound);

We didn't call the next function here because no other middleware matches this route at this point. We can safely send the response by letting the user know that they are lost. Let's fire this up and check it out. We will type a nonexistent route, http://localhost:3000/notfound, into our browser. So far, everything looks good. Now, let's try a known route, http://localhost:3000/. Uh-oh! Everything is responding as not found. What did we do wrong?

Note

If you can keep a secret, I'll let you know that I knew this wasn't going to work. Middleware runs in the order they are added in Express. We only added one middleware, which means it runs on every request. This middleware also returns a response and does not run the next() function.

Since I trust that you can keep secrets, I will tell you another one. You can actually call next() after sending a response. You will, in all probability, create an error because another middleware/route will try to send a response that you cannot. To sum this up, if you send a response from middleware, do not call next(). The default action of our routes is to just return a response and not run next.

How do we fix this issue? Pretty easily, in fact. We will add in another piece of middleware before our notFound handler. It will be the app.router middleware, since this is the function that maps all the routes. If a request matches a defined route, then it will execute the function we have defined for that route and return a response. If it doesn't match anything, the next middleware will be called. You should then open up app.js and move app.use(errorHandlers.notFound) under the routes we have created:

app.get('/', routes.index);
app.get('/login', routes.login);
app.post('/login', routes.loginProcess);
app.get('/chat', routes.chat);
app.use(errorHandlers.notFound);

This will check the request to see if it matches a route. If not, run the next middleware, which is the notFound function. Our app is now running how we expect it to. Try loading all the routes, and then run the routes that we know will create a 404 error to test our new middleware.

Let's add a few more pieces of middleware. First off, let's add a logging middleware. Create log.js under the middleware folder and put the following code in:

exports.logger = function logger(req, res, next){
  console.log(req.url);
  next();
};

Then, modify app.js, and add this as the first middleware:

var errorHandlers = require('./middleware/errorhandlers');
var log = require('./middleware/log');
app.use(log.logger);

app.get('/', routes.index);
app.get('/login', routes.login);
app.post('/login', routes.loginProcess);
app.get('/chat', routes.chat);
app.use(errorHandlers.notFound);

Each request will log the URL to the console. We did not modify the request in any way, so it continues down the middleware path to the route or the notFound handler. We can change this to write to a file or a database, but we are keeping things simple for now (is this foreshadowing? It probably is!). Also, we do not need to modify the request or response in any way.

Note

Although we built our own logging middleware for demonstration purposes, Express comes with its own logging middleware, which is express.logger().

Next, we will add the ability to serve static assets. Most sites use CSS and JavaScript, and we don't want to send these files through the view rendering engine (a concept we will get to later in this chapter). Express comes with middleware that can serve static files. So, create a folder in our project called static, and then create a file called static.txt, putting whatever you want in the file. Now, add the static middleware right above the router, as follows:

app.use(log.logger);
app.use(express.static(__dirname + '/static'));

Anything you put in this folder will be served. Browse to http://localhost:3000/static.txt, and you should see whatever you added to the file.

Finally, let's add an error handler. This middleware has a different function signature. It takes the four parameter functions of err, req, res, and next. This conforms to the node practice of passing the error as the first parameter. We will add the error handler inside middleware/errorhandlers.js. To conclude, add the following code to the file:

exports.error = function error(err, req, res, next){
  console.log(err);
  res.send(500, 'Something broke. What did you do?');
};

Here is our final middleware stack in app.js:

app.use(log.logger);
app.use(express.static(__dirname + '/static'));
app.get('/', routes.index);
app.get('/login', routes.login);
app.post('/login', routes.loginProcess);
app.get('/chat', routes.chat);

app.use(errorHandlers.error);
app.use(errorHandlers.notFound);

At this point, we cannot test the error handler. Every request we create does not throw an error, so let's create a route that actually does. Add this to the end of our route definitions:

app.get('/error', function(req, res, next){
  next(new Error('A contrived error'));
});

Remember that a route is just another piece of middleware. We create an error here to pass to the error handler. The only middleware that will match the function signature with an error is our new error handler. If we go to /error, we will see that our logging middleware writes to the console, followed by the error middleware writing our error to the console. It then concludes with Something broke. What did you do?. Our little website is now not blowing up on errors and logging everything.

Right now, we are serving HTTP responses based on routes and wired-up logging, 404 not found error page, and error handling middleware. This is all in roughly 20 lines of code in app.js. Not too bad!