After struggling with user authentication in my nuxt app for quite a while, I was able to find a solution with the help of a friend and I would love to share it with you. Let's go!
Prerequisites
This article asumes that you are familiar with the basics of web development as well as the basics of vuejs. If you are not familiar with vue js check out this turorial.
Project Structure
This is the project structure for our Nuxt Authentication with axios application.
Setup Nuxt Project
I would be using npm so to follow along with this tutorial you will need to install:
npm install axios vuex-persist vuex
vuex-persist
If you are not familiar with vuex-persist, it is a third party library in the vue ecosystem that saves the user's data in the browser's local or session storage (depending on your configuration). This enables the user to close a browser window and return with his details and data intact.
vuex-toasted helps display formatted messages to the user. Could be error messages or success messages.
After installation your package.json should look like this:
Ignore the packages not discussed in this tutorial. Focus on the versions of the ones used. Specifically: Axios, vuex-toasted and vuex-persist. If you are using Nuxt you should have axios and vuex pre-installed so that saves you some time.
Backend Api routes
Assuming you have a fully functional backend api with a base url of localhost:3000/backend/api/ you would need 2 routes: the register and login routes. For this tutorial we would be using:
- register route : auth/signup
- login route: auth/signin
vuex store
The vuex store handles the state of our user data. From it we retrieve and store the user login details in the state function.
- Import your vuex-persist plugin into your vuex store so it has access to your global state variables.
import VuexPersistence from 'vuex-persist'
function getPlugins() {
const plugins = []
if (process.browser) {
const vuexLocal = new VuexPersistence({
storage: window.localStorage,
})
plugins.push(vuexLocal.plugin)
}
return plugins
}
export const plugins = getPlugins()
export const state = () => ({
userDetails: [],
})
export const mutations = {
setUserDetails(state, val) {
state.userDetails = val
},
}
We use the store to hold state data. Which in this case is the user's authentication info which helps us manage the flow of data throughout the application's runtime.
Login page:
login.vue
<form @submit="login()" enctype="multipart/form-data" method="post">
<div class="fields">
<label for="email"><strong>Email Address</strong></label>
<input type="email" name="email" v-model="email" />
<label for="email"><strong>Password</strong></label>
<input type="password" name="email" v-model="password" />
<span class="forgot">
Use Uppercase, Lowercase and Numeric characters*
</span>
</div>
<div>
<div class="btn">
<button type="button" @click="login()">
Sign In
</button>
</div>
</div>
</form>
Axios requests
You would notice we get the user information from the store by calling this.$store.state.userDetails
. This is how user state is accessed from the store.
Next we make a post request to the signin endpoint supplying the API with the user data in the data object. The API then checks if the user exists and returns a response of successful.
We can now proceed to store the userdata in the local storage by committing the user data to the vuex store. The vuex persist plugin we imported in the store would save the data to enable persistent state between user sessions.
<script>
import axios from 'axios'
export default {
data() {
return {
email: '',
password: '',
userInfo: this.$store.state.userDetails,
}
},
methods: {
async login() {
const data = {
email: this.email,
password: this.password,
}
axios
.post('localhost:3000/backend/api/auth/signin', data)
.then((res) => {
const userData = res.data
userData.user.token = userData.token
this.$store.commit('setUserDetails', userData.user)
this.$toasted.show('You have logged in successfully', {
position: 'top-center',
duration: 500,
type: 'success',
})
this.$router.push('/home')
})
.catch((err) => {
this.$toasted.show(
'Please enter the correct details and try again',
err,
{
position: 'top-left',
duration: 200,
type: danger,
}
)
})
},
},
}
</script>
Register Page:
register.vue
<form @submit="register()" enctype="multipart/form-data" method="post">
<div class="names">
<div class="name">
<label for="firstname"><strong>First Name</strong></label>
<input type="text" name="firstname" v-model="firstname" />
</div>
<div class="name">
<label for="lastname"><strong>Last Name</strong></label>
<input type="text" name="lastname" v-model="lastname" />
</div>
</div>
<div class="fields">
<label for="email"><strong>Email Address</strong></label>
<input type="email" name="email" v-model="email" />
<label for="password"><strong>Password</strong></label>
<input type="password" name="password" v-model="password" />
<span class="forgot">
Use Uppercase, Lowercase and Numeric characters*
</span>
</div>
<div class="btn">
<button type="button" @click="register()">
Sign Up
</button>
</div>
</form>
axios requests
<script>
import axios from 'axios'
export default {
data() {
return {
firstname: '',
lastname: '',
email: '',
password: '',
error: null,
}
},
methods: {
async register() {
const data = {
firstname: this.firstname,
lastname: this.lastname,
email: this.email,
password: this.password,
}
axios
.post('localhost:3000/backend/api/auth/signup', data)
.then((res) => {
const userData = res.data
this.$toasted.show('You have registered successfully', {
theme: 'primary',
position: 'top-center',
duration: 5000,
type: 'success',
})
this.$router.push('/auth/login')
})
},
},
}
</script>
Vuex Toast Notifications:
This is a simple notification system that makes the helps create a good user experience. As seen above, the usage is simple. Specify your message as the first parameter to the toasted.show() method and then the specific configuration for the message type.
Your application would access these plugins (vuex-toasted and vuex-persist) from the files below.
Create a plugins folder and in it create two files like so:
We would use these files to host the configurations for vuex-persist and vuex-toast respectively.
vuex-persist.js
import VuexPersistence from 'vuex-persist'
export default ({ store }) => {
new VuexPersistence({
/* your options */
}).plugin(store)
}
vuex-toasted.js
import Vue from 'vue'
import Toasted from 'vue-toasted'
if (process.browser) {
Vue.use(Toasted)
}
As explained above, these files are meant to host the configurations for their respective packages.
You should try to make these code examples work with your specific use-case. Once you do that everything should work fine.
I am sure you are happy-crying now as you realize this long-ass article has finally come to an end.
Either way, let me know in the comments what do you think about the code and please do give suggestions for improvements, they will be greatly appreciated!
Top comments (2)
i didn't found the way to make it work on nuxt 3 !
Are you getting any error message? Can you send the details of your problem?