Vue Router Navigation Guards with Vuex

Part 36 of 48 in API Driven Development With Laravel and VueJS
Dan Pastori avatar
Dan Pastori January 8th, 2018
⚡️ Updated content is available We learned a lot since we originally wrote this article. We now have this updated for Laravel 10, Vue 3/NuxtJS 3, and Capacitor 3.

In the last two tutorials we opened up some data publicly on the API side of the application https://serversideup.net/public-private-api-laravel/ and blocked a few actions based on whether the use was authenticated or not https://serversideup.net/clean-vuejs-public-private-routes/. In this tutorial we will expand on blocking off certain features of our application on the front end side using Vue Router Navigation Guards. These occur before the route is rendered and determine if the user has access to the page.

For more information on Vue Router navigation guards check out the documentation here: Navigation Guards · vue-router

At first, we only allowed access through PHP. If the user was authenticated they could see everything in the application. Now we need to allow some pages to be accessible if the user is authenticated or not. The trick we will need to do with these navigations is ensure that we have actually loaded the user to determine if there is one authenticated or not. We luckily can watch certain Vuex state variables so we can wait to see if the user has been loaded before determining access. Let’s get started!

Step 1: Build requireAuth() method

The first thing we will be doing is making a method named requireAuth(). This method will determine if the current user has access to certain pages in the application.

First, open up the /resources/assets/js/routes.js file. Before we write our method, we will need import our Vuex data store into the file:

import store from './store.js';

Now we have access to our Vuex data store which we will be using in our requireAuth() method.

Now, let’s stub out our method. Right below the line that extends Vue to use VueRouter, add the following code:

/*
    This will cehck to see if the user is authenticated or not.
*/
function requireAuth (to, from, next) {

}

This method gets injected with three parameters: to being the route we are navigating to, from being the route we are navigating from, and next being the method we use to navigate to the next route. We will be calling this method when we determine if the user has access or not.

First we will add a function inside of our requireAuth() function that determines where the user will go. This method is called proceed():

/*
  Determines where we should send the user.
*/
function proceed () {
  /*
    If the user has been loaded determine where we should
    send the user.
  */
  if ( store.getters.getUserLoadStatus() == 2 ) {
    /*
      If the user is not empty, that means there's a user
      authenticated we allow them to continue. Otherwise, we
      send the user back to the home page.
    */
    if( store.getters.getUser != '' ){
      next();
    }else{
      next('/home');
    }
  }
}

At first, what we will do is check to see if the store (our Vuex store we imported) has loaded our user. This is a check to ensure we aren’t redirecting to the wrong place. Next, we will check from the store by getting the user. If the user is not null, we allow navigation to the next route otherwise we redirect the user home.

When we finish our loading, we will run the proceed() method and determine where the user will be sent.

Below the proceed() method in the requireAuth() method, add the following code:

/*
  Confirms the user has been loaded
*/
if ( store.getters.getUserLoadStatus != 2 ) {
  /*
    If not, load the user
  */
  store.dispatch( 'loadUser' );

  /*
    Watch for the user to be loaded. When it's finished, then
    we proceed.
  */
  store.watch( store.getters.getUserLoadStatus, function(){
        if( store.getters.getUserLoadStatus() == 2 ){
            proceed();
        }
  });
} else {
  /*
    User call completed, so we proceed
  */
  proceed()
}

This is the guts of our method. With this, we check to see if the user has been loaded. If the user has, we call our proceed() method right away. Otherwise we dispatch a request to load the user. We do this in case the user hasn’t been loaded yet.

Next, we apply a watcher to our store. This is the key to making this method work. We don’t want to do anything until we receive a successful callback form the API. Otherwise we are caught in limbo on if there is a user or not.

To apply a watcher, we need to pass it a method to watch and a function to call when successful.

For this, we added the code:

/*
  Watch for the user to be loaded. When it's finished, then
  we proceed.
*/
store.watch( store.getters.getUserLoadStatus, function(){
  if( store.getters.getUserLoadStatus() == 2 ){
        proceed();
    }
});

We watch the getUserLoadStatus method on the store’s getters and we pass it a method that simply calls our proceed() function. What this does is when the status changes, we check to see if the load status is equal to 2 which is a successful response from the database. We then determine where to send the user with the proceed() method.

Step 2: Change the getUserLoadStatus to return a method

Now the key to making this method work is we have to adjust our /resources/assets/js/modules/users.js module to return a method instead of just the state for the getUserLoadStatus() getter. This allows us to apply our watcher to the store for that piece of state. To do this, we simply wrap our return statement to return a function:

/*
  Returns the user load status.
*/
getUserLoadStatus( state ){
  return function(){
    return state.userLoadStatus;
  }
},

We just have to adjust the computed properties in our components to use the method instead of the piece of state.

To do this we need to open up our /resources/assets/js/components/global/Navigation.vue component and the /resources/assets/js/pages/Cafe.vue page.

Each of these Vue components has a computed property that needs to be updated to call the method instead of just reference the piece of state.

We need to change the return on the userLoadStatus() to be:

/*
  Retrieves the User Load Status from Vuex
*/
userLoadStatus(){
  return this.$store.getters.getUserLoadStatus();
},

This actually calls the method which returns the piece of state we want. Now we can apply our newly created requireAuth() method to our routes!

Step 3: Apply requireAuth method

If we open up our /resources/assets/js/routes.js file we can now add our requireAuth() method as a navigation guard to a few routes. To do this, we will need to hook into the beforeEnter hook which will call before we begin navigation.

If we take a look at our routes, there are only two routes that need this guard right now, these are the /cafes/:id/edit and /cafes/new routes. We don’t want unauthenticated users to access these pages. Like I mentioned before, since it’s Javascript there’s possibly a way around this code and users can inject their own code, but we also have these endpoints secured through the API so it’s more of a UX type deal.

To add the guard, simply add this key value below the component definition:

beforeEnter: requireAuth

Our cafes/new route will look like this:

{
  path: 'cafes/new',
  name: 'newcafe',
  component: Vue.component( 'NewCafe', require( './pages/NewCafe.vue' ) ),
  beforeEnter: requireAuth
},

And our cafes/:id/edit route will look like this:

{
  path: 'cafes/:id/edit',
  name: 'editcafe',
  component: Vue.component( 'EditCafe', require( './pages/EditCafe.vue' ) ),
  beforeEnter: requireAuth
},

Before we enter the page we will run the requireAuth method to determine if the user can navigate or not to the specified route.

Pretty slick, eh? That’s the power of Vuex and Vue Router for handling the frontend of the SPA. There’s a lot of other cool features we can add to our requireAuth() method and a whole bunch of navigation guards we can hook into for added functionality.

For more information on all of these navigation guards, refer to the documentation here: Navigation Guards · vue-router.

Conclusion

In the last couple tutorials we’ve opened up public data for roastandbrew.coffee. We’ve also implemented our first front end navigation guard and threw it in a reusable requireAuth() method. This way any other secure routes such as user profiles, friend listings, etc can be protected from unauthorized access.

Our next steps will be to add some social OG meta data now that we have a public facing application and some possible friend integrations!

For now, check out the code for all of our updates on Github: GitHub – serversideup/roastandbrew: Helping the coffee enthusiast find their next cup of coffee. Also, helping aspiring web and mobile app developers build a single page app and converting it to a mobile hybrid. All tutorials can be found at https://serversideup.net

Keep Reading
View the course View the Course API Driven Development With Laravel and VueJS
Up Next → Adding Laravel User Profiles

Support future content

The Ultimate Guide to Building APIs and Single-Page Applications with Laravel + VueJS + Capacitor book cover.

Psst... any earnings that we make off of our book is being reinvested to bringing you more content. If you like what you read, consider getting our book or get sweet perks by becoming a sponsor.

Written By Dan

Dan Pastori avatar Dan Pastori

Builder, creator, and maker. Dan Pastori is a Laravel certified developer with over 10 years experience in full stack development. When you aren't finding Dan exploring new techniques in programming, catch him at the beach or hiking in the National Parks.

Like this? Subscribe

We're privacy advocates. We will never spam you and we only want to send you emails that you actually want to receive. One-click unsubscribes are instantly honored.