Home Web Development Node.js 6.x Blueprints

Node.js 6.x Blueprints

By Fernando Monteiro
books-svg-icon Book
eBook $43.99 $29.99
Print $54.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 $43.99 $29.99
Print $54.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
    Building a Twitter-Like Application Using the MVC Design Pattern
About this book
Node.js is the most popular framework to create server-side applications today. Be it web, desktop, or mobile, Node.js comes to your rescue to create stunning real-time applications. Node.js 6.x Blueprints will teach you to build these types of projects in an easy-to-understand manner. The key to any Node.js project is a strong foundation on the concepts that will be a part of every project. The book will first teach you the MVC design pattern while developing a Twitter-like application using Express.js. In the next chapters, you will learn to create a website and applications such as streaming, photography, and a store locator using MongoDB, MySQL, and Firebase. Once you’re warmed up, we’ll move on to more complex projects such as a consumer feedback app, a real-time chat app, and a blog using Node.js with frameworks such as loopback.io and socket.io. Finally, we’ll explore front-end build processes, Docker, and continuous delivery. By the end of book, you will be comfortable working with Node.js applications and will know the best tools and frameworks to build highly scalable desktop and cloud applications.
Publication date:
August 2016
Publisher
Packt
Pages
376
ISBN
9781785888434

 

Chapter 1.  Building a Twitter-Like Application Using the MVC Design Pattern

The Model View Controller (MVC) design pattern was very popular in the eighties in the software industry. This pattern helped so many engineers and companies to build better software for a while and is still useful nowadays with the rise of Node.js and some Node frameworks as Express.js (more information about Express.js and its API can be found at http://expressjs.com/).

Note

As the Express.js website says, it is "Fast, unopinionated, minimalist web framework for Node.js."

Express.js is the most popular Node framework and many companies across the globe have adopted it. So in our first application, let's see how to apply the MVC pattern to create an application using only JavaScript at the backend.

In this chapter, we will cover these topics:

  • Installing the Node and Express framework

  • MVC design pattern

  • Dealing with Yeoman generator

  • How to use Express generator

  • How to deal with Express template engine

  • User authentication

  • MongoDB connection with Mongoose Schema

 

Installing Node.js


First off, we need to install the most up-to-date Node.js version. At the time of writing this book, Node.js's latest update is v6.3.0. You can go to the Node.js website at https://nodejs.org/en/ and choose your platform. For this book, we are using Mac OS X, but the examples can be followed on any platform.

To check the Node and Node Package Manager (NPM) version, open your terminal/shell and type these:

  • node -v
    
  • npm -v
    

The book uses Node version 6.3.0 and NPM version 3.10.3

Installing Yeoman

Throughout this book, we will use some tools that accelerate our development process. One of them is called Yeoman (more information can be found at http://yeoman.io/), a powerful web application generator.

Now let's install the generator. Open your terminal/shell and type the following code:

npm install -g yo
 

Installing Express generator


For our first application, let's use the official Express generator. The generator helps us in creating the initial code of our application and we can modify it to fit into our application.

Simply type the following command in your terminal or shell:

npm install -g express

Note that the -g flag means installing globally on your machine so that you can use it on any project.

Express is a powerful micro framework for Node.js; with it, it's possible to build web applications with ease.

 

Building the baseline


The project that will start now will be a fully server-side application. We will not use any interface framework such as AngularJS, Ember.js, and others; let's just concentrate on the express framework.

The purpose of this application is to use all the express resources and middleware to create an application following the MVC design pattern.

Middleware is basically functions that are activated by the routing layer of express. The name refers to when a route is activated until its return (from start to end). Middleware is in the middle as the name suggests. It is important to remember that the functions are executed in the order in which they were added.

In the code examples, we will be using  middleware including cookie-parser, body-parser, and many others.

Note

You can download the code used in this book directly from the book page present at Packt Publishing Website and you can also download this chapter and all others directly from GitHub at:

https://github.com/newaeonweb/nodejs-6-blueprints.

Each application is given the name of the relevant chapter, so let's dive into our code now.

First off, create a new folder called chapter-01 on your machine. From now on, we will call this folder the root project folder. Before we move on and execute the command to start our project, we will see a few lines about the flags that we use with the express command.

The command we use is express --ejs --css sass -git, where:

  • express is the default command used to create an application

  • --ejs means to use the embedded JavaScript template engine, instead of Jade (default)

  • --css sass means use SASS instead of plain CSS (default)

  • --git: means to add a .gitignore file to the project

As I'm using git for version control, it will be useful to use the express option to add a .gitignore file to my application. But I'll skip all git commands in the book.

To check all the options available from the express framework, you can type this into your terminal/shell:

express -h

And the framework gives us all the commands available to start a project:

Usage: express [options] [dir]
  Options:
    -h, --help          output usage information
    -V, --version       output the version number
    -e, --ejs           add ejs engine support (defaults to jade)
        --hbs           add handlebars engine support
    -H, --hogan         add hogan.js engine support
    -c, --css <engine>  add stylesheet <engine> support
                   (less|stylus|compass|sass) (defaults to plain css)
        --git           add .gitignore
    -f, --force         force on non-empty directory

Now, open your terminal/shell and type the following command:

express --ejs --css sass -git

The output in the terminal/shell will be as follows:

 create :
   create : ./package.json
   create : ./app.js
   create : ./.gitignore
   create : ./public
   create : ./public/javascripts
   create : ./public/images
   create : ./public/stylesheets
   create : ./public/stylesheets/style.sass
   create : ./routes
   create : ./routes/index.js
   create : ./routes/users.js
   create : ./views
   create : ./views/index.ejs
   create : ./views/error.ejs
   create : ./bin
   create : ./bin/www
   install dependencies:
     $ cd . && npm install
   run the app:
     $ DEBUG=chapter-01:* npm start

As you can see in the following screenshot, the generator is very flexible and only creates the minimum necessary structure to start a project:

However, we will make some changes before we proceed.

Adding changes to the package.json file

Open package.json in the root project folder and add the following highlighted lines of code:

{ 
    "name": "chapter-01", 
    "description": "Build a Twitter Like app using the MVC design pattern", 
    "license": "MIT", 
    "author": { 
        "name": "Fernando Monteiro", 
        "url": "https://github.com/newaeonweb/node-6-blueprints" 
    }, 
    "repository": { 
        "type": "git", 
        "url": "https://github.com/newaeonweb/node-6-blueprints.git" 
    }, 
    "keywords": [ 
        "MVC", 
        "Express Application", 
        "Expressjs" 
    ], 
    "version": "0.0.1", 
    "private": true, 
    "scripts": { 
        "start": "node ./bin/www" 
    }, 
    "dependencies": { 
        "body-parser": "~1.13.2", 
        "cookie-parser": "~1.3.5", 
        "debug": "~2.2.0", 
        "ejs": "~2.3.3", 
        "express": "~4.13.1", 
        "morgan": "~1.6.1", 
        "node-sass-middleware": "0.8.0", 
        "serve-favicon": "~2.3.0" 
    } 
} 

Even though it is not a high-priority alteration, it is considered a good practice to add this information to your project.

Now we are ready to run the project; let's install the necessary dependencies that are already listed in the package.json file.

On the terminal/shell, type the following command:

npm install

At the end, we are ready to go!

Running the application

To run the project and see the application in the browser, type the following command in your terminal/shell:

DEBUG=chapter-01:* npm start

The output in your terminal/shell will be as follows:

chapter-01:server Listening on port 3000 +0ms

You can run just npm start, but you won't see the previous output with the port name; later in this chapter, we will fix it.

Now, just check out http://localhost:3000. You'll see the welcome message from express.

 

Changing the application's structure


Let's make some changes to the structure of directories in our application and prepare it to follow the Model-View-Controller design pattern.

I will list the necessary steps for this refactoring:

  1. Inside the root project folder:

    • Create a new folder called server

  2. Inside the server folder:

    •  Create a new folder called config

    •  Create a new folder called routes

    • Create a new folder called views.

  3. Do not worry about the config folder at this point; we will insert its contents later.

  4. Now we need to move the error.js and index.js files from the chapter-01/views folder to the chapter-01/server/views folder.

  5. Move the index.js and user.js files from the chapter-01/routes folder to the chapter-01/server/routes folder.

  6. A very simple change here, but during the development process, it will be very useful to better organize all the files of our application.

We still need to change the path to this folder in the main application file, app.js. Open the app.js file from the project root folder and change the following highlighted lines:

... 
var routes = require('./server/routes/index'); 
var users = require('./server/routes/users'); 
 
var app = express(); 
 
// view engine setup 
app.set('views', path.join(__dirname, 'server/views')); 
app.set('view engine', 'ejs'); 
... 

Before we proceed, let's change the welcome message from the routes/index.js file to the following highlighted code:

/* GET home page. */ 
router.get('/', function(req, res, next) { 
    res.render('index', { title: 'Express from server folder' }); 
}); 

To run the project and see the application in your browser, follow these steps:

  1. Type the following command in your terminal/shell:

    DEBUG=chapter-01:* npm start
    
  2. Open your browser at http://localhost:3000.

  3. The output in your browser will be as follows:

    Application home screen

Now we can delete the folders and files from:

  • chapter-01/routes:

    •  index.js

    •  user.js

  • chapter-01/views:

    •  error.js

    •  index.js

Changing the default behavior to start the application

As mentioned earlier, we will change the default initialization process of our application. To do this task, we will edit the app.js file and add a few lines of code:

  1. Open app.js and add the following code after the app.use('/users', users); function:

          // catch 404 and forward to error handler 
          app.use(function(req, res, next) { 
             var err = new Error('Not Found'); 
              err.status = 404; 
              next(err); 
          }); 
    

    It's a simple middleware to intercept 404 errors.

  2. Now add the following code after the module.exports = app; function:

          app.set('port', process.env.PORT || 3000); 
          var server = app.listen(app.get('port'), function() { 
              console.log('Express server listening on port ' + 
              serer.address().port); 
          }); 
    
  3. Open the package.js file at the root project folder and change the following code:

          ... 
          "scripts": { 
             "start": "node app.js" 
          }, 
          ... 
    

    Note

    The debug command is still available if necessary: DEBUG=chapter-01:* npm start.

  4. The package.json file is a file of extreme importance in Node.js applications. It is possible to store all kinds of information for the project, such as dependencies, project description, authors, version, and many more.

  5. Furthermore, it is possible to set up scripts to minify, concatenate, test, build and deploy an application easily. We'll see more on how to create scripts in Chapter 9, Building a Frontend Process with Node.js and NPM.

  6. Let's test the result; open your terminal/shell and type the following command:

          npm start 
    
    
  7. We will see the same output on the console:

          > node app.js 
    
         
    Express server listening on port 3000!
    
 

Restructuring the views folder using partials


Now we will make a major change to the structure of directories in the views folder: we will add an important Embedded JavaScript (EJS) resource for the creation of reusable files in our templates.

They are known as partial files and will be included in our application using the <% = include %> tag.

Tip

You can find more information about EJS on the official project page at: http://ejs.co/

Inside the views folder, we will create two more folders, called partials and pages:

  1. The pages folder will be as follows at this point:

  2. Now let's move the files that were in the views folder to the pages folder.

  3. Create a pages folder inside the views folder.

  4. Create a partials folder inside the views folder.

    • server/

    • pages/

    • index.ejs

    • error.ejs

    • partials/

  5. Now we need to create the files that will be included in all templates. Note that we have just two templates: index.js and error.js.

  6. Create a file called stylesheet.ejs and add the following code:

          <!-- CSS Files --> 
          <link rel='stylesheet' href='https://cdnjs.cloudflare.com/
           ajax/libs/twitter-bootstrap/4.0.0-alpha/css/bootstrap.min.css'>
          <link rel='stylesheet' href='/stylesheets/style.css' />

    Tip

    We will use the latest version of the Twitter Bootstrap UI framework, which at the time this book is being written is in version 4.0.0-alpha.

  7. We are using a Content Delivery Network (CDN) for CSS and JS files.

  8. Create a file called javascript.ejs and add the following code to it:

          <!-- JS Scripts -->
          <script src='https://cdnjs.cloudflare.com/ajax/libs
            /jquery/2.2.1/jquery.min.js'></script>
          <script src='https://cdnjs.cloudflare.com/ajax/libs/
           twitter-bootstrap/4.0.0-alpha/js/bootstrap.min.js'></script>
          </body>
          </html>
  9. Then create a file called header.ejs and add the following code:

          <!-- Fixed navbar --> 
          <div class="pos-f-t"> 
              <div class="collapse" id="navbar-header"> 
                  <div class="container bg-inverse p-a-1"> 
                     <h3>Collapsed content</h3> 
                      <p>Toggle able via the navbar brand.</p> 
                  </div> 
              </div> 
              <nav class="navbar navbar-light navbar-static-top"> 
                   <div class="container"> 
                      <button class="navbar-toggler hidden-sm-up" type=
                        "button"data-toggle="collapse" data-target=
                          "#exCollapsingNavbar2"> 
                          Menu 
                      </button> 
                     <div class="collapse navbar-toggleable-xs"
                       id="exCollapsingNavbar2"> 
                          <a class="navbar-brand" href="/">MVC App</a> 
                          <ul class="nav navbar-nav navbar-right"> 
                              <li class="nav-item"> 
                                  <a class="nav-link" href="/login">
                                    Sign in
                                  </a>
                              </li> 
                              <li class="nav-item"> 
                                  <a class="nav-link" href="/signup">
                                    Sign up
                                  </a> 
                               </li> 
                               <li class="nav-item"> 
                                  <a class="nav-link" href="/profile">
                                     Profile</a> 
                               </li> 
                         
                              <li class="nav-item"> 
                                  <a class="nav-link" href="/comments">
                                    Comments</a> 
                              </li> 
                          </ul> 
                      </div> 
                  </div> 
              </nav> 
          </div> 
          <!-- Fixed navbar --> 
    
  10. Create a file called footer.ejs and add this code:

          <footer class="footer"> 
              <div class="container"> 
                  <span>&copy 2016. Node-Express-MVC-App</span> 
              </div> 
          </footer> 
    
  11. Let's adjust the path for the view templates in our app.js file; add the following lines of code:

          // view engine setup 
          app.set('views', path.join(__dirname, 'server/views/pages')); 
          app.set('view engine', 'ejs'); 
    

    Tip

    Note that we only added the pages folder path that already existed.

  12. Now we will replace the code in pages/index.ejs with the following code:

          <!DOCTYPE html>
          <html>
          <head>
            <title><%= title %></title>
             <% include ../partials/stylesheet %>
          </head> 
          <body>
              <% include ../partials/header %>
              <div class="container">
                <div class="page-header m-t-1">
                  <h1><%= title %></h1>
                </div> 
                <p class="lead">Welcome to <%= title %></p>
              </div>
              <% include ../partials/footer %>
              <% include ../partials/javascript %>
           </body>
           </html>
  13. Let's do the same for the error view file at pages/error.ejs:

          <!DOCTYPE html> 
          <html> 
          <head> 
               <title>Wohp's Error</title> 
               <% include ../partials/stylesheet %> 
          </head> 
          <body> 
               <% include ../partials/header %> 
     
              <div class="container"> 
                  <div class="page-header m-t-1"> 
                      <h1>Sorry: <%= message %></h1> 
                      <h2><%= error.status %></h2> 
                      <pre><%= error.stack %></pre> 
                  </div> 
     
              </div> 
               <% include ../partials/footer %> 
               <% include ../partials/javascript %> 
          </body> 
          </html> 
    

We currently have the following structure in our server folder:

  • server/

  • pages/

  • index.ejs

  • error.ejs

  • partials/

  • footer.ejs

  • header.ejs

  • javascript.ejs

  • stylesheet.ejs2

 

Adding templates for login, sign-up, and profile


Now we have a solid basis to move forward with the project. At this time, we will add some template files for login, sign-up, and profile screens.

The expected result for these pages will be as shown in the following screenshot:

Login screen

Sign-up screen

Profile screen

  1. Now let's create the login template. Create a new file called login.ejs in the views folder and place the following code:

          <!DOCTYPE html> 
          <html> 
          <head> 
              <title><%= title %></title> 
              <% include ../partials/stylesheet %> 
          </head> 
          <body> 
            <% include ../partials/header %> 
       
            <div class="container"> 
                <% if (message.length > 0) { %> 
                    <div class="alert alert-warning alert-dismissible
                       fade in" role="alert"> 
                      <button type="button" class="close" data-dismiss=
                         "alert" aria-label="Close"> 
                        <span aria-hidden="true">&times;</span> 
                      </button> 
                      <strong>Ohps!</strong> <%= message %>. 
                    </div> 
                 <% } %> 
                <form class="form-signin" action="/login" method="post"> 
                <h2 class="form-signin-heading">Welcome sign in</h2> 
                <label for="inputEmail" class="sr-only">Email address</label> 
                <input type="email" id="email" name="email" class="form-
                  control" placeholder="Email address" required=""> 
                <label for="inputPassword" class="sr-only">Password</label> 
                <input type="password" id="password" name="password"
                 class="form-control" placeholder="Password" required=""> 
                <button class="btn btn-lg btn-primary btn-block" 
                   type="submit">Sign in</button> 
                <br> 
                <p>Don't have an account? <a href="/signup">Signup</a> 
                   ,it's free.</p> 
              </form> 
            </div> 
     
            <% include ../partials/footer %> 
            <% include ../partials/javascript %> 
          </body> 
          </html> 
    
  2. Add the login route to routes/index.js after the index route:

          /* GET login page. */ 
          router.get('/login', function(req, res, next) { 
              res.render('login', { title: 'Login Page', message:
               req.flash('loginMessage') }); 
           }); 
    

    Note

    In the template, we are making use of the connect-flash middleware to display error messages. Later, we will show how to install this component; don't worry right now.

  3. Let's add the signup template to the views/pages folder.

  4. Create a new file in views/pages and save as signup.ejs; then add the following code:

          <!DOCTYPE html>
          <html>
          <head>
            <title><%= title %></title>
            <% include ../partials/stylesheet %>
          </head>
          <body>
            <% include ../partials/header %>
            <div class="container">
              <% if (message.length > 0) { %>
                 <div class="alert alert-warning" role="alert">
                   <strong>Warning!</strong> <%= message %>.
                 </div>
               <% } %>
               <form class="form-signin" action="/signup" method="post">
                 <h2 class="form-signin-heading">Please signup</h2>
                 <label for="inputName" class="sr-only">Name address</label>
                 <input type="text" id="name" name="name" class="form-control"
                  placeholder="Name" required="">
                 <label for="inputEmail" class="sr-only">Email address</label>
                 <input type="email" id="email" name="email" class=
                   "form-control" placeholder="Email address" required="">
                 <label for="inputPassword" class="sr-only">Password</label>
                 <input type="password" id="password" name="password" 
                   class="form-control" placeholder="Password" required="">
                 <button class="btn btn-lg btn-primary btn-block" 
                   type="submit">Sign in</button> 
                  <br> 
                  <p>Don't have an account? <a href="/signup">Signup</a>
                      ,it's free.</p>
                </form>
              </div>
              <% include ../partials/footer %>
              <% include ../partials/javascript %>
            </body>
            </html>
  5. Now we need to add the route for the sign-up view. Open routes/index.js and add the following code right after login route:

          /* GET Signup */ 
          router.get('/signup', function(req, res) { 
              res.render('signup', { title: 'Signup Page', 
                 message:req.flash('signupMessage') }); 
          }); 
    
  6. Next, we will add the template to the profile page and the route to this page. Create a file called profile.ejs inside the view/pages folder and add this code:

          <!DOCTYPE html> 
          <html> 
          <head> 
              <title><%= title %></title> 
              <% include ../partials/stylesheet %> 
          </head> 
          <body> 
              <% include ../partials/header %> 
              <div class="container"> 
                <h1><%= title %></h1> 
                <div class="datails"> 
                  <div class="card text-xs-center"> 
                      <br> 
                    <img class="card-img-top" src="<%= avatar %>" 
                      alt="Card image cap"> 
                      <div class="card-block"> 
                        <h4 class="card-title">User Details</h4> 
                        <p class="card-text"> 
                            <strong>Name</strong>: <%= user.local.name %><br> 
                            <strong>Email</strong>: <%= user.local.email %> 
                        </p> 
                        <a href="/logout" class="btn btn-default">Logout</a> 
                      </div> 
                  </div> 
                </div> 
             </div> 
            <% include ../partials/footer %> 
            <% include ../partials/javascript %> 
          </body> 
          </html> 
    
  7. Now we need to add the route for the profile view; open routes/index.js and add the following code right after the signup route:

          /* GET Profile page. */ 
          router.get('/profile',  function(req, res, next) {
              res.render('profile', { title: 'Profile Page', user : req.user,
              avatar: gravatar.url(req.user.email ,  {s: '100', r: 'x', d:
               'retro'}, true) });
          }); 
    

    Tip

    We are using another middleware called gravatar; later, we will show how to install it.

 

Installing additional middleware


As you can see in the previous sections, we used some middleware to display messages and the user icon using the gravatar. In this section, we will see how to install some very important modules for our application.

Since we created templates for the signin, signup, and profile pages, we will need to store the users with login and password.

These are the middleware that we will use for this task, with the definition for each one:

Component

Description

More details

connect-flash

User-friendly messages

https://www.npmjs.com/package/connect-flash

connect-mongo

Drive to connect with MongoDB

https://www.npmjs.com/package/connect-mongo

express-session

Store user sessions in the DB

https://www.npmjs.com/package/express-session

Gravatar

Show a random user picture

https://www.npmjs.com/package/gravatar

Passport

Authentication middleware

https://www.npmjs.com/package/passport

passport-local

Local user/password authentication

https://www.npmjs.com/package/passport-local

Open your terminal/shell and type:

npm install connect-flash connect-mongo express-session gravatar
      passport passport-local -save

Note

As we can see, we will use MongoDB to store user data; you can find more information about MongoDB at https://www.mongodb.org/, and the installation process at https://docs.mongodb.org/manual/installation/ . We assume that you already have MongoDB installed on your machine and it is running.

 

Refactoring the app.js file with the new middleware


At this time, we have to do a major restructuring of the app.js file to include the new middleware that we will use.

We will show you step by step how to include each middleware and at the end, we will see the complete file:

  1. Open app.js and add the following lines before var app = express():

          // ODM With Mongoose 
          var mongoose = require('mongoose'); 
          // Modules to store session 
          var session    = require('express-session'); 
          var MongoStore = require('connect-mongo')(session); 
          // Import Passport and Warning flash modules 
          var passport = require('passport'); 
          var flash = require('connect-flash'); 
    

    This is a simple import process.

  2. Add the following lines after app.set('view engine', 'ejs'):

          // Database configuration 
          var config = require('./server/config/config.js'); 
          // connect to our database 
          mongoose.connect(config.url); 
          // Check if MongoDB is running 
          mongoose.connection.on('error', function() {
            console.error('MongoDB Connection Error. Make sure MongoDB is
             running.'); 
          }); 
     
          // Passport configuration 
          require('./server/config/passport')(passport); 
    
  3. Note that we are using a config.js file in the first line; later we will create this file.

  4. Add the following lines after app.use(express.static(path.join(__dirname, 'public'))):

          // required for passport 
          // secret for session 
          app.use(session({ 
              secret: 'sometextgohere', 
              saveUninitialized: true, 
              resave: true, 
              //store session on MongoDB using express-session +
              connect mongo 
              store: new MongoStore({ 
                  url: config.url, 
                  collection : 'sessions' 
              }) 
          })); 
     
          // Init passport authentication 
          app.use(passport.initialize()); 
          // persistent login sessions 
          app.use(passport.session()); 
          // flash messages 
          app.use(flash()); 
    
 

Adding config and passport files


As mentioned earlier, let's create a config file:

  1. Inside server/config, create a file called config.js and place the following code in it:

          // Database URL 
          module.exports = { 
              // Connect with MongoDB on local machine 
              'url' : 'mongodb://localhost/mvc-app' 
          }; 
    
  2. Create a new file on server/config and name it passport.js. Add the following content:

          // load passport module 
          var LocalStrategy    = require('passport-local').Strategy; 
          // load up the user model 
          var User = require('../models/users'); 
     
          module.exports = function(passport) { 
              // passport init setup 
              // serialize the user for the session 
              passport.serializeUser(function(user, done) { 
                  done(null, user.id); 
              }); 
              //       deserialize the user 
              passport.deserializeUser(function(id, done) { 
                  User.findById(id, function(err, user) { 
                      done(err, user); 
                  }); 
              }); 
              // using local strategy 
              passport.use('local-login', new LocalStrategy({ 
                  // change default username and password, to email 
                  //and password 
                  usernameField : 'email', 
                  passwordField : 'password', 
                  passReqToCallback : true 
              }, 
              function(req, email, password, done) { 
                  if (email) 
                  // format to lower-case 
                  email = email.toLowerCase(); 
                  // process asynchronous 
                  process.nextTick(function() { 
                      User.findOne({ 'local.email' :  email }, 
                       function(err, user)
                    { 
                      // if errors 
                     if (err) 
                       return done(err); 
                     // check errors and bring the messages 
                     if (!user) 
                       return done(null, false, req.flash('loginMessage',
                       'No user found.')); 
                    if (!user.validPassword(password)) 
                      return done(null, false, req.flash('loginMessage',
                      'Wohh! Wrong password.')); 
                    // everything ok, get user 
                    else 
                      return done(null, user); 
                    }); 
                  }); 
               })); 
              // Signup local strategy 
              passport.use('local-signup', new LocalStrategy({ 
                  // change default username and password, to email and 
                 //  password 
                  usernameField : 'email', 
                  passwordField : 'password', 
                  passReqToCallback : true 
              }, 
              function(req, email, password, done) { 
                  if (email) 
                  // format to lower-case 
                  email = email.toLowerCase(); 
                  // asynchronous 
                  process.nextTick(function() { 
                      // if the user is not already logged in: 
                      if (!req.user) { 
                          User.findOne({ 'local.email' :  email },
                           function(err,user) { 
                      // if errors 
                      if (err) 
                        return done(err); 
                      // check email 
                      if (user) { 
                        return done(null, false, req.flash('signupMessage',
                         'Wohh! the email is already taken.')); 
                      }
                      else { 
                        // create the user 
                          var newUser = new User(); 
                          // Get user name from req.body 
                          newUser.local.name = req.body.name; 
                          newUser.local.email = email; 
                          newUser.local.password =
                           newUser.generateHash(password); 
                          // save data 
                         newUser.save(function(err) { 
                       if (err) 
                         throw err; 
                         return done(null, newUser); 
                        }); 
                       } 
                    }); 
                   } else { 
                     return done(null, req.user); 
                   }         }); 
              })); 
          }; 
    

    Note that in the fourth line, we are importing a file called models; we will create this file using Mongoose.

 

Creating a models folder and adding a user schema


Create a models folder inside server/ and add the following code:

// Import Mongoose and password Encrypt 
var mongoose = require('mongoose'); 
var bcrypt   = require('bcrypt-nodejs'); 
 
// define the schema for User model 
var userSchema = mongoose.Schema({ 
    // Using local for Local Strategy Passport 
    local: { 
        name: String, 
        email: String, 
        password: String, 
    } 
 
}); 
 
// Encrypt Password 
userSchema.methods.generateHash = function(password) { 
    return bcrypt.hashSync(password, bcrypt.genSaltSync(8), null); 
}; 
 
// Verify if password is valid 
userSchema.methods.validPassword = function(password) { 
    return bcrypt.compareSync(password, this.local.password); 
}; 
 
// create the model for users and expose it to our app 
module.exports = mongoose.model('User', userSchema); 
 

Protecting routes


At this point, we have enough code to configure secure access to our application. However, we still need to add a few more lines to the login and sign-up forms to make them work properly:

  1. Open server/routes/index.js and add the following lines after the login GET route:

          /* POST login */ 
          router.post('/login', passport.authenticate('local-login', { 
              //Success go to Profile Page / Fail go to login page 
              successRedirect : '/profile', 
              failureRedirect : '/login', 
              failureFlash : true 
          })); 
    
  2. Add these lines after the signup GET route:

          /* POST Signup */ 
          router.post('/signup', passport.authenticate('local-signup', { 
              //Success go to Profile Page / Fail go to Signup page 
              successRedirect : '/profile',       
              failureRedirect : '/signup', 
              failureFlash : true 
          })); 
    
  3. Now let's add a simple function to check whether the user is logged in; at the end of server/routes/index.js, add the following code:

          /* check if user is logged in */ 
          function isLoggedIn(req, res, next) { 
              if (req.isAuthenticated()) 
                  return next(); 
              res.redirect('/login'); 
          } 
    
  4. Let's add a simple route to a logout function and add the following code after the isLoggedIn() function:

          /* GET Logout Page */ 
          router.get('/logout', function(req, res) { 
              req.logout(); 
              res.redirect('/'); 
          }); 
    
  5. The last change is to add isloggedin() as a second parameter to the profile route. Add the following highlighted code:

          /* GET Profile page. */ 
          router.get('/profile', isLoggedIn, function(req, res, next) { 
              res.render('profile', { title: 'Profile Page', user : req.user,
               avatar: gravatar.url(req.user.email ,  {s: '100', r: 'x',
                 d:'retro'}, true) }); 
          }); 
    

The final index.js file will look like this:

var express = require('express'); 
var router = express.Router(); 
var passport = require('passport'); 
// get gravatar icon from email 
var gravatar = require('gravatar'); 
 
/* GET home page. */ 
router.get('/', function(req, res, next) { 
    res.render('index', { title: 'Express from server folder' }); 
}); 
 
/* GET login page. */ 
router.get('/login', function(req, res, next) { 
    res.render('login', { title: 'Login Page', message: req.flash('loginMessage') }); 
}); 
/* POST login */ 
router.post('/login', passport.authenticate('local-login', { 
    //Success go to Profile Page / Fail go to login page 
    successRedirect : '/profile', 
    failureRedirect : '/login', 
    failureFlash : true 
})); 
 
/* GET Signup */ 
router.get('/signup', function(req, res) { 
    res.render('signup', { title: 'Signup Page', message: req.flash('signupMessage') }); 
}); 
/* POST Signup */ 
router.post('/signup', passport.authenticate('local-signup', { 
    //Success go to Profile Page / Fail go to Signup page 
    successRedirect : '/profile', 
    failureRedirect : '/signup', 
    failureFlash : true 
})); 
 
/* GET Profile page. */ 
router.get('/profile', isLoggedIn, function(req, res, next) { 
    res.render('profile', { title: 'Profile Page', user : req.user, avatar: gravatar.url(req.user.email ,  {s: '100', r: 'x', d: 'retro'}, true) }); 
}); 
 
/* check if user is logged in */ 
function isLoggedIn(req, res, next) { 
    if (req.isAuthenticated()) 
        return next(); 
    res.redirect('/login'); 
} 
/* GET Logout Page */ 
router.get('/logout', function(req, res) { 
    req.logout(); 
    res.redirect('/'); 
}); 
 
module.exports = router; 

We have almost everything set to finalize the application, but we still need to create a page for comments.

 

Creating the controllers folder


Instead of using the routes folder to create the route and functions of the comments file, we will use another format and create the controllers folder, where we can separate the route and the controller function, thus having a better modularization:

  1. Create a folder called controllers.

  2. Create a file called comments.js and add the following code:

          // get gravatar icon from email 
          var gravatar = require('gravatar'); 
          // get Comments model 
          var Comments = require('../models/comments'); 
           
          // List Comments 
          exports.list = function(req, res) { 
             // List all comments and sort by Date 
              Comments.find().sort('-created').populate('user',
                'local.email').exec(function(error, comments) { 
                  if (error) { 
                      return res.send(400, { 
                          message: error       
                      }); 
                  } 
                  // Render result 
                  res.render('comments', { 
                      title: 'Comments Page', 
                      comments: comments, 
                      gravatar: gravatar.url(comments.email,
                         {s: '80', r: 'x', d: 'retro'}, true) 
                  }); 
              }); 
          }; 
          // Create Comments 
          exports.create = function(req, res) { 
             // create a new instance of the Comments model with request body 
              var comments = new Comments(req.body); 
              // Set current user (id) 
              comments.user = req.user; 
              // save the data received 
              comments.save(function(error) { 
                  if (error) { 
                      return res.send(400, { 
                          message: error 
                      }); 
                  } 
                  // Redirect to comments 
                  res.redirect('/comments'); 
              }); 
          }; 
          // Comments authorization middleware 
          exports.hasAuthorization = function(req, res, next) { 
              if (req.isAuthenticated()) 
                  return next(); 
              res.redirect('/login'); 
          }; 
    
  3. Let's import the controllers on the app.js file; add the following lines after var users = require('./server/routes/users'):

          // Import comments controller
          var comments = require('./server/controllers/comments'); 
  4. Now add the comments route after app.use('/users', users):

          // Setup routes for comments 
          app.get('/comments', comments.hasAuthorization, comments.list); 
          app.post('/comments', comments.hasAuthorization, comments.create); 
    
  5. Create a file called comments.ejs at server/pages and add the following lines:

          <!DOCTYPE html> 
          <html> 
          <head> 
              <title><%= title %></title> 
              <% include ../partials/stylesheet %> 
          </head> 
          <body> 
            <% include ../partials/header %> 
            <div class="container"> 
              <div class="row"> 
                <div class="col-lg-6"> 
                  <h4 class="text-muted">Comments</h4> 
                </div> 
                <div class="col-lg-6"> 
                  <button type="button" class="btn btn-secondary pull-right"
                   data-toggle="modal" data-target="#createPost">
                        Create Comment 
                    </button> 
                </div> 
                </div> 
                <!-- Modal --> 
                <div class="modal fade" id="createPost" tabindex="-1"
                 role="dialog" aria-labelledby="myModalLabel"
                  aria-hidden="true"> 
                  <div class="modal-dialog" role="document"> 
                    <div class="modal-content"> 
                      <form action="/comments" method="post"> 
                        <div class="modal-header"> 
                          <button type="button" class="close" 
                           data-dismiss="modal" aria-label="Close"> 
                             <span aria-hidden="true">&times;</span> 
                           </button> 
                           <h4 class="modal-title" id="myModalLabel">
                            Create Comment</h4> 
                        </div> 
     
                        <div class="modal-body"> 
                          <fieldset class="form-group"> 
                             <label  for="inputitle">Title</label> 
                             <input type="text" id="inputitle" name="title"
                               class="form-control" placeholder=
                                "Comment Title" required=""> 
                           </fieldset> 
                           <fieldset class="form-group"> 
                             <label  for="inputContent">Content</label> 
                             <textarea id="inputContent" name="content"
                              rows="8" cols="40" class="form-control"
                              placeholder="Comment Description" required="">
                             </textarea> 
                           </fieldset> 
     
                           </div> 
                            <div class="modal-footer"> 
                              <button type="button" class="btn btn-secondary"
                               data-dismiss="modal">Close</button> 
                              <button type="submit" class="btn btn-primary">
                               Save changes</button> 
                            </div> 
                      </form> 
                    </div> 
                  </div> 
                </div> 
                  <hr> 
                <div class="lead"> 
                  <div class="list-group"> 
                    <% comments.forEach(function(comments){ %> 
                      <a href="#" class="list-group-item"> 
                        <img src="<%= gravatar %>" alt="" style="float: left;
                          margin-right: 10px"> 
                   <div class="comments"> 
                    <h4 class="list-group-item-heading">
                      <%= comments.title %></h4> 
                     <p class="list-group-item-text">
                       <%= comments.content %></p> 
                     <small class="text-muted">By: 
                        <%= comments.user.local.email %>
                     </small> 
                    </div> 
                    </a> 
     
                <% }); %> 
                </div> 
              </div> 
             </div> 
              <% include ../partials/footer %> 
              <% include ../partials/javascript %> 
          </body> 
          </html> 
    
  6. Note that we are using a simple Modal component from Twitter-bootstrap for the addition of comments, as shown in the following screenshot:

    Model for the create comments screen

  7. The last step is to create a model for the comments; let's create a file named comments.js at server/models/ and add the following code:

          // load the things we need 
          var mongoose = require('mongoose'); 
          var Schema = mongoose.Schema; 
     
          var commentSchema = mongoose.Schema({ 
              created: { 
                  type: Date, 
                  default: Date.now 
              }, 
              title: { 
                  type: String,       
                  default: '', 
                  trim: true, 
                  required: 'Title cannot be blank' 
              }, 
              content: { 
                  type: String, 
                  default: '', 
                  trim: true 
              }, 
              user: { 
                  type: Schema.ObjectId, 
                  ref: 'User' 
              } 
          }); 
     
          module.exports = mongoose.model('Comments', commentSchema); 
    
 

Running the application and adding comments


Now it's time to test whether everything is working properly:

  1. Open terminal/shell at the root project folder and type the following command:

    npm start
    
  2. Check your browser at: http://localhost:3000.

  3. Go to http://localhost:3000/signup and create a user called John Doe with the e-mail ID as john@doe.com and password as 123456.

  4. Go to http://localhost:3000/comments, click on the create comment button and add the following content:

          Title: Sample Title 
          Comments: Lorem ipsum dolor sit amet, consectetur adipiscing elit,
           sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.
           Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris
           nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in
           reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla
           pariatur. Excepteur sint occaecat cupidatat non proident, sunt in
           culpa qui officia deserunt mollit anim id est laborum. 
    
  5. The following screenshot illustrates the final result:

    Comments screen

Checking the error messages

Now let's check the flash-connect messages. Go to http://localhost:3000/login and try to log in as a user; we will use martin@tech.com with password 123.

The following screenshot illustrates the result:

Error message on the login screen

Now we try to sign up with an already registered user. Go to http://localhost:3000/signup and place the following content:

name: John Doe 
email: john@doe.com
password: 123456 

This screenshot illustrates the result:

Error message on signup screen

 

Summary


In this chapter, we discussed how to create MVC applications using Node.js and the express framework, with an application fully on the server side-something very similar to applications created with the Rails or Django framework.

We also built safe routes and a very robust authentication with session control, storage of session cookies, and encrypted passwords.

We used MongoDB to store the data of users and comments.

In the next chapter, we will see how to use another database system with express and Node.js.

About the Author
  • Fernando Monteiro

    Fernando Monteiro is a full-stack engineer, speaker, and open source contributor. He has built and made some of his personal projects open source such as Responsive Boilerplate, Frontend Boilerplate, Angm-Generator, and TrelloMetrics. With around 16 years of experience in information technology, his current focus is on web and mobile enterprise JavaScript applications. He has worked as graphic designer for various companies and products, including mobile applications.

    Browse publications by this author
Latest Reviews (3 reviews total)
Good content, nice work, quality information
awesome content! I recommend!
Very economical prices for the quality of the material of the books, videos and courses.
Node.js 6.x Blueprints
Unlock this book and the full library FREE for 7 days
Start now