Express 4.x OAuth and Local Authentication with Passport.js: Pt 2

muyiwa akin-ogundeji
14 min readDec 27, 2015

--

In part 1 of this mini-series we covered the design of the app and built out the backend. In this article we’re going to build the frontend SPA which will consume the backend API endpoints and deliver useful features to the user.

The SPA portion of the app begins when a user successfully authenticates and is redirected to the ‘/dashboard’ route. The view that is rendered contains the basic structure required by the SPA to function as well as holding data that is useful to the user.

The SPA is built around Vue.js, as you may know from previous articles, Vue.js makes it simple and straightforward to build rich reactive UIs around the MVVM architecture.

Let’s begin.

THE SPA VIEW

The SPA view is delivered by dashboard.ejs as shown below:

Lines 12–21 hold the structure the SPA needs to function. As you may recollect from a previous article, a Vue.js application is essentially simply the VM portion of the MVVM pattern. The V portion is represented by 2 constructs a) the base view which is represented by the HTML in this case lines 12–21 of dashboard.ejs and b) ‘templates’ which render the view for each ‘component’ of the base VM.

Before we really dig in to the SPA portion, lets quickly recap the structure of a Vue.js VM. The VM itself is either an instance of the Vue constructor i.e.

var vm = new Vue({…});

or the VM may act as a ‘component’ and is thus an instance of an extension of the base Vue constructor i.e.

var component = Vue.extend({…});

In either case the VM requires the following properties/attributes:

  1. data model: this is a POJO for a base Vue VM (i.e. an instance of the base Vue constructor) or a function which returns an object for a Vue component VM.
//for a base Vue VM
var data = {…};
//for a Vue component VM
var data = function () {
return {...};
};

2. view element: this is a normal HTML structural element for the base Vue VM and a ‘template’ for a Vue component VM. The purpose of the view element is to render the VM.

//for a base Vue VM
<div id="app">.....</div>
//for a Vue component VM
<script id="component" type="text/x-template>
<div>....</div>
</script>

3. The 2 preceding properties are mandatory, optional elements include a ‘methods’ property (if the VM will handle DOM events/user action), an ‘events’ property (to handle custom events used to decouple the components of a base app), a ‘computed’ property (used to handle dynamically generated data) and life-cycle hooks (used to determine behaviors at specific stages of the VM life cycle).

Thus a full Vue VM may be structured as follows:

var vm = new Vue({
el: '#app',
data: {
places: ['home', 'office', 'gym']
},
methods: {
onClick: function () {
//do something
}
}
};

Recollect also that the Vue.js implementation of the MVVM pattern is built around the following:

  1. Views
  2. View Models
  3. Declarative data bindings
  4. Directives

OK, lets get down to it…

The functionality of the SPA is provided by the contents of ‘/public/js’, because we are using browserify, we are able to build our app using ‘Node style/CommonJS’ modules. Furthermore, the ‘partialify’ transform allows us to create the ‘templates’ for each component as a separate ‘.html’ file and simply ‘require’ the template within the interested component.

We begin with the base app.

APP.JS

In this module, the only dependency is the ‘vue.js’ npm module. Next we configure the module (lines 10–12) to use the ‘vue-resource’ module. This is a Vue.js specific AJAX/HTTP request module which makes it really easy to perform async ops from Vue.js. Furthermore as an extension of the base Vue.js, it exposes it’s functionality as a property of the VM i.e. ‘this.$http’.

Line 10 essentially pulls in the ‘vue-resource’ module and ‘uses’ it as middleware (notice the ‘.use()’ idiom). Lines 11–12 are basic default settings, mapping the ‘root’ property of ‘vue-resource’ to ‘/root’ of the Vue.js app and then setting the ‘Authorization’ header of the ‘http’ request.

In lines 19–47, we define the ‘appGlobals’ variable which is used to retrieve both app and user data from the ‘window.appUserData’ global variable created on lines 72–91 of dashboard.ejs. This object is accessible to both the base app and the components and provides a single point to make edits to app-wide variables.

In lines 48–52 we pull in the various components of the base app by requiring them. This allows our app to be modular, maintainable and much easier to debug.

In lines 56–90 we create the base VM, notice that the value of the ‘el’ attribute corresponds to the value of the ‘div’ on line 12 of dashboard.ejs, notice also that the single data property which the base app has is ‘currentPage’ which defaults to the ‘app-profile’ key. This key represents the value of the component which it’s mapped to i.e. appProfile (line 83).

The base VM also has a single ‘events’ property i.e. ‘setPage’ which is responsible for setting the proper ‘page’ to be rendered by the SPA. To clearly understand what’s going on here, lets go back to dashboard.ejs. Lines 12–21 are the interesting portions of dashboard.ejs as they define the basic structure of the base Vue VM.

As explained already, line 12 provides the ‘hook’ to which the VM is attached i.e. the HTML element which is rendered as its view.Line 14 is a custom element which represents a ‘component’ of the VM and this mechanism is a feature of the soon coming ‘Web Components’ standard, (FYI components are also used in React), Vue uses these custom elements to define the layout of components of a VM, this makes it quite easy to decide what goes where and to understand the interaction of the various pieces.

While ‘<app-nav class=”col-md-9"></app-nav>’ defines where the component which corresponds to the ‘app-nav’ key of the base VM component will go, the actual content of this element is determined by the ‘template’ attribute of the component VM. I hope that’s clear.

Lines 17–18 are of particular interest in understand the meaning of the ‘currentPage’ data property of the base VM and how the ‘setPage’ event handler works.

In Vue.js, the ‘<component>’ tag is a special custom tag which provides a single ‘mount point’ for dynamically switching component views based on some directive. In this app, the directive is provided by ‘:is=”currentPage” ‘ and it simply says that the component to be displayed is whatever the ‘currentPage’ data property of the base VM points to (remember that is ‘app-profile’ by default), furthermore, the ‘keep-alive’ attribute preserves any previously rendered component in memory rather than garbage collecting it, this makes the app much more responsive.

With that in mind, it’s easier to understand what ‘setPage’ does. ‘setPage’ maps to an event handler which responds to the ‘setPage’ custom event and receives a ‘page’ param and examines the value of the argument, it then sets the value of ‘currentPage’ to the appropriate value which maps to a VM component. Notice that it is the base app itself which mutates itself rather than allowing some child component to mutate it which could lead to unforeseen complications. This is a nice way (using custom events) to decouple our app and keep separate portions cleanly segregated.

In essence, a Vue.js VM ‘events’ attribute contains a list of custom events which that VM listens for and these events are mapped to handlers which respond to them.

The final portion of the base app is the ‘components’ attribute. This holds a list of ‘component’ identifiers which are mapped to ‘component’ instances. Because we are using browserify, these component instances have been created as separate modules within a ‘components’ directory. The components have been ‘required’ individually on lines 48–52, and now on lines 82–86 they’re mapped to ‘identifiers’ on the base app VM.

Note that the ‘<pre>{{$data | json}}</pre>’ in line 20 has no functional purpose, i’ve included it only so we can see how the value of the ‘data’ attribute of the base VM changes when we click the links in the SPA nav bar i.e. ‘app-nav’.

Next up we’ll examine the various components of the SPA.

APP-NAV COMPONENT

The ‘app-nav’ component resides in ‘./components/nav/index.js’. You may recall that if a ‘Node’ module is created as a directory, then Node looks for an ‘index.js’ file in the absence of a ‘package.json’ file for the directory in order to resolve the path for the module. This is why on lines 48–52 of the base app, we simply did something like ‘appNav = require(‘./components/nav’),’ rather than ‘appNav = require(‘./components/nav/index.js’),’ i.e. we pointed to the folder itself rather than the file, this is a very convenient idiom we should become familiar with.

The ‘app-nav’ component has 2 attributes, a) template: which requires the HTML template created as a ‘.html’ file, the ‘partialify’ transform makes this super easy for us to do, and b) methods: this contains a ‘changePage’ event handler, it takes a ‘page’ as argument and the ‘emits’ (line 5) a ‘setPage’ custom event as well as passing the received ‘page’ as the data attribute of the event. Recollect that the ‘setPage’ custom event is listened for by the base VM, this is how components can communicate without being tightly coupled.

What we have examined so far per ‘app-nav’ can be thought of as the ‘backend’, while this handles the event, the interface which captures the ‘click’ event is presented by the ‘app-nav’ component’s ‘template’ property.

APP-NAV TEMPLATE

As we may notice from the code above, each ‘li’ element has a directive ‘@click.prevent.stop’ which is mapped to the ‘changePage’ DOM event handler contained in the ‘app-nav’ component’s ‘methods’ attribute. The appropriate ‘page’ value is passed to the event handler and this is further passed to the base app VM to effect the rendering of the appropriate VM component. Thus when the ‘Local’ link is clicked, the ‘setPage’ custom event is emitted with the value of ‘page’ being set to ‘local’ and in the base app, on interception of the ‘setPage’ event with the ‘page’ data, the event handler sets the ‘currentPage’ property to the ‘app-local’ key which causes the ‘appLocal’ component to be rendered.

Next we examine the app-profile component.

APP-PROFILE COMPONENT

As noted earlier, ‘app-profile’ is the default setting for ‘currentPage’, the ‘app-profile’ component simply presents some useful information to the user as well as welcoming him/her to the SPA. The view is defined in the accompanying template.

APP-PROFILE TEMPLATE

Lets examine the ‘app-profile’ template to understand how it works.

In line 2, there is a ‘v-show’ directive mapped to the value of the ‘userPhoto’ property of the ‘app-profile’ data attribute. Recollect from a previous article that all properties declared in the VM template are exposed by the underlying VM as properties of it’s various attributes e.g, data, methods, events etc.

Moving forward, the directive on line 2 is simply saying ‘show this ‘figure’ element if the ‘userPhoto’ property of ‘app-profile’ is truthy!’. In the ‘img’ element, the ‘:src’ directive binds the value of the ‘img’ element’s ‘src’ attribute to the value of ‘app-profile’ userPhoto property. Likewise the ‘:alt’ directive bind’s the value of ‘alt’ to the value of ‘app-profile’ ‘person’ property.

On line 4, there’s a simple text interpolation to provide the values for ‘person’ and ‘currentProfile’. Notice that ‘person’ has 3 ‘{}’, while ‘currentProfile’ has 2. This is because ‘person’ may contain HTML elements and thus the 3 ‘{}’ is used to render the HTML without escaping it, while the 2 ‘{}’ for ‘currentProfile’ escapes HTML.

Lines 7–10 are used to conditionally render elements based on the underlying values of the ‘app-profile’ properties to which the directives are mapped. Notice also how interpolation is used to render certain values e.g. ‘{{localLinkStatus}}’.

Next we examine the ‘app-local’ component.

APP-LOCAL COMPONENT

The ‘app-local’ component has 3 attributes — a) template: which pulls in the template file b) data: which holds the values of ‘local’ specific data retrieved from the ‘appGlobals’ object and c) computed: this attribute contains a single property ‘showBlock’, what ‘showBlock’ does is examine the ‘details.username’ property of the data attribute to determine whether or not it is ‘truthy’. The property is ‘truthy’ if it has a length greater than ‘0’ and this determines whether the ‘showBlock’ property is ‘true’ or ‘false’.

Like all Vue.js VM’s, the value of the ‘showBlock’ property is used in the ‘template’ as shown next.

APP-LOCAL TEMPLATE

On line 3, we see where the ‘showBlock’ property is used as the mapping for a ‘v-if’ directive. What’s happening here is that ‘v-if’ is examining the value of ‘showBlock’ to determine whether or not to render the content within the ‘<template>..</template>’ block! The content will be rendered if ‘showBlock’ is ‘truthy’.

On line 6, there is a ‘loop’ which renders each property in the ‘details’ object of the data attribute of the ‘app-local’ VM. The special ‘$key’ key is used to render the property identifier, while the ‘value’ key is used to render the property value. Essentially what’s happening is that if there’s any data within the ‘details’ property of ‘app-local’, then that data will be displayed. Remember that because the SPA is built on top of the backend, data will only be available for display if the user has either logged in with a ‘local’ account or has ‘linked’ a ‘local’ profile to his/her account.

Next up, ‘app-facebook’ component.

APP-FACEBOOK COMPONENT

The ‘app-facebook’ component has 4 attributes viz: a) template: this pulls in the template file b) data: holds some useful properties required for displaying ‘facebook’ specific data to the user c) computed: this has 2 properties 1) ‘showBlock’ which is functionally identical to ‘showBlock’ examined in ‘app-local’ and 2) ‘showPost’ which examines the ‘posts’ property of the data attribute to determine the value to return i.e. ‘true’ or ‘false’, the purpose is similar to ‘showBlock’. d) methods: this attribute holds a single property which is a DOM event handler responsible for making an ‘AJAX’ call to the backend to retrieve the most recent facebook posts by the user.

As usual, the UI is presented by the template.

APP-FACEBOOK TEMPLATE

Line 3 holds the ‘v-if’ directive mapped to the value of ‘showBlock’, while line 12 holds the ‘v-show’ directive mapped to the value of ‘showPosts’.

On line 6 there’s a ‘loop’ to render the content of the ‘details’ property of the ‘app-facebook’ data attribute, line 9 holds the directive which handles the ‘click’ event and maps it to the ‘getPosts’ DOM event handler of ‘app-facebook’. Line 11 has a ‘v-show’ directive mapped to the ‘showLoading’ property of the data attribute, notice that on line 30 of the ‘app-facebook’ component, within the ‘getPosts’ event handler, the value of ‘showLoading’ is set to ‘true’ before the ‘AJAX’ call is made, this provides nice visual feedback to the user that something has happened in response to the click on the button, furthermore on lines 33 and 39 when there’s a response (data or error), ‘showLoading’ is set to ‘false’… nice!

Line 13 holds the directive that ‘loops’ over the received data and renders each item. Lines 15 and 16 contain directives o conditionally display certain post elements based on whether or not they are ‘truthy’.

Next, ‘app-twitter’.

APP-TWITTER COMPONENT

The structure of the ‘app-twitter’ component is similar to ‘app-facebook’ i.e. 4 attributes with similar functionality. The major difference between them is in the actual work the different functions do e.g. ‘ getTweets’ hitting the twitter API vs ‘getPosts’ hitting the facebook Graph API. The templates are also similar.

APP-TWITTER TEMPLATE

Having covered both the Vue base VM and the Vue component VMs we’ve essentially reviewed the SPA built on the ‘/dashboard’ route of the Express app. We will now review some of the ‘supporting’ magic that makes all this possible starting with browserify.

BROWSERIFY

According to the ‘Browserify Handbook’, browserify is a ‘tool’ for compiling ‘node flavored’ CommonJS modules for the browser! In essence this allows us to take advantage of Node’s module system to developed modularized apps built around isolated modules which can be pulled together to deliver some desired functionality over the browser… very cool.

Browserify is super easy to setup, simply install globally using npm. To use it in our app, particularly with Vue.js, we need the ‘partialify’ transform which allows ‘.html’ files among others to be required within a module and allows browserify to include the .html file as a valid dependency in the dependency tree which browserify builds for the app.

Essentially browserify starts from our entry point (in this case app.js) and recursively builds a dependency tree driven by all the ‘require’ statements of the app (i.e. the SPA portion) and outputs all these files as a single concatenated file in our case the ‘main.js’ script on line 95 of dashboard.ejs.

But browserify doesn’t work alone, we also need the magic of npm to get the full bang.

npm AS A TASK RUNNER

There are several good task runners out in JS land — Gulp, Grunt etc. I’ve always liked to be as close to the metal as possible, thus i was elated when i found out how to use npm as a task runner.

The key is to create a ‘scripts’ field within package.json and define the tasks to run. Let’s look at package.json again.

On lines 5–10, we create a special field for ‘browserify’, within this field we declare a ‘transform’ key which has an array as it’s value, the only transform we’re using is ‘partialify’. Next on lines 16–19, we define some ‘tasks’ we wish to run with npm within the ‘scripts’ field (line 14).

Line 18 defines a ‘build’ task, we would typically run this during production — it starts up browserify, pulls in the partialify transform and then begins it’s work from the entry point ‘i.e. ./public/js/app.js’ and outputs the result to ‘./public/js/main.js’.

Line 19 defines a ‘watch’ task which uses ‘watchify’ to automatically recompile the browserify output file in response to changes in any files in the dependency tree — this is great for development. Furthermore, the ‘-dv’ flag at the end of the command creates ‘source maps’ so we can cleanly trace bugs to file and line number in the constituent files as well as setting watchify to verbose mode so we can see whenever it’s triggered.

Line 17 has the ‘start-dev’ task, in development we would first run the ‘watch’ task and then the ‘start-dev’ task (in separate terminals) as shown below:

//in terminal 1
npm run watch
//in terminal 2 after watch has compiled the output
npm run start-dev

On line 16, we define a ‘start’ task, this would cause npm to first run the ‘build’ task and then start the app. This task is useful fro a production environment.

The last task we set is ‘test’ (line 15), this leverages a predefined ‘test’ directory (lines 11–13) and allows us to run our tests by simply calling ‘npm test’ or ‘npm run test’!

If you’ve followed my work, you know that while i’m a fan of ‘testing’ i’m not a fan of BDD or TDD or any form of test driven development, i believe we should clearly define the purpose for our app before writing any code, then develop the app to spec before creating tests. I believe that tests should be the crowning piece of our work and they should give us confidence that our app works as expected.

TESTS

For this app, we test the backend interface, the tests are available in the ‘test’ directory simply run ‘npm test’.

That brings us to the end of this post, thank you for reading, please leave comments or reach out to me.

EDIT

Someone asked me why i write so ‘verbosely’, the answer is 2 fold.

  1. When i started out in web development not so long ago, i didn’t find many tutorials which clearly explained why and how something worked in addition to explaining when and what. I decided that when i start writing, i’d ensure that even beginners would be able to follow along and understand the concepts being laid out.
  2. I’, also writing for myself, this is a record for my review of what i know now. As we learn new things we get a bit rusty in things we learnt before, by writing so verbosely i’m ensuring that i’ll always have a record of not just when and what but also how and why.

--

--