Understanding Vue middleware pipelines
Tags: vue
Published: 2019-07-08 20:20:54

---
title: Understanding Vue middleware pipelines
published: true
tags: vue
canonical_url: https://blog.logrocket.com/vue-middleware-pipelines/
---

![](https://thepracticaldev.s3.amazonaws.com/i/ig8gnz0b1n5x3smm7b6z.png)

Generally, when building SPAs there’s a need to protect certain routes. For example, let’s say we have a dashboard route that can only be accessed by authenticated users, we can make use of an auth middleware to ensure that only authenticated users have access to the dashboard route.

In this tutorial, we’ll see how we can implement a middleware pipeline for a Vue app using [Vue-Router](https://router.vuejs.org/).

## What is a middleware pipeline?

**A middleware pipeline** is a stack of different middlewares ran in parallel with each other.

Using our hypothetical case study from the introductory paragraph, let’s say we have another route at `/dashboard/movies`, that we only want accessible to subscribed users. We already know that to access the dashboard route, you need to be authenticated. How then do we go about protecting the `/dashboard/movies` route to ensure that only authenticated and subscribed users have access to that route? By using a middleware pipeline, we can chain several middlewares together and ensure they are run in parallel.

## **Getting started**

To get started, we’ll be using the [Vue CLI](https://cli.vuejs.org/) to quickly scaffold a new Vue project.

```jsx
vue create vue-middleware-pipeline
```

## Installing dependencies

Once the project directory has been created and installed, navigate to the newly created directory and run the following command from the terminal:

```jsx
npm i vue-router vuex
```

[Vue-router ](https://github.com/vuejs/vue-router/)—  is the official router for Vue.js

[Vuex](https://vuex.vuejs.org/) —  is a state management library for Vue

## Creating components

Our application is going to have three components.

**Login ** — This component will be displayed to users that have not been authenticated.

**Dashboard**  — This component will be displayed to users that are signed in.

**Movies**  — We’ll display this component to users that are signed in and have an active subscription.

Let’s create those components. Navigate to the `src/components` directory and create the following files: `Dashboard.vue` `Login.vue` and `Movies.vue`

Edit the `Login.vue` file with the following code:

```jsx
<template>
  <div>
    <p>This is the Login component</p>
  </div>
</template>
```

Edit the `Dashboard.vue` file with the following code:

```jsx
<template>
  <div>
    <p>This is the Dashboard component for authenticated users</p>
    <router-view/>
  </div>
</template>
```

Finally, add the following code to the `Movies.vue` file:

```jsx
<template>
  <div>
    <p>This is the Movies component for authenticated and subscribed users</p>
  </div>
</template>
```

## Creating the store

A store, in regards to `Vuex` , is simply a container for holding our application state. This will allow us to determine if a user is authenticated as well as check if the user is subscribed or not.  
Inside the src folder, create a `store.js` file and add the following code to the file:

```jsx
import Vue from 'vue'
import Vuex from 'vuex'

Vue.use(Vuex)


export default new Vuex.Store({
    state: {
        user: {
            loggedIn: false,
            isSubscribed: false
        }
    },

    getters: {
        auth(state) {
            return state.user
        }
    }
})
```

The store contains a `user` object within its state. The `user` object contains a `loggedIn` and `isSubscribed` property which helps us determine if a user is logged in and has an active subscription. We’ve also defined a `getter` inside the store that returns the `user` object.

[![LogRocket Free Trial Banner](https://i0.wp.com/blog.logrocket.com/wp-content/uploads/2017/03/f760c-1gpjapknnuyhu8esa3z0jga.png?resize=1200%2C280&ssl=1)](https://logrocket.com/signup/)

## Defining our routes

Before we create our routes, let’s define them and associate the respective middlewares that will be attached to each of these routes.

`/login` will be accessible to everybody except authenticated users. When authenticated users visit this route, they should be redirected to the dashboard route. This route will have a `guest` middleware attached to it.

`/dashboard` will only be accessible to authenticated users. Unauthenticated users should be redirected to the `/login` route when they visit this route. We’ll associate an `auth` middleware with this route.

`/dashboard/movies` will be accessible to only authenticated and subscribed users. This route will be protected by a `isSubscribed` and `auth` middleware.

## Creating the routes

Next, create a `router` folder in the `src` directory and then create a `router.js` file inside that folder. Edit the file with the following code:

```jsx
import Vue from 'vue'
import Router from 'vue-router'
import store from '../store'

import Login from '../components/Login'
import Dashboard from '../components/Dashboard'
import Movies from '../components/Movies'


Vue.use(Router)

const router = new Router({
    mode: 'history',
    base: process.env.BASE_URL,
    routes: [
        {
            path: '/login',
            name: 'login',
            component: Login
        },

        {
            path: '/dashboard',
            name: 'dashboard',
            component: Dashboard,
            children: [{
                path: '/dashboard/movies',
                name: 'dashboard.movies',
                component: Movies
            }
        ],
        }
    ]
})


export default router
```

Here, we created a new `router` instance while passing in a couple of configuration options as well as a `routes` property which accepts all the routes we defined earlier. At the moment, it’s important to note that none of these routes are protected. We’ll fix that shortly.  
Next, let’s inject the router and our store into the Vue instance. Edit the `src/main.js` file with the code below:

```jsx
import Vue from 'vue'
import App from './App.vue'
import router from './router/router'
import store from './store'

Vue.config.productionTip = false


new Vue({
  router,
  store,
  render: h => h(App),
}).$mount('#app')
```

## Creating the middlewares

In the `src/router` directory, create a `middleware` folder and then create `guest.js` ,`auth.js` and `IsSubscribed.js` file under that particular folder. Add the following code to the `guest.js` file:

```jsx
export default function guest ({ next, store }){
    if(store.getters.auth.loggedIn){
        return next({
           name: 'dashboard'
        })
    }
   
    return next()
   }
```

The `guest` middleware checks to see if the user is authenticated. If they’re authenticated, they are redirected to the `dashboard` route.

Next, edit the `auth.js` file with the following code:

```jsx
export default function auth ({ next, store }){
 if(!store.getters.auth.loggedIn){
     return next({
        name: 'login'
     })
 }

 return next()
}
```

In the `auth` middleware, using the `store` we check to see whether the user is currently `authenticated`. Depending on if the user is logged in or not, we either continue with the request or redirect them to the login page.

Edit the `isSubscribed.js` file with the following code:

```jsx
export default function isSubscribed ({ next, store }){
    if(!store.getters.auth.isSubscribed){
        return next({
           name: 'dashboard'
        })
    }
   
    return next()
   }
```

The `isSubscribed` middleware is similar to the `auth` middleware. Using our `store` we check to see if the user is subscribed. If the user is subscribed, they can access the intended route or else they are redirected back to the dashboard page.

## Protecting the routes

Now that we’ve created all our middlewares, let’s make use of them to protect our routes. Edit the `src/router/router.js` file with the following code:

```jsx
import Vue from 'vue'
import Router from 'vue-router'
import store from '../store'

import Login from '../components/Login'
import Dashboard from '../components/Dashboard'
import Movies from '../components/Movies'

import guest from './middleware/guest'
import auth from './middleware/auth'
import isSubscribed from './middleware/isSubscribed'


Vue.use(Router)

const router = new Router({
    mode: 'history',
    base: process.env.BASE_URL,
    routes: [{
            path: '/login',
            name: 'login',
            component: Login,
            meta: {
                middleware: [
                    guest
                ]
            }
        },

        {
            path: '/dashboard',
            name: 'dashboard',
            component: Dashboard,
            meta: {
                middleware: [
                    auth
                ]
            },
            children: [{
                path: '/dashboard/movies',
                name: 'dashboard.movies',
                component: Movies,
                meta: {
                    middleware: [
                        auth,
                        isSubscribed
                    ]
                }
            }],
        }
    ]
})


export default router
```

Here, we imported all our middlewares and then for each of the routes we defined a meta field that contains a middleware array. The middleware array contains all the middlewares we want to be associated with a particular route.

## Vue Router navigation guards

The [navigation guards](https://router.vuejs.org/guide/advanced/navigation-guards.html#global-before-guards) provided by Vue Router are the only reason why we can use a middleware to protect our routes in the first place. These navigation guards are primarily used to guard routes either by redirecting or canceling it.

One of those guards is the global before guard which is usually a hook called just before a route is triggered. To register a global before guard, we define a `beforeEach` method on the `router` instance.

```jsx
const router = new Router({ ... })
router.beforeEach((to, from, next) => {
 //necessary logic to resolve the hook
})
```

The `beforeEach` method receives three arguments:

`to:` This is the route we intend on accessing.

`from:` This is the route we’re currently coming from.

`next:` This is the `function` called to resolve the hook.

## Running the middleware

Using the `beforeEach` hook, we can run our middleware.

```jsx
const router = new Router({ ...})

router.beforeEach((to, from, next) => {
    if (!to.meta.middleware) {
        return next()
    }
    const middleware = to.meta.middleware

    const context = {
        to,
        from,
        next,
        store
    }
    return middleware[0]({
        ...context
    })
})
```

We first check to see if the current route being processed has a meta field that contains a `middleware` property. If the `middleware` property is found, we assign it to a `const` variable. Next, we define a `context` object which contains everything we’ll need to pass to each of the middleware. We then call the very first middleware in the middleware array as a function while passing in the `context` object.

Try visiting the `/dashboard` route, you should be redirected to the `login` route. This is because of the `store.state.user.loggedIn` property in our `/src/store.js` is set to `false`. Change the `store.state.user.loggedIn` property to true and you should be able to access the `/dashboard` route now.

Our middleware is working now, but not quite the way we want it to. Our goal was to achieve a pipeline where we can run multiple middlewares against a particular route.

```jsx
return middleware[0]({ …context})
```

Notice this line of code from the code block above, we’re only calling the first piece of middleware passed from the middleware array in the `meta` field. How then do we ensure other middlewares contained in the array, if any, are called too? This where our pipeline comes in handy.

## Creating the pipeline

Navigate to the `src/router` directory and then create a `middlewarePipeline.js` file. Add the following code to the file:

```jsx
function middlewarePipeline (context, middleware, index) {
    const nextMiddleware = middleware[index]

    if(!nextMiddleware){
        return context.next 
    }

    return () => {
        const nextPipeline = middlewarePipeline(
            context, middleware, index + 1
        )

        nextMiddleware({ ...context, next: nextPipeline })

    }
}

export default middlewarePipeline
```

The `middlewarePipeline` takes in three arguments:

`context:` This is the `context` object we created earlier on so that it can be passed to each piece of middleware in the stack.

`middleware:` This is the `middleware` array itself as defined on the `meta` field of the `route`.

`index:` This is the `index` of the current middleware being run in the `middleware` array.

```jsx
const nextMiddleware = middleware[index]
if(!nextMiddleware){
return context.next
}
```

Here, we simply pluck out the middleware at the `index` that was passed to the `middlewarePipeline` function. If no `middleware` is found at that `index` , the default `next` callback is returned.

```jsx
return () => {
const nextPipeline = middlewarePipeline(
context, middleware, index + 1
)
nextMiddleware({ ...context, next: nextPipeline })
}
```

We invoke the `nextMiddleware` to be run while passing in the `context` and then the `nextPipeline` `const` . It is important to note that the `middlewarePipeline` function is a recursive function that will call itself to fetch the next middleware to run in the stack, while incrementing the `index`by 1.

## Bringing it all together

Let’s make use of our `middlewarePipeline` . Edit the `src/router/router.js`file with the following code:

```jsx
import Vue from 'vue'
import Router from 'vue-router'
import store from '../store'

import Login from '../components/Login'
import Dashboard from '../components/Dashboard'
import Movies from '../components/Movies'

import guest from './middleware/guest'
import auth from './middleware/auth'
import isSubscribed from './middleware/isSubscribed'
import middlewarePipeline from './middlewarePipeline'


Vue.use(Router)

const router = new Router({
    mode: 'history',
    base: process.env.BASE_URL,
    routes: [{
            path: '/login',
            name: 'login',
            component: Login,
            meta: {
                middleware: [
                    guest
                ]
            }
        },

        {
            path: '/dashboard',
            name: 'dashboard',
            component: Dashboard,
            meta: {
                middleware: [
                    auth
                ]
            },
            children: [{
                path: '/dashboard/movies',
                name: 'dashboard.movies',
                component: Movies,
                meta: {
                    middleware: [
                        auth,
                        isSubscribed
                    ]
                }
            }],
        }
    ]
})

router.beforeEach((to, from, next) => {
    if (!to.meta.middleware) {
        return next()
    }
    const middleware = to.meta.middleware

    const context = {
        to,
        from,
        next,
        store
    }


    return middleware[0]({
        ...context,
        next: middlewarePipeline(context, middleware, 1)
    })

})

export default router
```

Here, we’re making use of the `middlewarePipeline` to run subsequent middlewares contained in the stack.

```jsx
return middleware[0]({
...context,
next: middlewarePipeline(context, middleware, 1)
})
```

After the first middleware has been called, using the `middlewarePipeline` function, subsequent middlewares contained in the stack are also called, till there are no longer any middlewares available.

If you visit the `/dashboard/movies` route, you should be redirected to the `/dashboard` route. This is because the `user` is currently `authenticated` but has no active subscription. Set the `store.state.user.isSubscribed` property in our `store` to `true` . You should be able to access the `/dashboard/movies`route now.

## Conclusion

Middlewares are a great way of protecting different routes in an application. This is a very simple implementation of how you can make use of multiple middlewares to protect a single route in your Vue application. You can find the link to the Github repo [here](https://github.com/Dotunj/vue-middleware-pipelines).

* * *

## Plug: [LogRocket](https://logrocket.com/signup/), a DVR for web apps
 
[![LogRocket Dashboard Free Trial Banner](https://i2.wp.com/blog.logrocket.com/wp-content/uploads/2017/03/1d0cd-1s_rmyo6nbrasp-xtvbaxfg.png?zoom=2&resize=730%2C412&ssl=1)](https://logrocket.com/signup/)
 
[LogRocket](https://logrocket.com/signup/) is a frontend logging tool that lets you replay problems as if they happened in your own browser. Instead of guessing why errors happen, or asking users for screenshots and log dumps, LogRocket lets you replay the session to quickly understand what went wrong. It works perfectly with any app, regardless of framework, and has plugins to log additional context from Redux, Vuex, and @ngrx/store.
 
In addition to logging Redux actions and state, LogRocket records console logs, JavaScript errors, stacktraces, network requests/responses with headers + bodies, browser metadata, and custom logs. It also instruments the DOM to record the HTML and CSS on the page, recreating pixel-perfect videos of even the most complex single-page apps.
 
[Try it for free](https://logrocket.com/signup/).
* * *
The post [Understanding Vue middleware pipelines](https://blog.logrocket.com/vue-middleware-pipelines/) appeared first on [LogRocket Blog](https://blog.logrocket.com).