Vue.js Login System with Vuex & Axios

Vue.js Login System with Vuex & Axios Thumbnail
Published on 5. November 2019Last updated on 1. September 2020

In this article you will learn how to create a complete Vue.js login system with Vue.js, Vuex and Axios.

Advertisement

This article is the first part of a two-part series to create a complete login system with Node.js and Vue.js. For this we use Vue.js as frontend and Node.js as backend.

The tutorial is divided into two parts so that you are not bound to a Node.js, but can use the Vue.js frontend with another system of your choice, like PHP or Python.

This way to part 1: Node.js Login System with Express, JWT & MySQL (Rest API)

Our goal: Vue.js Login – with Vuex and Axios

We create a Vue.js frontend where we create a session for the user, store it, and can access it over and over again to check if a user is logged in and has a valid session. We use Vue CLI, Axios, Vuex and the vuex-persistedstate module to temporarily store the session.

At the end you have a web application where users can register and log in. In addition, some pages should only be accessible if the user has a valid session, i.e. is logged in. But now we finally want to start!?

1. How Vuex works

Vuex is a kind of “store” in which different states are stored. If this data is adjusted, your Vue Component updates itself automatically. We use this to save the login status of the user to get access to different routes and to display adapted texts, etc.

Advertisement

We also use vuex-persistedstate to keep the data even after reloading the page. But more about this in step 3.

2. Install Vue CLI and initialize Vue App

At the beginning we install the Vue CLI – if not already done – with the following command:

npm install -g @vue/cli

If you have installed Vue CLI 1.x or 2. and want to update to a newer version, you must first uninstall the old version using npm uninstall vue-cli -g.

Then we can initialize our Vue App via Vue CLI. We do this with the following command:

vue create client

If the vue command in your console does not work on Windows, you can try to adjust the PATH environment variables as follows: C:\Program Files\nodejs\node.exe;C:\Users{YOUR_NAME}\AppData\Roaming\npm. After a restart of the CMD the command should work.

We have to select different components and modules that should be installed as standard. Here it makes sense to create your own preset (here “lh-standard”) and use it for future projects. So you should select “Manually select features” here. For this tutorial we absolutely need the modules vue-router and vuex. Everything else is optional.

Selection of the required modules when creating the Vue.js App
Selection of the required modules when creating the Vue.js App

Once the process is complete, you will receive a folder with this (or similar) structure.

Advertisement
Folder structure of the login system (left: Vue CLI 1.x oder 2.x, right: Vue CLI 3.x or newer)
Folder structure of the login system (left: Vue CLI 1.x oder 2.x, right: Vue CLI 3.x or newer)

3. Install dependencies

The next step is to install additional packages. Among them axios to send HTTP requests and vuex-persistedstate to get the data from Vuex even after reloading the page.

npm install axios vuex-persistedstate

4. Create Routes

Now we’ll plot three routes:

  • …/ => This route should only be accessible for logged in users.
  • …/sign-up => Registration page
  • …/login => Login page

We adapt the standard router file and assign each route its own Vuex View.

// src/router.js (Vue CLI 1.x & 2.x) or src/router/index.js (Vue CLI 3.x or newer)

import Vue from "vue";
import Router from "vue-router";
import Home from "./views/Home.vue";
import SignUp from "./views/SignUp.vue";
import Login from "./views/Login.vue";

Vue.use(Router);

export default new Router({
  mode: "history",
  base: process.env.BASE_URL,
  routes: [
    {
      path: "/",
      name: "home",
      component: Home
    },
    {
      path: "/sign-up",
      name: "sign-up",
      component: SignUp
    },
    {
      path: "/login",
      name: "login",
      component: Login
    },
  ]
});

5. Set up Vuex

The next step is to set up Vuex. Therefore we integrate the plugin vuex-persistedstate (lines 6 and 19). This helps us to get data even after reloading the page.

To “save” the data we now create the status token and user. We also create actions for login and logout. Very important: In line 46 we set the JWT token of our Rest API as Authorization Header. Without this line all future requests to the Rest API would not work!

If you want to know more about Vuex, you can read it here.

// src/store.js (Vue CLI 1.x & 2.x) oder src/store/index.js (Vue CLI 3.x or newer)

import Vue from 'vue';
import Vuex from 'vuex';
import Axios from 'axios';
import createPersistedState from 'vuex-persistedstate';

Vue.use(Vuex);

const getDefaultState = () => {
	return {
		token: '',
		user: {}
	};
};

export default new Vuex.Store({
	strict: true,
	plugins: [createPersistedState()],
	state: getDefaultState(),
	getters: {
		isLoggedIn: state => {
			return state.token;
		},
		getUser: state => {
			return state.user;
		}
	},
	mutations: {
		SET_TOKEN: (state, token) => {
			state.token = token;
		},
		SET_USER: (state, user) => {
			state.user = user;
		},
		RESET: state => {
			Object.assign(state, getDefaultState());
		}
	},
	actions: {
		login: ({ commit, dispatch }, { token, user }) => {
			commit('SET_TOKEN', token);
			commit('SET_USER', user);

			// set auth header
			Axios.defaults.headers.common['Authorization'] = `Bearer ${token}`;
		},
		logout: ({ commit }) => {
			commit('RESET', '');
		}
	}
});

So that the JWT token is passed also with each call, we must still adapt the main.js as follows.

// src/main.js

import Vue from 'vue';
import App from './App.vue';
import router from './router';
import store from './store';
import Axios from 'axios';

Vue.config.productionTip = false;

// set auth header
Axios.defaults.headers.common['Authorization'] = `Bearer ${store.state.token}`;

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

6. Services for registration and login

We create a new file AuthService.js to make requests to our server (Rest API). For this we need a function login(credentials) for the login and the function signUp(credentials) to register. Here you need our package axios, which we already installed in step 3.

Advertisement

The third function getSecretContent() returns the secret content, which is only displayed if you are logged in.

// src/services/AuthService.js

import axios from 'axios';

const url = 'http://localhost:3000/api/';

export default {
	login(credentials) {
		return axios
			.post(url + 'login/', credentials)
			.then(response => response.data);
	},
	signUp(credentials) {
		return axios
			.post(url + 'sign-up/', credentials)
			.then(response => response.data);
	},
	getSecretContent() {
		return axios.get(url + 'secret-route/').then(response => response.data);
	}
};

7. Sign Up page

The probably most important step: The registration of new users. We have already created a route in the router and are now creating the appropriate Vue View. Here we have a simple form with user name, password, password repetition and a button to register.

When clicking, we call the signUp(credentials)function from the AuthService and pass the entered user data. Additionally we have a field in which error and success messages are displayed automatically: msg.

// src/views/SignUp.vue

<template>
	<div>
		<h1>Sign Up</h1>
		<input type="text" placeholder="Username" v-model="username" />
		<input type="text" placeholder="Password" v-model="password" />
		<input
			type="text"
			placeholder="Password (repeat)"
			v-model="password_repeat"
		/>
		<input type="button" @click="signUp" value="Sign Up" />
		<p v-if="msg">{{ msg }}</p>
	</div>
</template>
<script>
import AuthService from '@/services/AuthService.js';

export default {
	data() {
		return {
			username: '',
			password: '',
			password_repeat: '',
			msg: ''
		};
	},
	methods: {
		async signUp() {
			try {
				const credentials = {
					username: this.username,
					password: this.password,
					password_repeat: this.password_repeat
				};
				const response = await AuthService.signUp(credentials);
				this.msg = response.msg;
			} catch (error) {
				this.msg = error.response.data.msg;
			}
		}
	}
};
</script>

Optionally you can redirect to the login page after line 38. You can use this code for this:

this.$router.push('/');

If we call this page we will get this result with full functionality:

Vue.js Sign Up

Ever heard of Double Opt-In? It involves sending confirmation emails to verify a registration. In this tutorial you will learn how to implement Double Opt-In in your Node.js application.

8. Login page

At login we have a similar structure and this time we call the login(credentials) function.

Advertisement

It is important to call Vuex in line 36. We pass the user object user and the token token from the HTTP response and write the data in Vuex.

After successful login, line 38 will forward you to the “protected route”, which should only be accessible after login.

// src/views/Login.vue

<template>
	<div>
		<h1>Login</h1>
		<input type="text" placeholder="Username" v-model="username" />
		<input type="text" placeholder="Password" v-model="password" />
		<input type="button" @click="login" value="Login" />
		<p v-if="msg">{{ msg }}</p>
	</div>
</template>
<script>
import AuthService from '@/services/AuthService.js';

export default {
	data() {
		return {
			username: '',
			password: '',
			msg: ''
		};
	},
	methods: {
		async login() {
			try {
				const credentials = {
					username: this.username,
					password: this.password
				};
				const response = await AuthService.login(credentials);
				this.msg = response.msg;

				const token = response.token;
				const user = response.user;

				this.$store.dispatch('login', { token, user });

				this.$router.push('/');
			} catch (error) {
				this.msg = error.response.data.msg;
			}
		}
	}
};
</script>

And this is what the route looks like in action:

Vue.js Login – Demo

9. Protect routes

Registration and login is complete. Now comes the part where we want to protect our route / and make it accessible only with valid user data. We go to the corresponding view and add a few lines to check if the user is logged in. If not, he will be redirected to the login page.

// src/views/Home.vue

<template>
	<div>
		<h1>Hi {{ username }}</h1>
		<p>{{ secretMessage }}</p>
		<input type="button" value="Logout" @click="logout" />
	</div>
</template>

<script>
import AuthService from '@/services/AuthService.js';

export default {
	data() {
		return {
			secretMessage: '',
			username: ''
		};
	},
	async created() {
		if (!this.$store.getters.isLoggedIn) {
			this.$router.push('/login');
		}

		this.username = this.$store.getters.getUser.username;

		this.secretMessage = await AuthService.getSecretContent();
	},
	methods: {
		logout() {
			this.$store.dispatch('logout');
			this.$router.push('/login');
		}
	}
};
</script>

We have also added a logout button to end the current session and the username will also be displayed. Of course everything is extendable and customizable. Our login system looks like this:

Finished Vue.js login page – demonstration of the functions

10. Conclusion

That?s it! In the second part we implemented the Vue.js frontend with the help of Vuex. We created a registration page, a login page, “normal” pages and pages protected by a login.

If you haven’t read part 1: Node.js Login System with Express, JWT & MySQL (Rest API) yet, you can do it directly to provide your login system with a backend component. With this we have implemented a complete login system, which you can use for your web applications. Thanks for reading! 🙂

Related Posts
NEW 🚀
Join the Conversation

19 Comments

  1. Thank you for your tutorial. When i console.log AuthService.signUp(credentials) I get undefined.

    1. Then check your axios response directly. Maybe there’s an error with your RestAPI? Do some console.log()s in your AuthService.js

  2. Hey! great tutorial- I have a little problem, I tested the Sign Up an the Login it all works great, but when I get to the last step (9. Protect routes), I was faced with 2 errors,

    – The first on being: [Vue warn]: Error in created hook (Promise/async): “TypeError: Cannot read property ‘username’ of undefined”
    found in —> at src/views/Home.vue
    at src/App.vue

    – and the second: TypeError: Cannot read property ‘username’ of undefined
    at _callee$ (Home.vue?76f2:31)
    at tryCatch (runtime.js?96cf:45)
    at Generator.invoke [as _invoke] (runtime.js?96cf:274)
    at Generator.prototype. [as next] (runtime.js?96cf:97)
    at asyncGeneratorStep (asyncToGenerator.js?1da1:3)
    at _next (asyncToGenerator.js?1da1:25)
    at eval (asyncToGenerator.js?1da1:32)
    at new Promise ()
    at eval (asyncToGenerator.js?1da1:21)
    at VueComponent.created (Home.vue?76f2:27)

    I Couldn’t find any solution for the error, does it have any thing to do with dispatch from: actions: {
    login: ({ commit, dispatch }, { token, user }) => {…

    any thoughts?

    Note: I had also the following error from the eslint-loader but I just disabled lent on save for the time being to get rid of the error:
    – \client\src\store\index.js
    39:23 error ‘dispatch’ is defined but never used no-unused-vars.

    Thanks in Advance
    TN

    1. Hey, thank you!

      Check your store.js. It looks like the getter method getUser doesn’t work correctly. Output your state variable and check what’s in it. Maybe that can help you. 🙂

      Greetings
      LH

  3. When i click sign up in sign up page, in console log says “TypeError: Cannot read property ‘data’ of undefined”, can you tell me whats’ wrong it is ?

    1. Check if your try-catch block works correctly. It looks like you run into the catch block. Output the error variable. And check if you get the right answer from AuthService.signUp(credentials);.

  4. Hi, great tut – I have a workring result ? Now how do I deploy or build, so that I can upload it to my hosting-server and use it live? I have used a remote (live) Database from the start, so that should be set?

    1. Hey, thanks! If you’re using Plesk you can follow this guide: https://webdeasy.de/en/deploy-node-js-app-with-plesk/

      There are a few steps you have to do:
      create a vue.config.js file in your client folder with the following content:

      const path = require('path');
      module.exports = {
      outputDir: path.resolve(__dirname, '../server/public'),
      devServer: {
      proxy: {
      '/api': {
      target: 'http://localhost:3000'
      }
      }
      }
      };

      Then you can run: npm run build
      This will export your Vue App to a folder in your server folder. Than you need to publish all files/folders to your server (excluding the client folder) and start your Node.js App (server/index.js) on it. For example with the Plesk guide. 🙂

      Hope this helps!
      LH

      1. Thanks – But I don’t need the node-server now that I am uploading to an actual server, do I? I though I only had to upload the lib, middleware and routes folders (or something like that) and I was good to go?

        1. You need a node server, too. This node application is to manage the serverside routes. The vue application is only frontend and you need both parts to use this app. Generally every application with an RestAPI needs a node server.
          LH

          1. Cool I got it working live not (using Heroku – because its free) – Thx! If I want to hide elements (like navigation items), can I then use a v-if-statement to do so?

          2. That’s great! Of course, just add the v-if directive to the div (or whatever) to hide.

  5. isnt something is off with store.js?
    Authservice is defines (line 8) but never used :/
    dispatch (line 43) , but i never see it used.
    getting “[Vue warn]: Error in created hook “. authorization is denied. any tips?

    1. Hey! I removed line 8 with “AuthService”, i forgot it while coding.. sorry :/
      And you can usually ignore warnings for now…
      LH

  6. is it possible to have the source code?
    Also you can know how the project was created including part 1 (eg npm create …), given I am a newbie.
    Comuque is a nice example.

Your email address will not be published. Required fields are marked *