Home Web Development Building Scalable Apps with Redis and Node.js

Building Scalable Apps with Redis and Node.js

By Joshua Johanan
books-svg-icon Book
eBook $28.99 $19.99
Print $48.99
Subscription $15.99 $10 p/m for three months
$10 p/m for first 3 months. $15.99 p/m after that. Cancel Anytime!
What do you get with a Packt Subscription?
This book & 7000+ ebooks & video courses on 1000+ technologies
60+ curated reading lists for various learning paths
50+ new titles added every month on new and emerging tech
Early Access to eBooks as they are being written
Personalised content suggestions
Customised display settings for better reading experience
50+ new titles added every month on new and emerging tech
Playlists, Notes and Bookmarks to easily manage your learning
Mobile App with offline access
What do you get with a Packt Subscription?
This book & 6500+ ebooks & video courses on 1000+ technologies
60+ curated reading lists for various learning paths
50+ new titles added every month on new and emerging tech
Early Access to eBooks as they are being written
Personalised content suggestions
Customised display settings for better reading experience
50+ new titles added every month on new and emerging tech
Playlists, Notes and Bookmarks to easily manage your learning
Mobile App with offline access
What do you get with eBook + Subscription?
Download this book in EPUB and PDF formats, plus a monthly download credit
This book & 6500+ ebooks & video courses on 1000+ technologies
60+ curated reading lists for various learning paths
50+ new titles added every month on new and emerging tech
Early Access to eBooks as they are being written
Personalised content suggestions
Customised display settings for better reading experience
50+ new titles added every month on new and emerging tech
Playlists, Notes and Bookmarks to easily manage your learning
Mobile App with offline access
What do you get with a Packt Subscription?
This book & 6500+ ebooks & video courses on 1000+ technologies
60+ curated reading lists for various learning paths
50+ new titles added every month on new and emerging tech
Early Access to eBooks as they are being written
Personalised content suggestions
Customised display settings for better reading experience
50+ new titles added every month on new and emerging tech
Playlists, Notes and Bookmarks to easily manage your learning
Mobile App with offline access
What do you get with eBook?
Download this book in EPUB and PDF formats
Access this title in our online reader
DRM FREE - Read whenever, wherever and however you want
Online reader with customised display settings for better reading experience
What do you get with video?
Download this video in MP4 format
Access this title in our online reader
DRM FREE - Watch whenever, wherever and however you want
Online reader with customised display settings for better learning experience
What do you get with video?
Stream this video
Access this title in our online reader
DRM FREE - Watch whenever, wherever and however you want
Online reader with customised display settings for better learning experience
What do you get with Audiobook?
Download a zip folder consisting of audio files (in MP3 Format) along with supplementary PDF
What do you get with Exam Trainer?
Flashcards, Mock exams, Exam Tips, Practice Questions
Access these resources with our interactive certification platform
Mobile compatible-Practice whenever, wherever, however you want
BUY NOW $10 p/m for first 3 months. $15.99 p/m after that. Cancel Anytime!
eBook $28.99 $19.99
Print $48.99
Subscription $15.99 $10 p/m for three months
What do you get with a Packt Subscription?
This book & 7000+ ebooks & video courses on 1000+ technologies
60+ curated reading lists for various learning paths
50+ new titles added every month on new and emerging tech
Early Access to eBooks as they are being written
Personalised content suggestions
Customised display settings for better reading experience
50+ new titles added every month on new and emerging tech
Playlists, Notes and Bookmarks to easily manage your learning
Mobile App with offline access
What do you get with a Packt Subscription?
This book & 6500+ ebooks & video courses on 1000+ technologies
60+ curated reading lists for various learning paths
50+ new titles added every month on new and emerging tech
Early Access to eBooks as they are being written
Personalised content suggestions
Customised display settings for better reading experience
50+ new titles added every month on new and emerging tech
Playlists, Notes and Bookmarks to easily manage your learning
Mobile App with offline access
What do you get with eBook + Subscription?
Download this book in EPUB and PDF formats, plus a monthly download credit
This book & 6500+ ebooks & video courses on 1000+ technologies
60+ curated reading lists for various learning paths
50+ new titles added every month on new and emerging tech
Early Access to eBooks as they are being written
Personalised content suggestions
Customised display settings for better reading experience
50+ new titles added every month on new and emerging tech
Playlists, Notes and Bookmarks to easily manage your learning
Mobile App with offline access
What do you get with a Packt Subscription?
This book & 6500+ ebooks & video courses on 1000+ technologies
60+ curated reading lists for various learning paths
50+ new titles added every month on new and emerging tech
Early Access to eBooks as they are being written
Personalised content suggestions
Customised display settings for better reading experience
50+ new titles added every month on new and emerging tech
Playlists, Notes and Bookmarks to easily manage your learning
Mobile App with offline access
What do you get with eBook?
Download this book in EPUB and PDF formats
Access this title in our online reader
DRM FREE - Read whenever, wherever and however you want
Online reader with customised display settings for better reading experience
What do you get with video?
Download this video in MP4 format
Access this title in our online reader
DRM FREE - Watch whenever, wherever and however you want
Online reader with customised display settings for better learning experience
What do you get with video?
Stream this video
Access this title in our online reader
DRM FREE - Watch whenever, wherever and however you want
Online reader with customised display settings for better learning experience
What do you get with Audiobook?
Download a zip folder consisting of audio files (in MP3 Format) along with supplementary PDF
What do you get with Exam Trainer?
Flashcards, Mock exams, Exam Tips, Practice Questions
Access these resources with our interactive certification platform
Mobile compatible-Practice whenever, wherever, however you want
  1. Free Chapter
    Backend Development with Express
About this book
Publication date:
September 2014
Publisher
Packt
Pages
316
ISBN
9781783984480

 

Chapter 1. Backend Development with Express

This chapter will show how to set up Express for our application. Express is the application framework for Node.js. If Node.js is the foundation, Express will be the framework of our house. Express provides us with routing, sessions, form processing, and so on. Entire books have been written on it, so we will just touch on what we require. If you have never used Express before in a Node project, don't worry, you will get a good feel of it by the end of this chapter. If you were lucky enough to use Express before, then hopefully I can show you something you don't already know.

The topics that we will cover in this chapter are as follows:

  • Serving and responding to HTTP requests

  • Routing

  • Middleware

  • Templating

  • Creating sessions

  • Parsing form data

  • Integrating everything together

 

Node.js and Node Package Manager


Node.js is a platform that uses Google Chrome's JavaScript engine. This means that we can create server applications using JavaScript. We do not need an in-depth knowledge of everything on Node.js, we only need it to be installed. Node.js binaries are packaged for all the major platforms at http://nodejs.org/download/.

We will use npm (also known as Node Package Manager) to install all the libraries that we are going to use. Most languages/platforms/frameworks move to a managed packaging system. This will be familiar if you have ever used Python's pip with virtualenv, Debian's apt-get, or Microsoft's NuGet, to name a few. Managed packaging allows developers to explicitly define what dependencies your application requires. We will install almost all the Node packages locally. This allows us to install and test new dependencies separately without creating conflicts on the system. By default, npm installs the packages to a folder named node_modules in the root of our project. When a package is used in a file, Node will check this directory for the package. The code that accompanies each chapter will not have the node_modules directory included, but it will have the file that defines what is needed. When we install something globally, we will use the –g flag for npm. This installs the packages to a central node_modules directory so that every Node project can use the package. If you have built a Node.js project before, this should not be new. I will write out the commands for anyone that has not used npm.

Node packages are very notorious for having fast release cycles, which means that by the time you have read this, some of the packages you will use might be of a different version. There are a few ways to combat this. We can use npm shrinkwrap, which will explicitly define each package and all its dependencies. Another way, is to include all the dependencies into source control, so we are completely sure what package and version is installed. I will list out the versions I have used, so that you can install the same versions. As an example, Express has already gone through a major version upgrade (from Version 3.x to 4.x), which was not completely backwards compatible.

The following are the versions that we will use:

  • body-parser: 1.4.3

  • connect: 3.0.2

  • cookie-parser: 1.3.2

  • csurf: 1.3.0

  • ejs: 0.8.5

  • express: 4.6.1

  • express-partials: 0.2.0

  • express-session: 1.6.5

  • redis: 0.10.1

  • connect-redis: 1.4.7

  • connect-flash: 0.1.1

To ensure that these are the versions installed, you can create a file in the root of the project named package.json. The file should look similar to the following code:

{
  "name": "NodeChat",
  "version": "0.0.0",
  "main": "app.js",
  "scripts": {
    "stop": "echo not implemented",
    "start": "node ./app.js"
  },
  "dependencies": {
    "ejs": "0.8.5",
    "express": "4.6.1",
    "express-partials": "0.2.0",
    "redis": "0.10.1",
    "connect-redis": "1.4.7",
    "connect-flash": "0.1.1"
  }
}

Note

Downloading the example code

You can download the example code files for all Packt books you have purchased from your account at http://www.packtpub.com. If you purchased this book elsewhere, you can visit http://www.packtpub.com/support and register to have the files e-mailed directly to you.

The package.json file defines attributes about our project using JSON. The key property that we are concerned with here is dependencies. We define each one in the dependency object along with the version. Note that each dependency is pinned to a specific version. We can add a line to install Express 4 or above as follows:

"express": ">=4.0.0"

The issue with this is that we will not know what version we can get. Always prefer explicit dependencies to implicit ones.

We can then install the required packages by running the following command:

npm install
 

Using Express in Node


With everything installed, we can create our project directory. It does not matter where you create it (I created a projects directory under my user folder), but all the files we created will be referenced from the root of this folder. To do this, create an app.js file. Note that the aim here is to build our very own, simple Express application. To get started, open app.js and add the following code to it:

var express = require('express');
var app = express();

app.get('*',function(req, res){
  res.send('Express Response');
});

app.listen(3000);
console.log("App server running on port 3000");

To start the Express server, we will run the following commands:

node app.js
npm start

The first tells Node to run using this file. The other uses the value from our package.json file under scripts.start. We can then open our browser to http://localhost:3000. Any path we put into our browser will return the Express response, which you can see in the following screenshot:

In order to receive this response, the app.get function uses a regular expression to match any GET request, no matter the path. The req and res objects are the request and response objects. The res.send Express function adds all the basic headers for us, before sending the response back.

The Express application can respond to all the common HTTP verbs such as GET, POST, PUT, and DELETE. As you have guessed by now, we will use these verbs in order to use the method call passing in the path, as well as the callback.

Although this works well, it is in fact not very useful. We will need to define routes, which are specific HTTP verbs matching with the URL. We need to add all of our routes and connect them to functions. Our application is very simple from a routing standpoint. We will have an index page, a login page that has a form, and a chat page. Although we can just as easily add all of these functions to app.js, app.js, it will then become very difficult to maintain quickly. Instead, we will create a routes folder and then add an index.js file. Our folder structure should then look similar to the one in the following screenshot:

Inside the index.js file in routes, we will create all our routes with the following code:

module.exports.index = index;
module.exports.login = login;
module.exports.loginProcess = loginProcess;
module.exports.chat = chat;
function index(req, res){
  res.send('Index');
};
function login(req, res){
  res.send('Login');
};
function loginProcess(req, res){
  res.redirect('/');
};
function chat(req, res){
  res.send('Chat');
};

Following this, app.js should only use our routes. Our app.js file should look similar to the following code:

var express = require('express');
var app = express();
var routes = require('./routes');

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

app.listen(3000);
console.log("App server running on port 3000");

Our routes are now nice and clean, which also lends itself to aliases. If we want people to be able to see the login form from /login and /account/login, we just have to add the following line:

app.get('/account/login', routes.login);

We can also group related functions together in the same file. Our example is simple, but it will be easy to have as many files mapping to specific routes. Each file would just have the functions that relate to its primary duty, which means you could then wire them to any route you wanted.

You may already be asking, "What happens when a route is not defined?". This is, in fact, a great question, and it was something I was just about to discuss. By default, Express will respond with Cannot GET /notfoundURL. Usually, it is a bad idea to keep this as our 404 response. What we actually want is to tell the user that they have made a wrong turn somewhere. This naturally leads us to our next major idea—using middleware in Express.

 

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!

 

Adding templates to the mix


We now have a working site that has routes and middleware, but we are missing one import thing—HTML. Our requests come back with a Content-Type of text/html. Although res.send() adds this header for us, it is just text. It is missing a document type, head, and body, which is where another feature of Express comes in: templates.

We will use Embedded JavaScript (EJS) as our view template engine. I must notify you here that many tutorials, and even the Express command-line utility, default the view engine to Jade. I have used a few other template systems, which are not limited to PHP (which by itself is a kind of template system), Python's Django template, and Microsoft's Razor engine in MVC, to name a few. Luckily, EJS feels closer to all of these. I also feel that I don't need a parser to build my HTML. I want to write HTML and then add my variables where I need them. These are just a few reasons why we will continue using EJS.

Note

If you like to use Jade, a major part of the application configuration will be the same. You will just have to build your own templates.

On a side note, I will reiterate that we don't want to install Express globally. You will see answers on Stack Overflow asking if the questioner has installed it locally or globally. Everything we need to do can be done locally. There is nothing that Express installed globally can do differently, where even the command-line utility works in this way:

 ./node_modules/.bin/express --help. 

We need to enable Express's view engine using the Express command app.set(). Before our middleware stack (app.use()), we should add the following:

app.set('view engine', 'ejs');
app.use(log.logger);
app.use(express.static(__dirname + '/static'));

To see a list of possible settings, visit http://expressjs.com/api.html#app-settings. The two settings we are concerned with now are view engine and views. The views setting is used to set the view directory. By default, it is set to the views directory under the current directory. We will use the default value, so now we should create a directory named views. Our current folder structure should then look as shown in the following screenshot:

Under views, create a file named index.ejs and add the following HTML code to it:

<!DOCTYPE html>
<html>
<head>
  <title>Index</title>
</head>
<body>
Index
</body>
</html>

Following this, open up index.js from routes, and modify the index function as follows:

exports.index = function index(req, res){
  res.render('index');
};

Restart node and load the root. Alright, you now have a valid HTML5 document. Express knows to look in the views directory (views setting) for index.ejs (view engine setting). This is working great, but it is not very dynamic. We want to be able to change the response. We will need to modify the index.ejs file and change the head element, which you can do in the following way:

<head>
  <title><%= title %></title>
</head>

The main features we will use from EJS are <%= %>, which will output the value of the variable into the HTML document and <% %>, which allows us to execute JavaScript. Any if or for loop will also be used, in addition to any plain JavaScript.

Now, we have to pass a title variable to the view, so you can add the following code to the index function:

res.render('index', {title: 'Index'});

Browse to http://localhost:3000/, and you should see that the title of the tab is now Index. You can view the source of this page and see that it is rendered in HTML. This is great but not very maintainable. Essentially, this is no different than running a completely static website. So, we next need to add a layout.

Layouts

Express 3 lets each template-rendering library implement its own layout, which means it does not force any set way on the layout. This is different from Express 2 as it's defaulted to using a file named layout in the views directory. You will have to be careful that you do not get information about Express 2 when searching on the Internet. Our package of EJS does not have layout support, but we have already installed a package that does, which is express-partials.

We have to do a little setup to let Express know that we are using partials. The first step is to get a reference to our new library, which you can do by:

//all the other variables declarations at the top of app.js
var partials = require('express-partials');

Next, we add it as middleware. The only caveat here is that it has to be before our routes. This makes sense because we will have to render our complete page before the server sends a response.

app.use(partials());
//all other middleware functions

Finally, we can add a view option for the default layout.

//after variable declarations 
app.set('view options', {defaultLayout: 'layout'});
//but before the middleware

Setting the default layout is not required, but I will recommend it. Otherwise, we will have to define the layout for every view. We can override the default layout (or set it in the first place) using the render function:

res.render('index', {layout: 'layout', title: 'Index'});

Now that we have our layout setup, we can create it and update all our routes to use the layout. Create a file called layout.ejs in the views folder. Add the following code to it:

<!DOCTYPE html>
<html>
<head>
  <title><%= title %></title>
</head>
<body>
<%- body %>
</body>
</html>

Now, we can create two more files called login.ejs and chat.ejs. Finally, we have to update each res.render function to have the name of the file to render and a title.

exports.login = function chat(req, res){
  res.render('login', {title: 'Login'});
};
exports.chat = function chat(req, res){
  res.render('chat', {title: 'Chat'});
};

Now, all the routes will return valid HTML.

Let's go ahead and make our layout pretty. We will add the HTML code that our app will use, and the CSS framework we will be using is Bootstrap. It's great to get projects off the ground as we can start to be concerned with the code as opposed to the design. We will use a theme named Cosmo from http://bootswatch.com/ so that our site does not look like a default Bootstrap site. We can get the CSS framework from http://bootswatch.com/cosmo/. Once you have downloaded the CSS file, create a new directory named css under the static directory. The path from root should be static/css/cosmo.min.css.

Note

You can recognize Bootstrap sites as they have the same font, buttons, color, and CSS elements. If you are going to build something, you will want to differentiate it from other sites. You want users to associate with your site with just a look.

After carrying out all of these tasks, here is what our layout should look like:

<!DOCTYPE html>
<html>
<head>
    <title><%= title %></title>
    <link rel="stylesheet" href="css/cosmo.min.css">
</head>
<body>
<div class="container">
  <div class="row">
      <div class="col-sm-4"><h1>PacktChat</h1></div>
  </div>
  <div class="row">
  <%- body %>
  </div>
</div>
</body>
</html>

Refer to the following screenshot to see the index page in the new layout:

Note

I recommend using Bootstrap when you start your projects. You can easily get a good-looking site without spending any time in design. Another great option is to use Zurb's Foundation. Using either of these frameworks is a good idea if you do not have much design experience. You can always go back and change the look.

 

Using sessions in Express


Express uses the same methods, cookies, as most other web frameworks to track sessions. A cookie will have the session ID so that Express can look it up on each request.

Using cookies in Express

The latest version of Express has taken out much of the middleware that was previously included in Express. This is important when migrating from Express 3 to 4. We will use the cookie-parser package, which should already be installed. We will now add cookie parsing to our app. It is a middleware, so we will put it with all the other middleware. Remember that the middleware is processed in order, so that we can add each before the middleware that will actually use it, which is our routes. We will do this by adding a variable declaration and another function in the middleware stack.

//with all the other requires at the top of the file
var cookieParser = require('cookie-parser');
//in the middleware stack
app.use(express.static(__dirname + '/static'));
app.use(cookieParser());

We will repeat this pattern many times over this chapter and the next. Anytime there is require, it will be at the top of the file. The code that comes along with the book will also declare all the variables together. There will be one var, and each line will have require with a comma instead of a semicolon. Whether there is one var declaration or many, the code will still run. Further down in the file, we will use our new variable. I will try to include landmarks in the code to help, but we will add a lot of code in many places at times. Refer to the code that is supplied with the book.

The cookie parser gives us access to req.cookies. This will be an object that we can read the values out of. At this point, if we run our application, nothing will be different. We have not set cookies, nor are we trying to access them. Let's change that.

First, let's set up our views to show us the cookies in the request. In index.ejs under views, let's add the section. The file should look similar to the following code:

Index
<div>Cookie passed: <%= cookie %></div>

We now have to pass the cookie to our view. You should edit routes/index.js and add this to our view function. Note that we do not need to specify a layout because we have a default layout set with app.set('view options', {defaultLayout: 'layout'}).

exports.index = function index(req, res){
  res.render('index', {title: 'Index', cookie: JSON.stringify(req.cookies)});
};

Let's check it out in the browser. We should see that we have a blank cookie object in our request. It is time to create our own cookie. Open the JavaScript console in the browser (I am using Google Chrome) and type this in:

document.cookie="test=Test Cookie"

Refresh the page and see that it has our cookie in it. We can see that the request cookie object is just a simple JavaScript object, as seen in the following screenshot:

Next, we will set a cookie from the server. Express has a simple way to do this: in our index.js file under routes, let's add a cookie in the index function:

exports.index = function index(req, res){
  res.cookie('IndexCookie', 'This was set from Index');
  
res.render('index', {title: 'Index', cookie: JSON.stringify(req.cookies)});
};

Restart the node and load the page twice. The first restart will set the cookie, and the second restart will read it into our response. From the following screenshot, you should now see both cookies on our page:

You can also easily get rid of cookies by using clearCookie off the response object:

res.clearCookie('IndexCookie');

If you want to do it from the browser side, you can usually get a list of current cookies. In Chrome, this is in the developer tools. Click on the menu button in the upper right and navigate to Tools | Developer Tools. Then click on Resources | Cookies. You can then right-click on a specific cookie in the list and delete the cookie or select Clear All to delete all the cookies, as shown in the following screenshot:

By now, you should be feeling good about adding and removing cookies to requests and responses, so now let's see how to tie these cookies to a session.

Note

Hopefully, I have demonstrated how easily any attacker can forge cookies. Do not store sensitive information in your cookie. For example, storing a Boolean variable whether or not the user is logged in is a bad idea. We will shortly cover how to do all of this securely.

Adding sessions

Sessions allow us to store data about requests that are tied together with a cookie. HTTP is stateless, but cookies that map back to a session allow us to know that this is the same browser making multiple requests. You should be able to guess by now that Express comes with a great session middleware.

The first thing to know is that we need to store our sessions somewhere. For now, we will use a memory store.

You should add this to our variable declarations at the top of app.js:

var session = require('express-session');

Next, add the middleware. You should remember to add it under our cookieParser middleware, as follows:

app.use(cookieParser());
app.use(session());

The express session uses cookies, so the cookie object needs to be present before it can use the session.

Now, we can use our session. We will update our index page to show what is stored in our session. Edit index.ejs under views to display a session:

Index
<div>Cookie passed: <%= cookie %></div>
<div>Session: <%= session %></div>

The session middleware adds a new object to our request, which is req.session. Let's pass this to the view from index.js under middleware:

function index(req, res){
  res.cookie('IndexCookie', 'This was set from Index');
  res.render('index', {title: 'Index', cookie: JSON.stringify(req.cookies), session: JSON.stringify(req.session)});
};

Once you load this up, you will find that we get an error. If we check our console, as you can see from the following screenshot, we need to add a secret option for sessions:

We can now do this by revisiting our session middleware and adding a secret option:

app.use(session({secret: 'secret'}));

The secret option uses the string we pass in to create a hash of our session ID, so we can tell if someone has tried to tamper with our cookie (also known as a request forgery). We just covered ways by which users can easily delete and create any cookie that they want. If our cookie had a session ID in it, which for example could be 1234, a user could delete that cookie and create a new one with a session ID of 1235. As far as the server knows, the next request comes from the user who has session 1235. A hashed session ID makes this much more difficult. If the user does not know the secret (don't actually use secret or 123456, use something such as http://randomkeygen.com/ or http://www.guidgenerator.com/ to get a unique secure secret), then their ability to create a valid token is reduced. This is a very contrived example, but it should illustrate why we need this.

Reload the node and refresh twice. We can now see our session and the cookie that was created in the following screenshot:

We can test our security by deleting our connect.sid cookie and creating a new one. On the next request, we will get a new connect.sid cookie set.

Let's build a simple page counter in the session. On each request, we will increment a counter. We can do this easily by adding a middleware function. We only need to remember to add it under the session middleware so that we have access to req.session; we will write this function inline as we are not going to keep it in our final middleware stack. Add this to the stack right under session:

app.use(function(req, res, next){
  if(req.session.pageCount)
    req.session.pageCount++;
  else
    req.session.pageCount = 1;
  next();
});

Test it by going around and loading a bunch of pages. The pageCount session variable should track each different request you make. The request could be a 404 or even an error. Our middleware runs and adds to the total before any error handling. One thing to remember is that only our index view will output pageCount. After testing this, we can remove the middleware.

One limitation to how we have set this up is that only the node instance that created the session also has access to it. If you run multiple node instances, you will need to have a different session store from memory.

Redis as a session store

Redis is an in-memory key-value store. We will use Redis to hold the session ID as a key and the session data as a value. It is important to note that we will not get into what Redis is and how to install it here as Chapter 5, Adopting Redis for Application Data, will cover the topic. Also, we will not cover the security issues with Redis now as we just want to get it working for our sessions. However, we will cover how to add it as an Express session store.

We will use the two packages redis and connect-redis. To use a Redis store, we assume that we are running Redis locally and that Redis' version is above 2.0.0 (the latest version, as of writing this book, is 2.8.6, so this isn't a huge hurdle). First, let's change our reference to the memory store so that our variable session will point to a connect-redis instance. Change these variable declarations in app.js:

var session = require('express-session');
var RedisStore = require('connect-redis')(session);

Connect-redis extends the session. We can now set up our middleware. Change our session middleware to this:

app.use(session({
  secret: 'secret',
  saveUninitialized: true,
  resave: true,
  store: new RedisStore(
    {url: 'redis://localhost'})
  })
);

We use the same secret, but we will now create a new RedisStore object with an options object using the Redis server's URL. This URL can take a username, password, and port, if all of these were not the default values. At this point, we can restart our server and load up our index page. It should be working in exactly the same way as it was with an in-memory store. We also have a couple of other options. If these are not added, a warning is thrown.

Let's actually take a peek into what is happening here. We know at this point that our session is tracked with a cookie, but unfortunately, this is a signed value. We can get access to this by changing our cookieParser middleware to use the same secret as the session middleware. The following line of code is what our new cookieParser line should look like:

app.use(cookieParser('secret'));

Remember that the secret passed must match the one used for the session. This is because the session middleware creates the cookie and the cookieParser middleware reads it out. We will now have req.signedCookies. Any signed cookie will be here, so it is time to test this out. We will need to update index.ejs in the View folder and index.js in the routes folder provided in the code bundle.

The index.ejs file in the views folder looks like:

Index
<div>Cookie passed: <%= cookie %></div>
<div>Signed Cookie passed: <%= signedCookie %></div>
<div>Session: <%= session %></div>

The index.js file in the routes folder looks like:

exports.index = function index(req, res){
  res.cookie('IndexCookie', 'This was set from Index');
  res.render('index', {title: 'Index', 
    cookie: JSON.stringify(req.cookies), 
    session: JSON.stringify(req.session), 
    signedCookie: JSON.stringify(req.signedCookies)});
};

From the following screenshot, you can see that our unsigned cookies will be first and our connect.sid cookie will be second:

The browser will still get the signed cookie, as you can see in the following screenshot:

Without getting too much into Redis, we will look up our session in Redis. We can quickly install Redis on Mac OS X by running the following command:

brew install redis

We can then launch redis-cli (which we should now have if we have Redis installed. If you face issues, jump to Chapter 5, Adopting Redis for Application Data). We can now run a command against Redis. connect-redis will prepend sess: to all the session keys in Redis. To see our session, we will run GET sess:YOUR-SESSION-ID, as shown in the following command line:

$ redis-cli
127.0.0.1:6379> GET sess:0DMsXhobExvbCL3FFeYqRGWE
"{\"cookie\":{\"originalMaxAge\":null,\"expires\":null,\"httpOnly\":true,\"path\":\"/\"}}"

We can see that this returns our session object as an escaped string. You can compare this to the object that was returned from our response and see that it is the same. We have successfully moved our sessions to a data store that can be accessed from multiple servers. One of the most basic ideas of creating a scalable application is not to keep any shared state on the local instance. Previously, with the memory store for sessions, each server had its own state. Now, we can have multiple servers share the state. Here, we are using Redis, but you can use any data store to do this (which is not limited to memcache, MongoDB, Postgres, and many others). We are not going to do this here in this chapter, but we have started to prepare our app to be scalable. Another thing to note is that the Redis server is running on localhost. For a production-ready scalable application, Redis will be moved to a separate server or even several servers.

Let's clean up our views a little. You definitely do not want to send all of a user's session data to them. In index.ejs in the views folder, remove everything except for Index. In index.js in the routes folder, drop all the other attributes except for title, and remove the line that sets the cookie. This is shown as follows:

exports.index = function index(req, res){
  res.render('index', {title: 'Index'});
};
 

Processing forms


All web frameworks that are useful allow you to process forms. We have a route that we have not tested yet, which is app.post('/login', routes.loginProcess);. We have only been using and testing GET routes, so let's build a form and then do something with it.

We will create the form on the /login GET request. Before we modify our template, we will need some local styles that extend Bootstrap. Firstly, create a file that is called style.css in static/css, and add the following styles to it:

.facebook {background: #3b5998; color: #ffffff;}
.google {background: #dd4b39; color: #ffffff;}
.top-margin {margin-top: 20px;}

These are the styles that we will use to extend base Bootstrap. The styles are mainly for making sure that we color our social buttons correctly.

Please don't forget to add this to our layout:

<link rel="stylesheet" href="css/style.css">

We can now create our login form, which you can do by opening up login.ejs in login.ejs and adding the following code:

  <div class="row">
      <div class="col-sm-8 col-sm-offset-2">
          <div class="row">
              <div class="col-sm-12">
                <form method="post">
                  <div class="form-group">
                      <label for="username">Username</label>
                      <input type="text" class="form-control" id="username" placeholder="username" name="username">
                  </div>
                  <div class="form-group">
                      <label for="password">Password</label>
                      <input type="password" class="form-control" id="password" placeholder="password" name="password">
                  </div>
                  <button class="btn btn-primary btn-block">Login</button>
              </div>
            </form>
          </div>
          <div class="row top-margin">
              <div class="col-sm-6">
                  <button class="btn btn-block facebook"><i class="fa fa-facebook"></i> Facebook</button>
              </div>
              <div class="col-sm-6">
                  <button class="btn btn-block google"><i class="fa fa-google-plus"></i> Google</button>
              </div>
          </div>
      </div>
  </div>

We will not spend a lot of time on all the classes for the elements because the Bootstrap documents are a quick-and-easy read if you are looking for a quick reference of each class. We should now have a nice-looking form, as shown in the following screenshot, although it cannot currently do much:

Note

This should demonstrate why using Bootstrap can get your app running quickly. I am only using around a dozen different classes, and already, the form does not look like the basic browser style.

The form will make a POST request to /login, with the route responding with a redirect back to root. Express is not doing anything with our POST info, so let's change it.

We will first need to add a new piece of middleware (you should be seeing a trend now). Without parsing the body, our POST request will not see the data that we are sending. Add this variable declaration and middleware to the stack:

//variable declarations
var bodyParser = require('body-parser');
//middleware stack right after session
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({extended: false}));

The middleware will process POST data that has a content type of application/json and application/x-www-form-urlencoded. We do not have the middleware for multipart as it can lead to a denial-of-service (DOS) attack. The multipart middleware creates a new tmp file for each new request, which can eventually kill your server. Since we are not processing files, we do not need to include it. The reason we do not use it is because it is merely a reference to all three of the parsers, as you can see in the following code line:

app.use(bodyParser());

Let's make sure that this is working how we expect it to. Open up the index.js file from routes and modify the loginProcess function to the following:

function loginProcess(req, res){
  console.log(req.body);
  res.send(req.body.username + ' ' + req.body.password);
};

Now, each post should show us what was entered into the form, instead of redirecting and writing it to the console. We can use the form we created, or we can use curl. Curl should be available if you are using Mac OS X or Linux, although it can be installed on Windows. Run the command below and check your node console:

$ curl -X POST -H "Content-Type: application/json" -d '{"username":"josh","password":"password"}' http://localhost:3000/login

It should log { username: 'josh', password: 'password' } in the console. Let's test a URL encoded form now, so run the following curl command:

$ curl -X POST -H "Content-Type: application/x-www-form-urlencoded" -d 'username=josh&password=password' http://localhost:3000/login

It should log { username: 'josh', password: 'password' } in exactly the same way as the json request.

We have just built a form that will post back to itself and then parse out the form values.

Cross-Site Request Forgery (CSRF)

We are missing one huge security piece—the Cross-Site Request Forgery (CSRF) protection. Anyone can create a form and then tell it to process against our site. As far as Express is concerned, it is just another request, which is where CSRF comes in. It is essentially a token that is put into the form and a store on the server in the session. When the form is submitted, the server checks the token. If they do not match, the server knows the form is not from us and rejects it. It is time to implement it!

First, we add a..., you've got it, a middleware! Add the following code after your session and body parsing middleware:

//variable declarations
Var csrf = require('csurf');
//middleware stack
app.use(bodyParser.json());
app.use(bodyParser.urlencoded());
app.use(csrf());

By doing this, it will add the CSRF token to our session.

With CSRF loaded, let's try to submit our form using the POST method. As you can see in the following screenshot, we get the error Something broke. What did you do?

The CSRF middleware threw a 403 status code forbidden error. Our CSRF protection is working exactly as planned as we did not add the token to the form. This is exactly what will happen if someone with nefarious purposes tried to submit a form against our site with the token.

To fix this, let's add another piece of middleware (note that our app is 90 percent middleware and only 10 percent views). We will create a utility function file by creating a utilities.js file under the middleware directory. Inside this file, add the following:

module.exports.csrf = function csrf(req, res, next){
  res.locals.token = req.csrfToken();
  next();
};

Res.locals is a special object that is available to all templates that have access to this response. It will make all of its attributes available for the template to use. This is great for middleware as the only other way that we covered to get variables to the template was by passing them in through render(). Create a new variable at the top of app.js:

Var util = require('./middleware/utilities');

Add it as middleware directly under our CSRF middleware:

app.use(csrf());
app.use(util.csrf);

It seems a little redundant to add two straight CSRF middlewares, but they serve different functions. The first is the built-in middleware that will add the token to the session. The other is our utility that will take it from the session and make it available to the template. Maintainable middleware should have one simple single focus.

We can now edit our form to send this token, so right under the form declaration, add the following input tag:

<input type="hidden" name="_csrf" value="<%= token %>">

Note

If you get errors while following along, I can almost guarantee that it is a middleware out-of-order issue. If you try to set the CSRF token in the session before the session is added to the request, you will get an error. When errors arise, step through your middleware and make sure that they are being executed in the correct order.

Our forms should now process and our middle stack should look similar to the following code:

app.use(partials());
app.use(log.logger);
app.use(express.static(__dirname + '/static'));
app.use(cookieParser('secret'));
app.use(session({
  secret: 'secret',
  saveUninitialized: true,
  resave: true,
  store: new RedisStore(
    {url: 'redis://localhost'})
  })
);app.use(bodyParser.json());
app.use(bodyParser.urlencoded());
app.use(csrf());
app.use(util.csrf);
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);

We have now learned all the basic building blocks of using Express. We will now take them and see how to build a functioning site.

Note

CSRF should be used for any POST endpoint that you make public. Without it, anyone can make POST requests to your server, and it will process them!

 

Very simple authentication


We want everyone who goes to /chat to be authenticated. We will build a very simple, yet very insecure, authentication system. We will then gut this and put in a real authentication system later, but this exercise will show us how all the pieces we have talked about work together. The first thing is to check if a user is logged in during a request. We will use middleware for this. Open up our utilities.js from the middleware folder, and add two new functions.

The first function is to add an isAuthenticated variable for our templates, which we will use shortly. The following is our function:

module.exports.authenticated = function authenticated(req, res, next){
  res.locals.isAuthenticated = req.session.isAuthenticated;
  if (req.session.isAuthenticated) {
    res.locals.user = req.session.user;
  }
  next();
};

We will store whether or not someone is authenticated in the session. We are just adding it to the response so that a template can check the isAuthenticated value. We also add a user object if the user is logged in.

Next, we will create middleware to check to see if someone is authenticated. If not, we redirect them to the login page. The following is the function:

module.exports.requireAuthentication = function requireAuthentication(req, res, next){
  if (req.session.isAuthenticated) {
    next();
  }else {
    res.redirect('/login');
  }
};

This middleware is pretty straightforward. If you are authenticated, run the next middleware, if not, redirect to /login.

Now, we need to add these to our middleware stack. Add the authenticated function right after our CSRF and before our routes, as you can see in the following code:

app.use(util.csrf);
app.use(util.authenticated);

Our other middleware is going to go on the chat route. So far, all middleware has been added by using app.use. The app.use function will apply the middleware to every request coming in. For a lot of middleware, this is the correct place. For some though, the middleware should only be executed on certain routes. To do this, add them as the second parameter on a route definition. You can also chain multiple middleware together by using an array. Here is how our chat route looks now:

app.get('/chat', [util.requireAuthentication], routes.chat);

You can just pass the function by itself as the second parameter, but I wanted to demonstrate this syntax. Each middleware passed in the array will be executed in order.

If you load this up, you will see that you cannot get to http://localhost:3000/chat; it will always redirect you to /login.

We need to build an authentication function so that we can log users in. Open up utilities.js from middleware, and add the following function:

module.exports.auth = function auth(username, password, session){
  var isAuth = username === 'joshua' || username === 'brian';
  if (isAuth) {
    session.isAuthenticated = isAuth;
    session.user = {username: username};
  }
  return isAuth;
};

This is a very simple username check as it will only authenticate if you enter joshua or brian as the username.

Note

I will reiterate—do not use anything like this in production. We will cover how to do proper and secure local authentication in Chapter 3, Authenticating Users.

We now have to execute this inside our login post route. Open up index.js from routes and edit the loginProcess function:

//add a reference to util at the top of the file
var util = require('../middleware/utilities');
//then modify loginProcess
function loginProcess(req, res){
  var isAuth = util.auth(req.body.username, req.body.password, req.session);
  if (isAuth) {
    res.redirect('/chat');
  }else {
    res.redirect('/login');
  }
};

We pass in the username, password, and session so that auth can do its job. Depending on whether the user is authenticated or not, we will send them to /chat or redirect them back to /login. If the authentication was successful, our auth function will set isAuthenticated on the session, which means that our requireAuthentication function will not redirect you. Our little app works, well kind of. It is still a little clunky and missing some polish, and in addition to that, there is no way to log out.

This leads us right into writing a logout function, so in our utilities.js file, add the following function:

module.exports.logOut = function logOut(session){
  session.isAuthenticated = false;
  delete session.user;
};

A simple logOut function for a simple auth system. We have set isAuthenticated on the session back to false and got rid of the user in the session. Now, we have to put this in a route, so let's add that route in index.js present in the routes folder.

function logOut(req, res){
  util.logOut(req.session);
  res.redirect('/');
};

We log the user out, and then redirect to root. Finally, it needs to be added to our routes. Open up app.js.

app.get('/logout', routes.logOut);

After logging in, we can log ourselves out by going to http://localhost:3000/logout. We are still missing a little polish on our app, so let's add links to log in and out of our app.

We will do this by using partials. We will have a loggedin and loggedout partial. Create a directory in views called partials, before adding two files called user-loggedin.ejs and user-loggedout.ejs. The files should look like the following:

  • user -loggedin.ejs: Hello <%= user.username %> <a href="/logout">Logout</a>

  • user-l oggedout.ejs: <a href="/login">Login</a>

We can use the user object in our templates because of our middleware. We know the logged-in template will only be run when a user has successfully authenticated.

We will now update our layout to use these partials. Remember this functionality is provided by express-partials as of Express 3 and 4. Express 2 had this built in, so you can run into issues with code from the Internet. Here is what our layout.ejs should look like now:

<!DOCTYPE html>
<html>
<head>
    <title><%= title %></title>
    <link rel="stylesheet" href="css/cosmo.min.css">
    <link rel="stylesheet" href="css/style.css">
</head>
<body>
<div class="container">
  <div class="row">
      <div class="col-sm-4"><h1 class="pull-left">PacktChat</h1></div>
      <div class="col-sm-4 col-sm-offset-4 top-margin">
        <div class="pull-right">
      <% if (isAuthenticated) { %>
        <%- partial('partials/user-loggedin') %>
      <% } else { %>
        <%- partial('partials/user-loggedout') %>
      <% } %>
        </div>
    </div>
  </div>
  <div class="row">
  <%- body %>
  </div>
</div>
</body>
</html>

Our authentication middleware sets req.locals.isAuthenticated, which means that any request can run a Boolean check on it. It also sets the req.locals.user object for the template. The partial function will search for the path that is passed to it beginning at the views directory. The following is a screenshot of what our site should look like now:

The following screenshot highlights our other partial that uses the logged in user's username and changes the link to Logout:

The final part of our app that we will add is flash messages. Flash messaging is when we have something to tell the user from one request to the next. It is called flash because we only want to show it once. A great example of this is when someone enters a wrong username or password, which is in fact what we are going to implement. Right now, our app just takes you back to the login page without letting you know why, which is a very bad user experience.

We will use connect-flash to let the user know when something has happened. Connect-flash uses the session, so it must be after the session middleware. Let's initialize it and add it to our middleware stack:

//variable declarations
Var flash = require('connect-flash');
//middleware stack after session, but before the routes
app.use(flash());

This gives us access to req.flash to get and set flash messages. The first message we will set is our login failed message. Change the loginProcess function in index.js present in the routes folder to include our message, as follows:

function loginProcess(req, res){
  var isAuth = util.auth(req.body.username, req.body.password, req.session);
  if (isAuth) {
    res.redirect('/chat');
  }else {
    req.flash('error', 'Wrong Username or Password');
    res.redirect('/login');
  }
};

The message is now in the session. To display this, we just have to get it out. The act of getting it out will also delete it from the session, so it is time to edit our login function in index.js present in the routes folder.

function login(req, res){
  res.render('login', {title: 'Login', message: req.flash('error')});
};

The message is now passed to the template, but the template is not ready to display it. Edit login.ejs, and add this code right under the form declaration:

<form method="post">
<% if (message.length > 0) { %>
    <div class="alert alert-danger"><%= message %></div>
  <% } %>

The message will come out in an array. We do a quick check to see if there is at least one message, and then display it. Our users will now see that authentication has failed, as seen in the following screenshot:

 

Setting up a config file for our app


Currently, our app runs off of whatever is in the app.js file. If you want to change how the application runs, you will need to edit app.js. This is not very maintainable. As a simple example, what if our cookie secret changes? What happens when we only update one of the references to the cookie secret? Let's say our app grows and the secret is referenced in two other middleware. This will create a lot of pain and waste time tracking down weird bugs in the code. What we need is a config file to store all the application's settings.

The first thing is to create a file named config.js in the root of our app and add this to the file:

var config = {
  port: 3000,
  secret: 'secret',
  redisUrl: 'redis://localhost',
  routes: {
    login: '/login',
    logout: '/logout'
  }
};

module.exports = config;

We are creating an object and then returning it when this file is required. We have the port, cookie secret, Redis URL, and simple route map in our config. We now have to go find where all of these are used, and then update the code.

Route mapping

A route map allows us to use a programmable name for a specific URL. Our new config does this for login and logout. A quick check of our application shows that we are using the string '/login' in four different places. Let's all make it in one place.

We will do this by creating another piece of middleware. Any backend file can get access to this by loading the config module with require(), but the templates do not have this ability. Also, if they did, we do not want to put a boilerplate requirement at the top of every view template. This is where our new middleware comes in. Add a reference at the top of the utitlities.js file present in the middleware folder, and create a new function in the file.

var config = require('../config');
//the other functions or you could put this at the top
exports.templateRoutes = function templateRoutes(req, res, next){
  res.locals.routes = config.routes;

  next();
};

We can see that this just adds the routes object from config to res.locals. Every template will now be able to use the login and logout routes.

Now, add it to the middleware stack. Thinking about our middleware execution order, we know that it just has to go before any templates render. The first middleware that renders is our app.router, so it must go before this.

app.use(flash());
app.use(util.templateRoutes);

Our app now has a config, so we must find all the references to the various settings.

Updating our app to use the config

The first thing to look at is app.js. We will need to add this object to the scope, so add this line as the last variable declaration:

Var config = require('./config');

Now, we must find each reference to the settings in our config. Let's update the secret.

app.use(cookieParser(config.secret));
app.use(session({
  secret: config.secret,
  saveUninitialized: true,
  resave: true,
  store: new RedisStore(
   {url: config.redisUrl})
  })
  );

We fixed the issue that we had initially posed. The only change that we now need to make is in config.js, and all the secrets will be set.

We can now set up the route map's routes. We will change three of our routes in app.js.

app.get(config.routes.login, routes.login);
app.post(config.routes.login, routes.loginProcess);
app.get(config.routes.logout, routes.logOut);

The app now uses the route map to determine the actual URL to bind to. However, now we have an issue: if we update the route in the config, we have some functions that have hard coded '/login'. This will throw a 404 error. We need to track down all the references to login and logout.

The first reference, utilites.js, present in the middleware folder, is the requireAuthentication function. We will update isAuthenticated to redirect back to config.routes.login.

module.exports.requireAuthentication = function requireAuthentication(req, res, next){
  if (req.session.isAuthenticated) {
    next();
  }else {
    res.redirect(config.routes.login);
  }
};

Next is our index.js present in the routes folder; it is the loginProcess function. We will need to redirect back to login on an auth failure.

//add the config reference
var config = require('../config');
//change the function
function loginProcess(req, res){
  var isAuth = util.auth(req.body.username, req.body.password, req.session);
  if (isAuth) {
    res.redirect('/chat');
  }else {
    req.flash('error', 'Wrong Username or Password');
    res.redirect(config.routes.login);
  }
};

The last two files that we will update are the partials. Each partial file has a hard-coded URL that we will change to use config.route as follows:

  • The user-loggedin.ejs file present in the views/partials folder:Hello <%= user.username %> <a href="<%= routes.logout %>">Logout</a>

  • The user-loggedout.ejs file present in the views/partials folder:<a href="<%= routes.login %>">Login</a>

Here is where our middleware is valuable. Every template will have a route's object straight from the config. We are only using it for login and logout, but a site-wide URL should be here so that you can easily update it. For example, we can change the config.routes login and logout to '/account/login' and '/account/logout', and the app will not break.

Finally, we will update the port that the app listens on. This is in app.js:

app.listen(config.port);
 

Methods to extend an application


One thing you can do to extend an application if you are building a larger site is to build it using MVC (Model, View, Controller). We already have the view part sorted, so let's look at the other pieces. We have some sort of a controller setup with our routes directory. The next step will be to create a file for each. For example, we can have account.js, which will have the login, logout, and createAccount functions. We haven't really covered adding models to our app. We can create a directory called models, and then add a user model, for which we can find a user, update, and create a user to tie in with our account controller. Note that there are some other frameworks that build on Express in this way (sails.js is a good example). We are not using any so that you can see the insides, if you will, of the application.

Another key point from this chapter is to learn how to use middleware. In our app, we had 6 routes and 14 pieces (15, if you include the middleware that only runs on the /chat route) of middleware. There are three great reasons for this. Firstly, middleware allows you to create flexible and reusable code. We built a quick and dirty authentication module in just two pieces of middleware. If we decide to create a profile page, we just create a route like the following:

app.get('/profile', [util.requireAuthentication], routes.profile);

Then, when we build our profile template, we will know that the user object will be populated with the user's username. No more copying/pasting if statements in our routes check a session whether the user is logged in or not!

Next, there is a great ecosystem of current middleware. Everything we built in this app has a package that can do the same thing. As we covered, Express uses Connect internally, so we can use any of the Connect middleware. Currently, it has 18 bundled middleware (we have used quite a few already, such as csrf, sessions, body parsing, cookies, and static, to name a few). There are also many third-party middleware. We have used two: connect-redis and connect-flash. The Connect website has a list of the most popular third-party middleware on GitHub.

Finally, middleware functions are the perfect size for unit tests. We did not cover them here, but having a set of reusable and unit test-covered functions will make your life as a developer much better. We will cover unit testing with Nodeunit in Chapter 8, JavaScript Best Practices for Application Development.

 

Summary


Our app now looks and feels like a real application. We went from little knowledge of Express to actually using it to build a small yet fully functional site. At this point, we now know how to respond to different HTTP methods. We know how to wire up routes in a clean and extendable manner. Our app has 14 different pieces of middleware. This might seem like a lot, but our app is also doing a lot. We can use templates and partials to render our pages easily. Finally, we have a simple authentication system that uses sessions. We laid a very good base for our chat application, all in just over 40 lines, in our app.js.

In our next chapter, we will cover how to add real-time communication between the server and the browser using Socket.io.

About the Author
  • Joshua Johanan

    Joshua Johanan is the author of two books, Building Scalable Apps with Redis and Node.js and Web Developers Reference Guide. Both books deal with some of the details that will be used in this course. He is currently a web developer with over 7 years of experience writing sites and applications in many different languages. These languages include PHP, Python, JavaScript, and C#. Using many languages also means using many frameworks, including Zend Framework, Django, and .Net MVC. You can read his blog at http://ejosh.co/de/ where he posts semi-regularly.

    Browse publications by this author
Latest Reviews (7 reviews total)
Had to get a diff publishers book so I could make good use of my weekend study time. I'll get back to it eventually but got stymied at the first block of code and had to read another publisher's book on Node8
Good content, nice work, quality information
awesome content! I recommend!
Building Scalable Apps with Redis and Node.js
Unlock this book and the full library FREE for 7 days
Start now