Working with Express.js and Node.js.

I created a blog using express and node. This should be a pretty long tutorial. Express describes itself as a minimal and flexible Node.js web application framework that provides a robust set of features for web and mobile applications. You can learn more about express here. In order to create the blog I had to use many packages. Some of the things I will go through in this tutorial are:

  • Using debugging in node
  • Getting post values
  • Authentication using passport
  • Express routers
  • View engines and ejs
  • Importing assets ( css, js )
  • MongoDB
  • Controllers and Models setup

When learning new software topics I usually using PluralSight as my main resource. To get started with express and node I learnt from a course you can find here.

Getting Started

Create Page

As always there are a few ways to get started. You can download my repository and follow along, actually that’s the only way because I don’t really deep dive into any areas. So to have a full overview you should download the completed application. Check it here or follow the instructions below.

# download repo
git clone https://github.com/wyntonfranklin/express-blog-tutorial.git

# Go into repo
cd express-blog-tutorial

#Install dependencies
npm install

#run the app
npm start

App.js File

The app.js file is the main file that executes your express application. At the top of my file are some of my important imports.

// app.js

const express = require('express'); // express
const debug = require('debug')('app'); // debugging
const path = require('path'); // path management
const bodyParser = require('body-parser'); // forms
const passport = require('passport'); // authentication
const app = express();
const port = process.env.PORT || 3000; // port to run on

The view engine I used was ejs. So I had to set my view engine and the folder where the views would be located.

// app.js

app.set('views', './src/views');
app.set('view engine', 'ejs');

I also had to let express know where my CSS and JavaScript assets were. To do that I added the line below. Note how the path function join is used.

app.use(express.static(path.join(__dirname, '/public/')));

Next I get my routes and add them to the app object. I will go through how this was done.

const postRouter = require('./src/routes/postRoutes')(nav);
const homeRouter = require('./src/routes/homeRoutes');

app.use('/post', postRouter);
app.use('/', homeRouter);

Finally I execute my application by calling listen. The port number was sent at the top of the file.

app.listen(port, () => {
  debug(`listening on port ${chalk.green(port)}`);
});

Alot is happening here so I’ll go through as much as possible.

Directory

The directory setup is pretty simple.

  • app.js – main file
  • src – source files ( routes, controllers, models, views )
  • public – assets ( css, img, js, vendor )
app.js
 /src
   /routes
     homeRoutes.js
     postRoutes.js
   /controllers
     homeController.js
     postController.js
   /models
     login.js
     post.js
     user.js
   /views
     index.ejs
 /public
   /css
   /img
   /js
   /vendro

homeRoutes.js File

The homeRoutes file has the main routes that are used in the application. Its also simpler in step up than the postRouter. To build this psudo blog I used controllers and attached the controller functions to my route. This made the code so much cleaner. A beginner might get confused but you can always refer to express documents. Check out routing here.

To get started I called in my express object and got a router from it. I also required my homeController which we will look at next.

const express = require('express');

const homeRouter = express.Router();
const controller = require('../controllers/homeController');

Its pretty straight forward from here. I called the controller functions where I wanted to use them in my routes. The ‘/’ location should call the controller.home function and the ‘/login’ will call the controller.auth function. Once I’m done adding functions to my router I export it.

homeRouter.route('/')
.get(controller.home);

homeRouter.route('/login')
.post(controller.auth);

module.exports = homeRouter;

homeController.js File

To get started I imported the objects I needed to work in this file.

const model = require('../models/post');
const debug = require('debug')('app');
const passport = require('passport');
const nav = require('../menus/main');
const user = require('../models/user');

The model called post and user I will go through later. Looking at a simple function in the homeController called the about function which maps to ‘/about’ route. When done like this – on the exports object – I don’t need to specifically export anything. Either way works find module.exports or exports.

exports.about = (req, res) => {
  res.render(
    'about',
    {
      title: 'About',
      nav: getNavs(req),
      excerpt: 'A Blog created using express.js'
    }
  );
};

Above we note we have a function with two parameters which are req, res or request and response objects. On the res object I call a function render to render a view named ‘about’ and I pass in some data that would be accessible in that view. If I didn’t use a controller this same function would be in the router file instead.

The home function on this controller uses the post model to get data from the database to show our recent posts.

exports.home = async (req, res) => {
  await model.getPosts().then((data) => {
    res.render(
      'index',
      {
        username: user.userName(req),
        title: 'Express.js Blog',
        nav: getNavs(req),
        excerpt: 'A Blog created using express.js',
        posts: data
      }
    );
  });
};

Using a async function and await to wait for the results from the database. It returns a promise containing the results which I retrieve using the then function and then renders the index page with the data from the database.

post.js File

The post.js file is a model based on the MVC architecture. I should have named it postModel.js. To late now 😦 . This file interacts with the database and gets the post collections from it. As always I require the objects I need.

const { MongoClient, ObjectID } = require('mongodb');

const debug = require('debug')('app');

const url = 'mongodb://localhost:27017';

In order to use mogoclient and mongodb you would have had to install these npm packages of course. My package.json file had all these dependencies.

// package.json  

"dependencies": {
    "body-parser": "^1.18.3",
    "bootstrap": "3.3.7",
    "chalk": "1.1.3",
    "cookie-parser": "^1.4.3",
    "debug": "2.6.6",
    "ejs": "^2.5.7",
    "express": "4.15.2",
    "express-session": "^1.15.6",
    "jquery": "3.2.1",
    "mongodb": "^3.1.10",
    "morgan": "1.8.1",
    "nodemon": "^1.12.1",
    "passport": "^0.4.0",
    "passport-local": "^1.0.0",
    "pug": "^2.0.0-rc.4"
  },

Back to the post model. The getPost function connects to the database and requests the collection using the find function. It converts the results to an array and returns the posts. Which is a promise with the results.

async function getPosts() {
    const client = await MongoClient.connect(url);
    const posts = client.db('wfTutorials')
        .collection('posts')
        .find()
        .toArray();
    return posts;
}

You can view these snippets from the code itself as I do the same thing for the other two functions. To get a single post I connect to the client and use the findOne function.

async function getPost(id) {
    const client = await MongoClient.connect(url);
    const post = client.db('wfTutorials')
        .collection('posts').findOne({
            _id: new ObjectID(id)
        });
    return post;
}

Add to insert an object into the database you do the following below.

async function insertPost(object) {
    const client = await MongoClient.connect(url);
    client.db('wfTutorials')
        .collection('posts').insertOne(object, (error, result) => {
            if (error) {
                debug(error);
            }
            return result;
        });
}

Using async functions were pretty difficult for me at first. But I think its the only way to retrieve long tasks when you are using node. If you run this without async the results return null because it hasn’t been executed as yet. Using async it waits for the results.

Finally I add the functions to my exports object.

exports.getPosts = getPosts;

exports.getPost = getPost;

exports.insertPost = insertPost;

Running MogoDB

This might be in the wrong part of this post but here goes. To setup mongo server on your local computer you should following the steps here.

For me I had to run a Linux command to get it started after I downloaded it of course. It required a db/files folder in my base directory. I also installed Robo 3T which is a GUI client for mongo. You can learn about them here.

Robo 3T client

Once mogodb was setup I could using my application properly. If the mongo server isn’t started the app would error out. Note that my post model ( post.js ) I have a constant named url.

// src/models/post.js

const url = 'mongodb://localhost:27017';

Mongo runs on port 27017 by default.

postController.js File

The post controller deals with creating a new post and viewing a post. To view a post I just had to get the id param from the url.

exports.view = async (req, res) => {
    const { id } = req.params;
    await model.getPost(id).then((post) => {
        res.render('post', {
            nav: guestNav,
            title: 'View Post',
            excerpt: 'Post Details',
            post
        });
    });
};

Using req.params and using object destructing (learn more here ) I got the id from the url. The route is in the postRouter.js file and looks like below.

// src/routes/postRoutes.js

  postRouter.route('/:id')
.get(controller.view);

The id placeholder in the route is what allows me to get the id in the controller.

The save function in the post controller is how I created a new post.

exports.save = async (req, res) => {
    try {
        await model.insertPost(req.body);
    } catch (error) {
        debug(error);
    }
    renderCreateView(res, true);
};

The req.body holds the post data from your form. This is why we required body-parser in the main app to get this feature. The model insertPost function was reviewed in the post.js section. After I save the post I rendered a view with two parameters.

function renderCreateView(res, status) {
    res.render('create', {
        formSubmit: status,
        nav: guestNav,
        title: 'Create Post',
        excerpt: 'Create a new Post',
    });
}

Basically the formSubmit key is what I use to determine if a post was recently created. If true I display an alert.

// src/views/create.ejs   

<% if(formSubmit){ %>
          <div class="alert alert-success" role="alert">
            Your post was successfully submitted.
            <button type="button" class="close" data-dismiss="alert" aria-label="Close">
            <span aria-hidden="true">×</span>
          </button>
          </div>
        <% } %>
Form Submit

The router function for the save function in the post Controller is shown below.

 postRouter.route('/create')
    .get(controller.create)
.post(controller.save);

As you can see I can attach multiple methods to my routes. The get function displays the form and the post function runs the save function on the controller to create a new post.

If you wanted to pass in a parameter to your router file you can do something similar to the postRouter.js.


function router(nav) {

  postRouter.route('/create')
    .get(controller.create)
    .post(controller.save);

  postRouter.route('/:id')
    .get(controller.view);

  return postRouter;
}


module.exports = router;

It passes in the nav object and returns the postRouter. In the main app all I had to do was call the function with the required parameter.

const postRouter = require('./src/routes/postRoutes')(nav);

Setting up Authentication

To add a login feature to this blog I used passport. You can learn more about passport.js here. Getting started I imported the required modules.

const passport = require('passport');
const cookieParser = require('cookie-parser');
const session = require('express-session');

Next I created a model called login.js in the models directory. This model holds all the features I needed to get my authentication working.

login.js File

To get started I call in all the modules that I need.

const passport = require('passport');
const { Strategy } = require('passport-local');
const { MongoClient } = require('mongodb');
const debug = require('debug')('app')

In my dependencies you would realized I installed a package call passport-local. Here I take out the object Strategy from it. Learn more about it here.

Setting up the local strategy is shown below. I just followed suite here from the PluralSight course.

// src/models/login.js

exports.localStrategy = function localStrategy() {
    const userObject = {
        usernameField: 'username',
        passwordField: 'password'
    };
    const strat = new Strategy(userObject, async (username, password, done) => {
        const user = await getUser(username);
        if (user.password === password) {
            done(null, user);
        } else {
            done(null, false);
        }
    });
    passport.use(strat);
};

The object new Strategy takes and object and has a callback. Within the callback you need to implement the logic to check if the user object exists. I used mongo to check the username. The getUser function is shown below.

// src/models/login.js 

async function getUser(username) {
    const client = await MongoClient.connect(url);
    const user = client.db(dbName)
        .collection('users')
        .findOne({ username });
    return user;
}

Basic Mongo DB stuff. Async stuff. Collection stuff. Find One function. Returns the user object. When that’s finish I tell passport to use this strategy.

To initialize passport I used the function below

// src/models/login.js 

exports.initalizePassport = function initalizePassport() {
    // Stores user in session
    passport.serializeUser((user, done) => {
        done(null, user);
    });

    // Retrieves user from session
    passport.deserializeUser((user, done) => {
        done(null, user);
    });
};

This function is called in the app.js file to get passport started.

So in the main app.js file I call my login model and give it the name auth. I then call the functions that are need to get passport working. And I tell express to use passport and start a passport session.

// app.js 

const auth = require('./src/models/login');

auth.localStrategy();
app.use(passport.initialize());
app.use(passport.session());
auth.initalizePassport();

User Model

Passport adds the user model to the request object that we use in controllers and routes. What this means is that basically we can check to see if we are logged in if the user object exists. The user.js file does this.

// src/models/user.js

function isGuest(req) {
    if (req.user) {
        return true;
    }
    return false;
}


exports.userName = (request) => {
    if (isGuest(request)) {
        return request.user.username;
    }
    return 'Guest';
};

exports.isGuest = isGuest;

Basically I check for the user object from a request object. To get the username I do the same thing except that I return the username from the existing object.

front page

In the above image if the user is logged in the username will replace guest. You can see this in action from the homeController function for this view.

// src/controllers/homeController.js

const user = require('../models/user');

exports.home = async (req, res) => {
  await model.getPosts().then((data) => {
    res.render(
      'index',
      {
        username: user.userName(req), // get username
        title: 'Express.js Blog',
        nav: getNavs(req),
        excerpt: 'A Blog created using express.js',
        posts: data
      }
    );
  });
};

So I use the user model to get the username if the user is logged in. To logout the user all i have to do call the logout function on the request object.

// src/controllers/homeController.js

exports.logout = (req, res) => {
  req.logout();
  res.redirect('/');
};

Above I logout the user and redirect to the home page.

Views

I use ejs as the view template engine. Ejs is cool because I can use HTML as normal. Unlike pug. I’m sure pug has its advantages just right now plain old HTML is fine for me. I used a bootstrap startup template that you can find here.

In ejs to get a variable that I passed in I can do the following.

// src/view/index.ejs

<div class="page-heading">
        <h1><%= title %>s</h1>
        <span class="subheading"><%= excerpt %></span><br>
        <span>Logined as <%= username %> </span>
</div>

Above I’m using the variables title, excerpt and username. Remember from the controller I pass these values in. The home function is what renders the index.ejs view.

// src/controllers/homeController.js

exports.home = async (req, res) => {
  await model.getPosts().then((data) => {
    res.render(
      'index',
      {
        username: user.userName(req),
        title: 'Express.js Blog',
        nav: getNavs(req),
        excerpt: 'A Blog created using express.js',
        posts: data
      }
    );
  });
};

To display the posts I had to go through and array of post data in the template file. Below you can see the for function. Note I have no equals next to the <% item. This is only when I’m display or echoing content to the page. Like when I’m getting the post title.

      <div class="col-lg-8 col-md-10 mx-auto">
        <%for ( let k=0; k< posts.length; k++) { %> 
          <div class="post-preview">
            <a href="/post/<%=posts[k]._id %>">
              <h2 class="post-title">
               <%= posts[k].title %>
              </h2>
              <h3 class="post-subtitle">
                <%= posts[k].excerpt %>
              </h3>
            </a>
            <p class="post-meta">Posted by
              <a href="#"><%= posts[k].author %></a>
              on <%=posts[k].date %></p>
          </div>
          <hr>
          <% } %>
          <!-- Pager -->
          <div class="clearfix">
            <a class="btn btn-primary float-right" href="#">Older Posts →</a>
          </div>
</div>

To save yourself from redundancy you can include sub templates in your ejs files. For me the header and footer sections where included so I wouldn’t have to add them twice.

<% include header %>

 <% include footer %>

You can use your JavaScript variables in these included filfes as well. So everything works fine.

Debugging

Debugging is important even though I left it last. In order to do debugging properly I added some modules in the app.js file. I’ll do a short overview of what each does.

// app.js

const chalk = require('chalk');
const debug = require('debug')('app');
const morgan = require('morgan');

Chalk is used to give color to your console statements. In the app.js file it is used when outputting to the console the port the app is running on.

 debug(`listening on port ${chalk.green(port)}`);

So above we used debug and a template string to use the chalk function to get green output.

Output to console using chalk

You can learn more about chalk on its npm page here.

Using the debug function is much like using console.log except debug only works in debug mode when running your application. In the package.json file you can see the script we run when starting our app.

// package.json

"scripts": {
    "test": "echo \"Error: no test specified\" && exit 1",
    "start": "DEBUG=app nodemon app.js", // npm start
    "lint": "eslint app.js"
},

The command npm start executes the text in the script object above. When we created the debug object we told it where in the application we were in.

const debug = require('debug')('app'); // app is our tag

So running the application using DEBUG=app will show all errors associated with that tag. We could also use DEBUG=* to get all debug statements.

Morgan is a HTTP request logger middleware for node.js. In the app.js file we use it with a config setting tiny.

// app.js 

app.use(morgan('tiny'));

You can learn more about morgan from its npm page here.

Middleware

All these use statements is something called middleware. Which looks something like the code below. You can learn more about middleware here.

var app = express()

app.use(function (req, res, next) {
  console.log('Time:', Date.now())
  next()
})

You will notice that app.js has alot of middleware. The bodyparser, passport our routers are all middleware to name a few.

// some middleware in use in app.js

app.use(morgan('tiny'));
app.use(bodyParser.json());
app.use('/post', postRouter)

Conclusion

Express.js makes it easy to create a web application using node. It was easy to understand and not hard to setup. What will be really interesting for me is using node in a more asynchronous nature. Probably using the mean stack to build an application. For now this is a great start to understand web development in node.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google photo

You are commenting using your Google account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s