Vue.js Login System with Vuex & Axios

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

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.

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.

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
    },
  ]
});

If you haven’t tested your Rest API yet, you can try this strategy for testing Rest APIs.

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.

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.

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
Join the Conversation

37 Comments

  1. When I run npm run serve I get the error:  ERROR Failed to compile with 1 error 9:28:37 PM[eslint]C:\Users\Joe\Vue\login_register\vue\client\src\store\index.js 41:23 error ‘dispatch’ is defined but never used no-unused-vars

    1. Lorenz says:

      It looks like you’ve linting enabled and never used the ‘dispatch’ variable. Try to remove the import line for ‘dispatch’ in your store/index.js.

  2. mattis says:

    hey awesome tutorial thx, but i’m a little new into coding, I followed the tutorial line by line and I get “Cannot GET /login” whe I try to test it, do I need to enter some type of command before testing ?

    1. Lorenz says:

      Thanks! You can follow this Postman tutorial to test your RestAPI. The error says, that you have not registered your GET /login router correctly. The only “command” you need to execute is to call your Route e.g. http://localhost:3000/login. Hope this helps! 🙂

  3. Sabri Sansoy says:

    i’ve followed this tutorial and copied all code line by line and when i run npm client serve i get the standard vue welcome page. dont i need to modify App.vue to get to the login/register page?

    1. Lorenz says:

      Check your created() function in your Home.vue. There must be an redirect if !this.$store.getters.isLoggedIn. Hope this helps 🙂

  4. boomin says:

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

    1. Lorenz says:

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

  5. Troy says:

    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. Lorenz says:

      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

      1. Jonny says:

        Hey bro , I also got the same error as above, let me ask you how to fix this
        Thank you for reading my comments and have a good day

  6. handsomejack says:

    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. Lorenz says:

      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);.

  7. Rue says:

    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. Lorenz says:

      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. Rue says:

        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. Lorenz says:

          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. Rue says:

            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. Lorenz says:

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

          3. Rue says:

            Can you show how – each time I try it crashes…

          4. Lorenz says:

            https://webdeasy.de/en/complete-login-system-with-node-js-vue-js-vuex-part-2-2/#sign-up-page

            Like line 14 in this code. You just have to enter your variable instead.

  8. Beamo says:

    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. Lorenz says:

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

      1. Leo says:

        Hi LH,
        I’m still a little confused about what you need to add/remove concerning AuthService in line 8 or 42. Can you please help and specify because I’ve got the same error? That would be brilliant!

        1. Lorenz says:

          Hi Leo,
          you don’t need to change anything. I have adjusted it in the tutorial.
          Is it a warning or an error? And do you use the Node.js RestAPI I wrote in the first part?

          Greetings
          Lorenz

  9. franco says:

    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.

    1. Lorenz says:

      Hey! The whole source code is on this page and the first tutorial (https://webdeasy.de/en/complete-login-system-with-node-js-vue-js-restapi-jwt-part-1-2/). You can copy them out of the code boxes. 🙂
      LH

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

bold italic underline strikeThrough
insertOrderedList insertUnorderedList outdent indent
removeFormat
createLink unlink
code

This can also interest you