This page looks best with JavaScript enabled

Role-based Data Filters in Vue

 ·   ·  ☕ 4 min read

I would love for the world to wait for a carefully architected VueJS app. But often it doesn’t. I recently had to quickly implement a role-based filter for a data driven application. And, the experience left me somewhat dizzy.

First, the basics -

  1. Roles had been implemented in the application as custom logic - back-end server framework had roles defined against users
  2. Permissions were used to check access to specific routes of API. Front-end app just did not show irrelevant links
  3. We had to implement data-filters - e.g. show only accounts assigned to a sales person, while show all of the company accounts to manager. The rules were defined, but not infinitely scalable like that in a typical CRM app

My first design idea was to find the current user role and apply filter in Vuex.

This was quickly abandoned in favor or tying the functionality to the UI since there could be opportunities for managers to view data “as a” sales rep. The design was changed to use routes and props to individual views. The driving factor was quick implementation and minimal impact to the larger application.

Pass props in router
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
// ./router.js

import Vue from "vue";
import Router from "vue-router";

Vue.use(Router);

export default new Router({
    routes: [
    {
      path: "/my-account",
      name: "my-account",
      component: () => import("./views/Account.vue"),
      props: { authMode: "my" }
    },
    {
      path: "/all-account",
      name: "all-account",
      component: () => import("./views/Account.vue"),
      props: { authMode: "all" }
    },
});
Change views / create new views

The view is changed to receive authMode as prop. Use a single view to fetch data, and display

  • data from server in a list format (in AccountList component)
  • display details of selected ‘active account’ in a form (which is part of the TabPanel component)
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
<!-- Account.vue -->
<template>
  <div class="account">
    <v-layout row wrap>
      <v-flex xs12>
        <!-- Account list -->
        <AccountList></AccountList>
      </v-flex>
      <v-flex xs12>
        <!-- Account list -->
        <TabPanel></TabPanel>
      </v-flex>
    </v-layout>
  </div>
</template>

<script>
  import { mapActions } from "vuex";

  import AccountList from "../components/AccountList";
  import TabPanel from "../components/TabPanel";

  export default {
    data() {
      return {
        detailDialog: false
      };
    },
    components: { AccountList, TabPanel },
    computed: {
      ...mapState("accounts", ["accounts"])
    },
    props: {
      authMode: {
        type: String,
        required: false
      }
    },
    methods: {
      ...mapActions("accounts", ["fetchAccount"])
    },
    mounted() {
      this.fetchAccount({ page: 1, authMode: this.authMode });
    }
  };
</script>
Change store module

The account module in Vuex store will have code to call distinct server APIs based on the filter (initially passed by authMode).

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
// ./store/account.js

//*
// lot of code
//*
export default {
  actions: {
    fetchAccount({ commit, rootState, params }) {
      let { page, query, authMode } = params;

      commit("setLoading", true);
      commit("setDisplayError", null);

      page = page ? page : 1;

      let getStr;
      switch (authMode) {
        case "my":
          getStr = `api/my-accounts?page=${page}`;
          break;

        case "all":
          getStr = `api/all-accounts?page=${page}`;
          break;

        default:
          getStr = `api/accounts?page=${page}`;
      }

      // axios.get(getStr)
    }
  }

  //*
  // lot of code
  //*
}; // export default
Change server APIs

The server will have my and all routes that simply call one service to fetch accounts based on filter.

Ok, what next?

This quick implementation works. The advantages were -

  • Ability to secure server APIs based on roles (filter based on API access, no need to check within API services or controllers)
  • Secure data access with server-controlled filters
  • Reuse single UI for all roles (minimal changes)

But I am not happy with -

  • code repetition in client - can do better
  • lack of role-based security for front-end URLs
  • potentially confusing functionality & spaghetti code if we need to secure updates to specific fields or allow actions based on roles

This may need a larger refactoring using a standard ACL function in the back-end and something similar on the front-end. That is probably for some other day.

Stay in touch!
Share on

Prashanth Krishnamurthy
WRITTEN BY
Prashanth Krishnamurthy
Technologist | Creator of Things