Search code examples
async-awaitaxiosfirebase-authenticationnuxt.jsvuex

Proper way to pass user uid to my database with Nuxt, Laravel API and Firebase Auth?


What I want to do.

I'm currently creating a restaurant booking web app with Nuxt, Laravel API and Firebase Authentication.

What I want to do is,

  1. Create a new user account in Firebase Authentication.

    {
        "email": "User's email address from input",
        "password": "User's password from input"
    }
    
  2. At the same time, insert a new user account to my local database with user uid as password.

    {
        "name": "User's name from input",
        "email": "User's email address from input",
        "password": "User's uid from store/auth.js"
    }
    

Issue

The registration success to the Firebase Auth and also to the local database as well if it's without uid. But when I try to add uid, I get this error in Laravel logs.

1364 Field 'General error: password' doesn't have a default value

I thought this error occurred because of asynchronous processing, so I added Async to the code, but the result does not change.

Code

// register.vue
<script>
import firebase from '~/plugins/firebase'

export default {
  layout: 'auth',
  data () {
    return {
      user: {
        name: null,
        email: null,
        password: null,
      }
    }
  },
  methods: {
    async register() {
      this.$store.dispatch('auth/register',
        {
          name: this.user.name,
          email: this.user.email,
          password: this.user.password
        }
      )
      .then(() => {
        this.$store.dispatch('auth/onAuth')
      })
      .then(() => {
        this.$axios.post('http://localhost:8000/api/v1/users',
          {
            name: this.user.name,
            email: this.user.email,
            password: this.$store.state.auth.userUid
          }
        )
      })
      .then(() => {
        this.$router.push('/thanks')
      })
    },
  }
}
</script>

// store/auth.js

import firebase from '~/plugins/firebase.js'

export const state = () => ({
    userUid: '',
    userEmail: '',
    loggedIn: false,
})

export const actions = {
    async register({ commit }, payload) {
        firebase
            .auth()
            .createUserWithEmailAndPassword(payload.email, payload.password)
        .then((result) => {
            const user = result.user
            commit('loginStatusChange', true)
            console.log('Regist a user was successful')
            commit('setUserUid', user.uid)
            commit('setUserEmail', user.email)
        })
    },

    onAuth({ commit }) {
        firebase.auth().onAuthStateChanged(user => {
            user = user ? user : {}
            commit('setUserUid', user.uid)
            commit('setUserEmail', user.email)
            commit('loginStatusChange', user.uid ? true : false)
        })
    }
}

export const mutations = {
    loginStatusChange(state, status) {
        state.loggedIn = status
    },
    setUserUid(state, userUid) {
        state.userUid = userUid
    },
    setUserEmail(state, userEmail) {
        state.userEmail = userEmail
    }
}

export const getters = {
    getUserUid(state) {
        return state.userUid
    },
    getUserEmail(state) {
        return state.userEmail
    }
}

Environment

  • Nuxt 2.15.8
  • Laravel Framework 8.72.0
  • MySQL 5.7.34
  • Firebase Auth

Solution

  • The issue is that your actions aren't composable so your Axios post request to Laravel doesn't wait for the previous two actions to complete, therefore this.$store.state.auth.userUid is empty at that stage.

    1. Have your async actions return a promise that actually waits for things to complete.

      export const actions = {
        async register({ commit }, { email, password }) {
          const { user } = await firebase // 👈 note the "await" here
              .auth()
              .createUserWithEmailAndPassword(email, password)
      
          commit('loginStatusChange', true)
          console.log('Register a user was successful')
          commit('setUserUid', user.uid)
          commit('setUserEmail', user.email)
      
          return user // 👈 return the user so consumers can use it
        }
      }
      
    2. Registering a firebase auth state change listener isn't something you should configure as a Vuex action. I would instead register this as a plugin

      // store/index.js
      import firebase from "~/plugins/firebase"
      
      const authStateChangePlugin = store => {
        firebase.auth().onAuthStateChanged(user => {
          user = user ? user : {}
          store.commit("auth/setUserUid", user.uid)
          store.commit("auth/setUserEmail", user.email)
          store.commit("auth/loginStatusChange", user.uid ? true : false)
        })
      }
      
      export const plugins = [ authStateChangePlugin ]
      
    3. Wait for your action to dispatch before posting to Laravel

      methods: {
        async register () {
          // 👇 note the use of "await"
          const user = await this.$store.dispatch("auth/register", { 
            ...this.user
          })
      
          await this.$axios.post("http://localhost:8000/api/v1/users", {
            ...this.user,
            password: user.uid // no need to reference the store here
          })
      
          this.$router.push('/thanks')
        }
      }