Over a million developers have joined DZone.
{{announcement.body}}
{{announcement.title}}

Vue.js 2 Authentication Tutorial, Part 2

DZone's Guide to

Vue.js 2 Authentication Tutorial, Part 2

In Part 2 of our series, we'll dive into the coding of our app, creating the front-end, the back-end, and building a router.

· Web Dev Zone
Free Resource

Start coding today to experience the powerful engine that drives data application’s development, brought to you in partnership with Qlik.

Our App: The Ultimate Startup Battle Ground

The Ultimate Startup Battle Ground

Following our introduction to Vue.js in Part 1, today we will build an app called The Ultimate Startup Battle Ground. Several startups are springing up all over the world. These startups are coming up with innovative technology but have limited funds. Our app hopes to alleviate the issue of funding by providing an up-to-date list of startup battles all over the world with details of sponsors and seed fund amounts. The Ultimate Startup Battle Ground app will display a list of startup battles to the general public.

Interested startup founders can get hold of this list and ensure their team does not miss out on it. However, the app will also provide a list of secret startup battles. This list will only be accessible to registered members.

Note: The secret startup battles have bigger sponsors. 

Build The Back-End

Let's build an API to serve the list of startup battles to our app. We'll quickly build the API with Node.js. The API is simple. This is what we need:

  • An endpoint to serve public startup battles - /api/battles/public.
  • An endpoint to serve secret startup battles - /api/battles/private.
  • Secure the endpoint that serves secret startup battles, so that it can only be accessed by registered users.

Go ahead and fetch the Node.js backend from GitHub.

Note: We'll be securing the backend with Auth0, so make sure you have an account already or sign up for one.

Your server.js should look like this:

'use strict';

const express = require('express');
const app = express();
const jwt = require('express-jwt');
const jwks = require('jwks-rsa');
const cors = require('cors');
const bodyParser = require('body-parser');

app.use(bodyParser.json());
app.use(bodyParser.urlencoded({ extended: true }));
app.use(cors());

const authCheck = jwt({
  secret: jwks.expressJwtSecret({
        cache: true,
        rateLimit: true,
        jwksRequestsPerMinute: 5,
        jwksUri: "https:///.well-known/jwks.json"
    }),
    // This is the identifier we set when we created the API
    audience: '{YOUR-API-AUDIENCE-ATTRIBUTE}',
    issuer: "https://{YOUR-AUTH0-DOMAIN}.auth0.com/",
    algorithms: ['RS256']
});

app.get('/api/battles/public', (req, res) => {
  let publicBattles = [
    // Array of public battles
  ];

  res.json(publicBattles);
})

app.get('/api/battles/private', authCheck, (req,res) => {
  let privateBattles = [
    // Array of private battles
  ];

  res.json(privateBattles);
})

app.listen(3333);
console.log('Listening on localhost:3333');

Check out the full server.js file here.

Note: Your YOUR-AUTH0-DOMAIN should be replaced with your auth0 domain.

Your package.json file should look like this:

{
  "name": "startup-battle",
  "version": "0.0.1",
  "description": "",
  "main": "server.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1",
    "start": "node server.js",
    "dev": "nodemon server.js"
  },
  "author": "Auth0",
  "license": "MIT",
  "dependencies": {
    "body-parser": "^1.15.2",
    "cors": "^2.8.1",
    "express": "^4.14.0",
    "express-jwt": "^3.4.0",
    "jwks-rsa": "^1.1.1"
  }
}

Note: Make sure you have nodemon installed globally.

Once you have cloned the project, run an npm install, then use postman to serve your routes like so:

API serving public startup battlesAPI serving public startup battles

API serving private startup battlesAPI serving private startup battles

The public startup battles endpoint should be http://localhost:3333/api/battles/public.

The private startup battles endpoint should be http://localhost:3333/api/battles/private.

Don't worry about the middleware in charge of securing our endpoint for now. We'll deal with that later. Now, let's build our frontend with Vue.js 2.

Build The Front-End With Vue.js 2

In the early days of Vue.js, there was no particular tool recommended or common way to set up a Vue.js app. However, there is a tool now for scaffolding your Vue.js apps. It's called the Vue.js CLI tool. It's being maintained by the Vue.js team.

Go ahead and install the vue-cli tool globally like so:

npm install -g vue-cli


InstallationYou will be greeted with a series of questions

After installing globally, go ahead and scaffold a new Vue.js 2 app like so:

vue init webpack ultimate-startup-battle

Note: In this context, webpack is a template. You can actually select the template you want vue-cli to scaffold your app with. Another alternative is to use browserify. Check out the list of templates here.

Move into the new directory, ultimate-startup-battle, and run npm installto install all the dependencies required for your app.

Now run npm run dev from your terminal to start up your app. It should automatically open up your web browser at http://localhost:8080 and serve your new app.

App recently scaffolded and showing at Localhost

Let's check out the structure of our newly scaffolded app.

Scaffolded App

ultimate-startup-battle/
  build/ - All build files are here
  config/ - All environment config files are here
  node_modules/ - All the packages required for the vuejs app resides here
  src/
    - assets - All assets reside here
    - components - All our Vue components resides here
    - router - Our router is defined here
    - App.vue - The Parent component
    - main.js - Starting point for our app where the router, template and App component are bound to the root app div
  static/ - contains static files
  .babelrc
  .editorconfig
  .eslintignore
  .eslintrc.js
  .gitignore
  .postcssrc.js
  index.html - Index file that declares the root div where the App component is been bound to
  package.json - File that contains the names of all the packages residing in node_modules folder
  README.md
  node_modules/ - All the packages required for the react app resides here
  package.json - File that contains the names of all the packages residing in node_modules folder


We will work with this structure but make some few modifications.

Note: We are not writing any tests for this application. It's out of the scope of this tutorial. So during the installation, I opted out by choosing the no option.

Make the following modifications like so:

  • Create a privateBattles.vue file inside the components directory. This component will take care of fetching the private startup battles and displaying them to the user.
  • Create a publicBattles.vue file inside the components directory. This component will take care of fetching the public startup battles and displaying them to the user.
  • Create a AppNav.vue file inside the components directory. This component will be in charge of our navigation throughout the app.
  • Create a folder called utils. This will house our helper functions.

Fetch the API Data

The first thing we need to do is to fetch the API data from our Node backend to display in our app. Make sure the Node server is running.

Let's create a helper file to handle fetching the API. Create a battles-api.jsfile inside the utils directory.

Open up the file and add code to it like so:

import axios from 'axios';

const BASE_URL = 'http://localhost:3333';

export {getPublicStartupBattles, getPrivateStartupBattles};

function getPublicStartupBattles() {
  const url = `${BASE_URL}/api/battles/public`;
  return axios.get(url).then(response => response.data);
}

function getPrivateStartupBattles() {
  const url = `${BASE_URL}/api/battles/private`;
  return axios.get(url).then(response => response.data);
}


battles-api.js

Note: Install axios in your app by running npm install axios --save.

We are using a very good promise based HTTP client, axios. An alternative for this is superagent.

In the getPublicStartupBattles and getPrivateStartupBattles functions, axios fetches data from the API endpoints. Then we do this: export {getPublicStartupBattles, getPrivateStartupBattles}; to make them ready for use in our components.

Build the Nav Component

The AppNav.vue file is our Nav component. Go ahead and add code to it like so:

<template>
  <nav class="navbar navbar-default">
    <div class="navbar-header">
      <router-link to="/" class="navbar-brand"> The Ultimate Startup Battle Ground</router-link>
    </div>
    <ul class="nav navbar-nav navbar-right">
      <li>
        <button class="btn btn-danger log" @click="handleLogout()">Log out </button>
        <button class="btn btn-info log" @click="handleLogin()">Log In</button>
      </li>
    </ul>
  </nav>
</template>

<script>
import { isLoggedIn, login, logout } from '../../utils/auth';

export default {
  name: 'app-nav',
  methods: {
    handleLogin() {
      login();
    },
    handleLogout() {
      logout();
    },
    isLoggedIn() {
      return isLoggedIn();
    },
  },
};
</script>

<!-- Add "scoped" attribute to limit CSS to this component only -->
<style scoped>
.navbar-right { margin-right: 0px !important}

.log {
  margin: 5px 10px 0 0;
}
</style>

The router-link Component from vue-router enables seamless client-side transition between routes without any page reload.

Build the Publicbattles and Privatebattles Component

By default, these two components will look similar in functionalities. They both display data from different endpoints. Let's start with the PublicBattlescomponent.

<template>
  <div>
    <app-nav></app-nav>
    <h3 class="text-center">Daily Startup Battles</h3>
    <hr/>

    <div class="col-sm-4" v-for="battle in publicBattles">
      <div class="panel panel-default">
        <div class="panel-heading">
          <h3 class="panel-title">  </h3>
        </div>
        <div class="panel-body">
          <p><span class="badge alert-info"> Sponsor: </span>  </p>
          <p><span class="badge alert-danger"> SeedFund: </span><strong> $ </strong></p>
        </div>
      </div>
    </div>
  
    <div class="col-sm-12">
      <div class="jumbotron text-center">
        <h2>View Private Startup Battles</h2>
        <router-link class="btn btn-lg btn-success" to="/private-battles">Private Startup Battles</router-link>
      </div>
    </div>
  </div>
</template>

<script>
import AppNav from './AppNav';
import { isLoggedIn } from '../../utils/auth';
import { getPublicStartupBattles } from '../../utils/battles-api';

export default {
  name: 'publicBattles',
  components: {
    AppNav,
  },
  data() {
    return {
      publicBattles: '',
    };
  },
  methods: {
    isLoggedIn() {
      return isLoggedIn();
    },
    getPublicStartupBattles() {
      getPublicStartupBattles().then((battles) => {
        this.publicBattles = battles;
      });
    },
  },
  beforeMount() {
    this.getPublicStartupBattles();
  },
};
</script>

<!-- Add "scoped" attribute to limit CSS to this component only -->
<style scoped>
</style>


publicBattles.vue

Let's analyze the code above. The publicBattles component is pulling data from an API, so it needs a way of holding that data. Vue.js has a data method where you can define properties to hold your data as some form of state. In the code above, we declared a publicBattles property.

The methods property also comes by default with Vue.js. In this property, you can define custom logic as functions within this property. So we defined isLoggedIn and getPublicStartupBattles functions.

In the getPublicStartupBattles method, we call the getPublicStartupBattles method we exported from the battles-api.jshelper file and set state as seen below:

...
 getPublicStartupBattles() {
      getPublicStartupBattles().then((battles) => {
        this.publicBattles = battles;
      });
  },
...

Now, we took advantage of one of the Vue.js 2 lifecycle hooks, beforeMount. Whatever is defined in this method is applied just after a component is mounted on the browser screen. So, we invoked the getPublicStartupBattles method in the hook as seen below:

...
 beforeMount() {
    this.getPublicStartupBattles();
  }
...

All we are trying to do is tell Vue.js to load the data from the API just before the publicBattles component gets rendered.

We imported the AppNav component and registered it under the componentsproperty. The name property has a value of publicBattles. What that simply means is this: if we need to use this component in a template, then we would have it as <publicBattles></publicBattles>.

Let's take a good look at what is enclosed in the <template> tag. This is what is rendered on the screen.

We looped through the publicBattles property, which is now an array, with the help of the v-for inbuilt directive to display the contents on the screen.

<div class="col-sm-4" v-for="battle in publicBattles">
    <div class="panel panel-default">
      <div class="panel-heading">
        <h3 class="panel-title"> {{ battle.name }} </h3>
      </div>
      <div class="panel-body">
        <p><span class="badge alert-info"> Sponsor: </span> {{ battle.sponsor }} </p>
        <p><span class="badge alert-danger"> SeedFund: </span><strong> ${{ battle.seedFund }} </strong></p>
      </div>
    </div>
</div>
...
  data() {
    return {
      publicBattles: '',
    };
  },
...


That's the publicBattles property right there. Vue.js automatically binds it to the DOM. So, we can just use it in the <template> tag.

Now, let's build the PrivateBattles component in the same way:

<template>
  <div>
    <app-nav></app-nav>
    <h3 class="text-center">Secret Startup Battles</h3>
    <hr/>
    
    <div class="col-sm-4" v-for="battle in privateBattles">
      <div class="panel panel-danger">
        <div class="panel-heading">
          <h3 class="panel-title"> {{ battle.name }} </h3>
        </div>
        <div class="panel-body">
          <p><span class="badge alert-info"> Sponsor: </span> {{ battle.sponsor }} </p>
          <p><span class="badge alert-danger"> SeedFund: </span><strong> ${{ battle.seedFund }} </strong></p>
        </div>
      </div>
    </div>
    
    <div class="col-sm-12">
      <div class="jumbotron text-center">
        <h2>View Public Startup Battles</h2>
        <router-link class="btn btn-lg btn-success" to="/"> Public Startup Battles </router-link>
      </div>
    </div>
  </div>
</template>

<script>
import AppNav from './AppNav';
import { isLoggedIn } from '../../utils/auth';
import { getPrivateStartupBattles } from '../../utils/battles-api';

export default {
  name: 'privateBattles',
  components: {
    AppNav,
  },
  data() {
    return {
      privateBattles: '',
    };
  },
  methods: {
    isLoggedIn() {
      return isLoggedIn();
    },
    getPrivateStartupBattles() {
      getPrivateStartupBattles().then((battles) => {
        this.privateBattles = battles;
      });
    },
  },
  beforeMount() {
    this.getPrivateStartupBattles();
  },
};
</script>

<!-- Add "scoped" attribute to limit CSS to this component only -->
<style scoped>
</style>

privateBattles.vue

Give yourself a pat on the back because you have successfully created the AppNavPublicBattles, and PrivateBattles components. Whoop! Whoop!

We need to take care of one more thing so that our app can function. Routing!!!

Build the Router

Open up the src/router/index.js file, this is where the vue router is defined. So modify the code like so:

import Vue from 'vue';
import Router from 'vue-router';
import PrivateBattles from '@/components/privateBattles';
import PublicBattles from '@/components/publicBattles';

Vue.use(Router);

export default new Router({
  mode: 'history',
  routes: [
    {
      path: '/',
      name: 'PublicBattles',
      component: PublicBattles,
    },
    {
      path: '/private-battles',
      name: 'PrivateBattles',
      component: PrivateBattles,
    },
  ],
});

index.js

Each route has a path, name, and the component to be rendered when that route is invoked by the user. By the way, we already imported the components at the top of the file.

import Vue from 'vue';
import Router from 'vue-router';
import PrivateBattles from '@/components/privateBattles';
import PublicBattles from '@/components/publicBattles';

Just a few things before we check our application in the browser:

  • Open up index.html in the root directory and add bootstrap. Now the content of the HTML file should look like this:
<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8">
    <title>startupbattle</title>
    <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css">
  </head>
  <body>
    <div id="app"></div>
    <!-- built files will be auto injected -->
  </body>
</html>


Feel free to check out your application in the browser. Right now, you should have something like this:

HomepageHomepage

CelebritypagePrivate Battles Page

Tune in tomorrow for Part 3!

Create data driven applications in Qlik’s free and easy to use coding environment, brought to you in partnership with Qlik.

Topics:
web dev ,vue.js ,front-end ,back-end ,routers

Published at DZone with permission of Prosper Otemuyiwa, DZone MVB. See the original article here.

Opinions expressed by DZone contributors are their own.

THE DZONE NEWSLETTER

Dev Resources & Solutions Straight to Your Inbox

Thanks for subscribing!

Awesome! Check your inbox to verify your email so you can start receiving the latest in tech news and resources.

X

{{ parent.title || parent.header.title}}

{{ parent.tldr }}

{{ parent.urlSource.name }}