# Creating and Registering a Vuex Module | Full-Stack Google Contacts Clone with AdonisJS Framework (Node.js) and Quasar Framework (Vue.js)

**Author:** Ndianabasi Udonkang  
**Published:** 2021-11-05

In this lesson, we create and register the contacts module for the Vuex store of our app and define all states, getters, actions, and mutations.

---

## Tags

- [JavaScript](/llms/technical-blog/tag/javascript.md)
- [Nodejs](/llms/technical-blog/tag/nodejs.md)
- [Vuejs](/llms/technical-blog/tag/vuejs.md)
- [Adonisjs](/llms/technical-blog/tag/adonisjs.md)
- [Quasar Framework](/llms/technical-blog/tag/quasar-framework.md)

## Part of Series: [Full-Stack Google Contacts Clone with Node.js (Adonisjs Framework) and Vue.js (Quasar Framework)](/llms/technical-blog/series/clnwu9bc991hiu3wo2vwmw45/full-stack-google-contacts-clone-with-node-js-adonisjs-framework-and-vue-js-quasar-framework.md)

This article is part of the **[Full-Stack Google Contacts Clone with Node.js (Adonisjs Framework) and Vue.js (Quasar Framework)](/llms/technical-blog/series/clnwu9bc991hiu3wo2vwmw45/full-stack-google-contacts-clone-with-node-js-adonisjs-framework-and-vue-js-quasar-framework.md)** series.

### Articles in this Series:

- [Introduction to Full-Stack Google Contacts Clone with Node.js (Adonisjs Framework) and Vuejs (Quasar Framework)](/llms/technical-blog/article/xe5mlxg43s9rpx1pxsvfdp3r/introduction-to-full-stack-google-contacts-clone-with-node-js-adonisjs-framework-and-vuejs-quasar-framework.md)
- [Workspace Setup | Full-Stack Google Contacts Clone with Adonis.js/Node.js and Quasar (Vue.js)](/llms/technical-blog/article/fo8str8yjpkz5dygxg875bpw/workspace-setup-full-stack-google-contacts-clone-with-adonis-js-node-js-and-quasar-vue-js.md)
- [Frontend Overview | Full-Stack Google Contacts Clone with Adonis.js/Node.js and Quasar (Vue.js)](/llms/technical-blog/article/nxgmng6fhf1cwjk30coflro2/frontend-overview-full-stack-google-contacts-clone-with-adonis-js-node-js-and-quasar-vue-js.md)
- [The Left Drawer | Full-Stack Google Contacts Clone with Adonis.js/Node.js and Quasar (Vue.js)](/llms/technical-blog/article/m6fagvy2il5h65llxxbbl9os/the-left-drawer-full-stack-google-contacts-clone-with-adonis-js-node-js-and-quasar-vue-js.md)
- [Improve The Header | Full-Stack Google Contacts Clone with Adonis.js/Node.js and Quasar (Vue.js)](/llms/technical-blog/article/jjwbxcg450jmkkcrrmkmwvwj/improve-the-header-full-stack-google-contacts-clone-with-adonis-js-node-js-and-quasar-vue-js.md)
- [New Contact Form Design | Full-Stack Google Contacts Clone with Adonis.js/Node.js and Quasar (Vue.js)](/llms/technical-blog/article/ebguwpkntk3empakunrki4dq/new-contact-form-design-full-stack-google-contacts-clone-with-adonis-js-node-js-and-quasar-vue-js.md)
- [Validating the Contact Form with Vuelidate | Full-Stack Google Contacts Clone with Adonis.js/Node.js and Quasar (Vue.js)](/llms/technical-blog/article/z0xr9m2ljler2cwnpsw2ycwa/validating-the-contact-form-with-vuelidate-full-stack-google-contacts-clone-with-adonis-js-node-js-and-quasar-vue-js.md)
- [Designing the Contacts Table | Full-Stack Google Contacts Clone with Adonis.js/Node.js and Quasar (Vue.js)](/llms/technical-blog/article/h2p1x1z406ib38zu2dci3jur/designing-the-contacts-table-full-stack-google-contacts-clone-with-adonis-js-node-js-and-quasar-vue-js.md)
- [Designing the Contacts Table (Part 2) | Full-Stack Google Contacts Clone with Adonis.js/Node.js and Quasar (Vue.js)](/llms/technical-blog/article/j0iscua9gg8ekuz8ifh6chpn/designing-the-contacts-table-part-2-full-stack-google-contacts-clone-with-adonis-js-node-js-and-quasar-vue-js.md)
- [Refactoring the Main Layout | Full-Stack Google Contacts Clone with Adonis.js/Node.js and Quasar (Vue.js)](/llms/technical-blog/article/uktmlqsivwtr3cvnpld59k9q/refactoring-the-main-layout-full-stack-google-contacts-clone-with-adonis-js-node-js-and-quasar-vue-js.md)
- [Improving User Experience with the Contacts Table | Full-Stack Google Contacts Clone with Adonis.js/Node.js and Quasar (Vue.js)](/llms/technical-blog/article/dzs812auf61ej7qu2cg726dh/improving-user-experience-with-the-contacts-table-full-stack-google-contacts-clone-with-adonis-js-node-js-and-quasar-vue-js.md)
- [Designing the Contact Details Page | Full-Stack Google Contacts Clone with Adonis.js/Node.js and Quasar (Vue.js)](/llms/technical-blog/article/kf9pz97n63fvgfrcy5viwzrg/designing-the-contact-details-page-full-stack-google-contacts-clone-with-adonis-js-node-js-and-quasar-vue-js.md)
- [Creating the Contact Edit Page | Full-Stack Google Contacts Clone with Adonis.js/Node.js and Quasar (Vue.js)](/llms/technical-blog/article/fmr90prj84c9zn0633jc368g/creating-the-contact-edit-page-full-stack-google-contacts-clone-with-adonis-js-node-js-and-quasar-vue-js.md)
- [How Software Backends Work | Full-Stack Google Contacts Clone with AdonisJs (Node.js) and Quasar Framework (Vue.js)](/llms/technical-blog/article/aca8fcrghz70nn1wqg3uzbum/how-software-backends-work-full-stack-google-contacts-clone-with-adonis-js-node-js-and-quasar-framework-vue-js.md)
- [Setting Up The Backend | Full-Stack Google Contacts Clone with AdonisJs (Node.js) and Quasar Framework (Vue.js)](/llms/technical-blog/article/fxb49fmz9ljbwrimbnnzudfv/setting-up-the-backend-full-stack-google-contacts-clone-with-adonis-js-node-js-and-quasar-framework-vue-js.md)
- [Why Choose the AdonisJs Framework? | Full-Stack Google Contacts Clone with AdonisJs (Node.js) and Quasar Framework (Vue.js)](/llms/technical-blog/article/lnhm5oudx34yo2869gmcms37/why-choose-the-adonis-js-framework-full-stack-google-contacts-clone-with-adonis-js-node-js-and-quasar-framework-vue-js.md)
- [Setting Up Our API Server with AdonisJs Framework | Full-Stack Google Contacts Clone with AdonisJs (Node.js) and Quasar Framework (Vue.js)](/llms/technical-blog/article/x1jgmet84aqj9qggu5mulkh0/setting-up-our-api-server-with-adonis-js-framework-full-stack-google-contacts-clone-with-adonis-js-node-js-and-quasar-framework-vue-js.md)
- [Setting Up Postman with the API Server | Full-Stack Google Contacts Clone with AdonisJS (Node.js) and Quasar Framework (Vue.js)](/llms/technical-blog/article/v4qehksislosmuy92qgjr4ct/setting-up-postman-with-the-api-server-full-stack-google-contacts-clone-with-adonis-js-node-js-and-quasar-framework-vue-js.md)
- [The Model-View-Controller Design Pattern in AdonisJS | Full-Stack Google Contacts Clone with AdonisJS (Node.js) and Quasar Framework (Vue.js)](/llms/technical-blog/article/h5wqql65iejop5xrplujw458/the-model-view-controller-design-pattern-in-adonis-js-full-stack-google-contacts-clone-with-adonis-js-node-js-and-quasar-framework-vue-js.md)
- [Create Column Definitions & Insert New Contacts | Full-Stack Google Contacts Clone with AdonisJS (Node.js) and Quasar Framework (Vue.js)](/llms/technical-blog/article/asc2emsho4u73r6jb2gxjddw/create-column-definitions-and-insert-new-contacts-full-stack-google-contacts-clone-with-adonis-js-node-js-and-quasar-framework-vue-js.md)
- [Data Validation & Sanitisation with AdonisJS | Full-Stack Google Contacts Clone with AdonisJS (Node.js) and Quasar Framework (Vue.js)](/llms/technical-blog/article/bcey0ac7m5sbs9hqy1h824sl/data-validation-and-sanitisation-with-adonis-js-full-stack-google-contacts-clone-with-adonis-js-node-js-and-quasar-framework-vue-js.md)
- [Creating a Middleware and Updating a Contact | Full-Stack Google Contacts Clone with AdonisJS (Node.js) and Quasar Framework (Vue.js)](/llms/technical-blog/article/oqxzmtgfy1r3oewpnedc7wf6/creating-a-middleware-and-updating-a-contact-full-stack-google-contacts-clone-with-adonis-js-node-js-and-quasar-framework-vue-js.md)
- [Fetching and Deleting a Contact with AdonisJS | Full-Stack Google Contacts Clone with AdonisJS Framework (Node.js) and Quasar Framework (Vue.js)](/llms/technical-blog/article/p6bh0ponq43u3vi6o8uww84x/fetching-and-deleting-a-contact-with-adonis-js-full-stack-google-contacts-clone-with-adonis-js-framework-node-js-and-quasar-framework-vue-js.md)
- [Using Model Factories and Seeders in AdonisJS | Full-Stack Google Contacts Clone with AdonisJS Framework (Node.js) and Quasar Framework (Vue.js)](/llms/technical-blog/article/cfotveuxbgjtdp12l6pzcb2y/using-model-factories-and-seeders-in-adonis-js-full-stack-google-contacts-clone-with-adonis-js-framework-node-js-and-quasar-framework-vue-js.md)
- **Creating and Registering a Vuex Module | Full-Stack Google Contacts Clone with AdonisJS Framework (Node.js) and Quasar Framework (Vue.js)** (Current Article)
- [Connecting UI Components to the Vuex Store | Full-Stack Google Contacts Clone with AdonisJS Framework (Node.js) and Quasar Framework (Vue.js)](/llms/technical-blog/article/fdyqp17yehbnqrjvjvab4b5b/connecting-ui-components-to-the-vuex-store-full-stack-google-contacts-clone-with-adonis-js-framework-node-js-and-quasar-framework-vue-js.md)
- [Connecting the Frontend to the API Server | Full-Stack Google Contacts Clone with AdonisJS Framework (Node.js) and Quasar Framework (Vue.js)](/llms/technical-blog/article/fk4262359gg3g7f0jn95fn9b/connecting-the-frontend-to-the-api-server-full-stack-google-contacts-clone-with-adonis-js-framework-node-js-and-quasar-framework-vue-js.md)
- [Creating and Updating Contacts From the Frontend | Full-Stack Google Contacts Clone with AdonisJS Framework (Node.js) and Quasar Framework (Vue.js)](/llms/technical-blog/article/t15w2s3ao9jo74l96stjo7z6/creating-and-updating-contacts-from-the-frontend-full-stack-google-contacts-clone-with-adonis-js-framework-node-js-and-quasar-framework-vue-js.md)
- [Uploading Files and Creating Avatars for Contacts With AdonisJS and Axios](/llms/technical-blog/article/hg9ilaubqesm8angg0rhrjp9/uploading-files-and-creating-avatars-for-contacts-with-adonis-js-and-axios.md)
- [Deleting a Contact with AdonisJS and Vuejs](/llms/technical-blog/article/zdr4pxmgcax2dsfnwimhty0g/deleting-a-contact-with-adonis-js-and-vuejs.md)

---

## Article Content

Each frontend component of our Google Contacts Clone app currently consumes the mock contacts data independently. In this lesson, we will begin using `Vuex` to manage the state of our contacts centrally so that all components can read from the same store. We will also get the `Vuex` store ready to fetch data from the backend of the application by creating `actions`. This step is necessary before we hook our frontend to the API server.

> This lesson focuses on Vuex. If you do not know what it is, please take some time to study the [Vuex docs](https://vuex.vuejs.org/). I will equally explain the pratical applications of it in this lesson.

Let's start by creating a new branch for our repo:

```bash
# Make sure you are within your project
git checkout -b 17-connect-components-to-vuex-store
```

## Create the `contacts` `Vuex` Store Module

There is a placeholder Vuex store module called `module-example` in the `ui/src/store` directory. This module was automatically created by the Quasar Framework. We will rename that folder to `contacts` and update the Vuex files within the folder.

In VS Code Explorer, rename `module-example` directory in `ui/src/store` to `contacts`.

Open `ui/src/store/contacts/state.ts`. Update the file with the content below. Refer to [this snapshot](https://github.com/ndianabasi/google-contacts/blob/18-connect-components-to-vuex-store/ui/src/store/contacts/state.ts) for the same content.

```ts
import { Contact } from "src/types";

export interface ContactStateInterface {
  contacts: Array<Contact>;
  currentContact: Contact | null;
  totalContacts: number | null;
}

function state(): ContactStateInterface {
  return {
    contacts: [],
    currentContact: null,
    totalContacts: null,
  };
}

export default state;
```

Above, we are defining the interface for our `contacts` module state called `ContactStateInterface`. The interface is implemented by the `state` function. The `state` function returns an object containing the `contacts` array which is an array of all `Contact`s which will be displayed on the `Index.vue` page; the `currentContact` object which will be the current `Contact` being viewed or edited; and the `totalContacts` which will be the total number of contacts in our database.

Open `ui/src/store/contacts/index.ts`. Update all occurrences of `ExampleStateInterface` to `ContactStateInterface`. Update all occurrences of `exampleModule` to `contactsModule`. This file exports an object containing the `actions`, `getters`, `state`, and `mutations` for the module. Refer to [this snapshot](https://github.com/ndianabasi/google-contacts/blob/18-connect-components-to-vuex-store/ui/src/store/contacts/index.ts) for the updated file.

```ts
import { Module } from "vuex";
import { StateInterface } from "../index";
import state, { ContactStateInterface } from "./state";
import actions from "./actions";
import getters from "./getters";
import mutations from "./mutations";

const contactsModule: Module<ContactStateInterface, StateInterface> = {
  namespaced: true,
  actions,
  getters,
  mutations,
  state,
};

export default contactsModule;
```

Open `ui/src/store/contacts/mutations.ts`. This file exports an object which contains functions for mutating (changing) the state of our `contacts` module. The functions are `setContactList`, `setCurrentContact`, and `setTotalContacts`. Update the file with the content below. Refer to [this snapshot](https://github.com/ndianabasi/google-contacts/blob/18-connect-components-to-vuex-store/ui/src/store/contacts/mutations.ts) for the same content.

```ts
import { Contact } from "src/types";
import { MutationTree } from "vuex";
import { ContactStateInterface } from "./state";

const mutation: MutationTree<ContactStateInterface> = {
  setContactList: (state, payload: Contact[]) => {
    const contactListLength = state.contacts.length;
    const isContactListEmpty = contactListLength === 0;
    state.contacts.splice(
      isContactListEmpty ? 0 : contactListLength - 1,
      0,
      ...payload
    );
  },
  setCurrentContact: (state, payload: Contact) => {
    state.currentContact = payload;
  },
  setTotalContacts: (state, payload: number) => {
    state.totalContacts = payload;
  },
};

export default mutation;
```

Each mutation function receives two argument. The first one is always the `state` of the module, while the second is always the `payload` sent when the mutation function is called i.e. a `commit` is made.

The `setContactList` mutation receives a payload which is an array of `Contact`s. We use the `Array.splice` method to insert the contacts at the end of the `Contact` array. The mutation will be committed during virtual scroll of the table on the `Index.vue` component. The `setCurrentContact` mutation sets the payload (a `Contact` object) to the `state.currentContact` module state property. The `setTotalContacts` mutation sets the `state.totalContacts` module state property.

Open `ui/src/store/contacts/getters.ts`. This file exports an object which contains functions for getting (retrieving) the state of our `contacts` module. The functions are `contactList`, `currentContact`, and `totalContacts`. Update the file with the content below. Refer to [this snapshot](https://github.com/ndianabasi/google-contacts/blob/18-connect-components-to-vuex-store/ui/src/store/contacts/getters.ts) for the same content.

```ts
import { GetterTree } from "vuex";
import { StateInterface } from "../index";
import { ContactStateInterface } from "./state";

const getters: GetterTree<ContactStateInterface, StateInterface> = {
  contactList: (state) => state.contacts,
  currentContact: (state) => state.currentContact,
  totalContacts: (state) => state.totalContacts,
};

export default getters;
```

Open `ui/src/store/contacts/actions.ts`. This file exports an object which contains asynchronous functions for making requests to our backend server and making commits (changing our module state) when the results from our backend arrive. The functions are `LOAD_CURRENT_CONTACT` and `LOAD_CONTACTS`. Update the file with the content below. Refer to [this snapshot](https://github.com/ndianabasi/google-contacts/blob/18-connect-components-to-vuex-store/ui/src/store/contacts/actions.ts) for the same content.

```ts
import { Contact } from "src/types";
import { ActionTree } from "vuex";
import { StateInterface } from "../index";
import { ContactStateInterface } from "./state";
import { contacts as rawContacts } from "../../data/Google_Contacts_Clone_Mock_Data";

const actions: ActionTree<ContactStateInterface, StateInterface> = {
  LOAD_CURRENT_CONTACT({ commit }, id: Contact["id"]): Promise<Contact> {
    return new Promise((resolve, reject) => {
      try {
        const currentContact = rawContacts
          .filter((contact) => contact.id === id)
          .reduce((prev, cur) => {
            prev = { ...cur };
            return prev;
          }, {} as Contact);

        commit("setCurrentContact", currentContact);

        return resolve(currentContact);
      } catch (error) {
        return reject(error);
      }
    });
  },

  LOAD_CONTACTS(
    { commit },
    { nextPage, pageSize }: { nextPage: number; pageSize: number }
  ): Promise<Contact[]> {
    return new Promise((resolve, reject) => {
      try {
        const requestedContacts = [...rawContacts].slice(
          nextPage <= 1 ? 0 : (nextPage - 1) * pageSize,
          nextPage <= 1 ? pageSize : nextPage * pageSize
        );

        commit("setContactList", requestedContacts);
        commit("setTotalContacts", rawContacts.length);

        return resolve(requestedContacts);
      } catch (error) {
        return reject(error);
      }
    });
  },
};

export default actions;
```

The `LOAD_CURRENT_CONTACT` action is dispatched when a contact is being viewed or edited. The `id` of the contact being viewed or edited is dispatched with the action and passed into the `action` as the second argument after the `context` which is the first argument. The `LOAD_CURRENT_CONTACT` action uses the `id` to filter the requested contact from the `Contact`s array in the `Google_Contacts_Clone_Mock_Data` file. The filtered array is then reduced to obtained the requested contact. After getting the requested contact, the `setCurrentContact` mutation is committed to set the `currentContact` in the `contacts` module state.

Note that this line:

```ts
const currentContact = rawContacts
          .filter((contact) => contact.id === id)
          .reduce((prev, cur) => {
            prev = { ...cur };
            return prev;
          }, {} as Contact);
```

Could also be simplified as shown below. But I wanted to demonstrate how to reduce a filtered array result to get an object:

```ts
const currentContact = rawContacts
          .filter((contact) => contact.id === id)[0] as Contact;
```

The `LOAD_CONTACTS` action is called in the `Index.vue` component during the virtual scrolling of the table. The action is dispatched with an object containing the `nextPage` and `pageSize` and receives same as its second argument. It takes the `rawContacts` from the `Google_Contacts_Clone_Mock_Data` file and slices the `Contact`s array to obtain a range of `Contact`s depending on the values of the `nextPage` and `pageSize` properties.

```ts
const requestedContacts = [...rawContacts].slice(
          nextPage <= 1 ? 0 : (nextPage - 1) * pageSize,
          nextPage <= 1 ? pageSize : nextPage * pageSize
        );
```

After obtaining the range of `Contact`s, the `setContactList` mutation is committed to append the contacts into the `contacts` array in the module state. The `setTotalContacts` is also called to set the total number of contacts in the `Google_Contacts_Clone_Mock_Data` file.

> You will notice that the actions are promises and each of them `resolves` a value at the end. These values can be received as results in the components which dispatched the actions. So, you actually decide to get the `currentContact` from the result of the action dispatch instead of fetching from the Vuex store. The result of a promise is obtained in the `then` function:

```ts
await store
          .dispatch("contacts/LOAD_CURRENT_CONTACT", props.contactId)
          .then((currentContact) => {
           // The `currentContact` above is from the resolved value not the Vuex store
            Object.keys(currentContact).forEach((key) => {
                if (key !== "id") {
                  form[key].value = currentContact[key];
                }
              });
          });
```

But we are using Vuex store to centrally store our data. So, the same action dispatch looks like this in our code:

```ts
await store
          .dispatch("contacts/LOAD_CURRENT_CONTACT", props.contactId)
          .then(() => {
            void nextTick(() => {
              Object.keys(currentContact.value).forEach((key) => {
                if (key !== "id") {
                  form[key].value = currentContact.value[key];
                }
              });
            });
          });
```

> Also note that since the actions are promises, we have to `await` them when dispatching them in our components as seen in the above examples.

> the `nextTick` hook from `vue` is used to carry out further processing on the `currentContact` in the next process tick. This ensures that data committed into the store have been properly propagated to the getters, else we might getter null or old values instead of the expected ones.

> Also note that since our actions are under the `contacts` module, we have to dispatch them by prepending `contacts` to the name of the action as the `type` of the dispatch call: `dispatch("contacts/LOAD_CURRENT_CONTACT", props.contactId)`. Action dispatch have the format: `dispatch(type, payload)`

## Registering the `contacts` modue with Vuex

Now, we will register our `contacts` modules with the `store/index.ts` file so that Vuex is aware of the module. Open `ui/src/store/index.ts`. Refer to [this snapshot](https://github.com/ndianabasi/google-contacts/blob/18-connect-components-to-vuex-store/ui/src/store/index.ts) for the updated file. Copy-and-paste the contents of the snapshot file into the `ui/src/store/index.ts` file. Let's go through the changes.

At Line 7, we import `createLogger` from the `vuex` node module. The `createLogger` function will be used to log the state of our Vuex store to the console after each action is dispatched and mutations are committed. I used this because of performance issues with the Vuex browser addon when testing the contacts table built with the `QTable` component.

```diff
import {
  createStore,
  Store as VuexStore,
  useStore as vuexUseStore,
+  createLogger,
} from "vuex";
```

At Lines 10 and 11, we import the `contacts` module and the `ContactStateInterface`

```diff
+ import contacts from "./contacts";
+ import { ContactStateInterface } from "./contacts/state";
```

At Line 24, we add the `ContactStateInterface` to the `StateInterface`

```diff
export interface StateInterface {
...
-  example: unknown
+  contacts: ContactStateInterface;
}
```

At Line 41, we add the imported `contacts` module into the `modules` object of the store. This is the point of registration of the `contacts` module. At Line 43, we also add the `plugins` property to the store and register the `createLogger` Vuex plugin. We pass in an object containing `logActions` and `logMutations` properties into the `createLogger` plugin so that the state of our store before and after each action and mutation will be logged.

```diff
export default store((/* { ssrContext } */) => {
  const Store = createStore<StateInterface>({
    modules: {
      // example
+      contacts,
    },
+    plugins: [createLogger({ logActions: true, logMutations: true })],

    // enable strict mode (adds overhead!)
    // for dev mode and --debug builds only
    strict: !!process.env.DEBUGGING,
  });
  return Store;
});
```

If you have gotten to this point, congratulations! You Vuex `contacts` module is ready. In the next lesson, we will connect our frontend components to the Vuex store.

Save all your files and commit the current changes. We are not done with this branch so we won't merge with the `master` branch yet.

```bash
git add .
git commit -m "feat(ui): create,  "
```


*This document was generated from the live article page on https://ndianabasi.com/technical-blog/article/cw96ewnzujgsz2e2garhbuzb/creating-and-registering-a-vuex-module-full-stack-google-contacts-clone-with-adonis-js-framework-node-js-and-quasar-framework-vue-js • 2026-06-07*
