No-Nonsense Firebase Auth + Firestore in Vue

Let's add Firebase to our Vue + Vuex App with no nonsense.

Tip: Ctrl + F for "recipe" to find variables to update

NPM

npm i firebase core-js

Config Files

Add src/firebase.js:

import * as firebase from "firebase/app";
import "firebase/auth";
import "firebase/firestore";
// ๐Ÿ‘† This example is using Auth + Firestore

var firebaseConfig = {
  // This comes from Google - add your config here
};
firebase.initializeApp(firebaseConfig);

// utils
const db = firebase.firestore();
const auth = firebase.auth();

// collections
const usersCollection = db.collection("users");
const recipeCollection = db.collection("recipe");
// ๐Ÿ‘† Here you create your Collections you want to use later

// ๐Ÿ‘‡ You export things here | update the variable names
export { db, auth, usersCollection, recipeCollection };

Modify src/main.js

// ๐Ÿ‘‡ Add this to your imports
import { auth } from "./firebase";

// ๐Ÿ‘‡ Wrap your new Vue like this
let app;
auth.onAuthStateChanged((user) => {
  if (!app) {
    app = new Vue({
      router,
      store,
      render: (h) => h(App),
    }).$mount("#app");
  }

  if (user) {
    store.dispatch("fetchUserProfile", user);
  }
});

Vuex Store

Template State

// ๐Ÿ‘‡ Add these imports
import * as firebase from "../firebase";
import router from "../router/index";

// ๐Ÿ‘‡ Add this outside your store const if it isn't user dependent filtering
firebase.recipeCollection
  // ๐Ÿ‘† Replace this with your variable from firebase.js
  .where("public", "==", true)
  // ๐Ÿ‘† This filters incoming data
  .onSnapshot((snapshot) => {
    let recipeArray = [];

    snapshot.forEach((doc) => {
      let recipe = doc.data();
      recipe.id = doc.id;

      recipeArray.push(recipe);
    });
    // ๐Ÿ‘† Create an array of all docs found.
    store.commit("setRecipes", recipeArray);
  });

// ๐Ÿ‘‡ From here down replaces the export default new Vuex.Store({...}) that Vue adds by default
const store = new Vuex.Store({
  state: {
    userProfile: {},
    recipes: [],
  },
  mutations: {
    setUserProfile(state, val) {
      state.userProfile = val;
    },
    setRecipes(state, val) {
      state.recipes = val;
    },
  },
  actions: {
    async login({ dispatch }, form) {
      const { user } = await firebase.auth.signInWithEmailAndPassword(
        form.email,
        form.password
      );

      dispatch("fetchUserProfile", user);
    },

    async fetchUserProfile({ commit }, user) {
      const userProfile = await firebase.usersCollection.doc(user.uid).get();

      commit("setUserProfile", userProfile.data());

      if (router.currentRoute.path === "/login") {
        router.push("/");
      }
    },
    async logout({ commit }) {
      await firebase.auth.signOut();

      // clear userProfile and redirect to /login
      commit("setUserProfile", {});
      router.push("/login");
    },

    async createRecipe({ state, commit }, recipe) {
      let now = new Date();

      // ๐Ÿ‘‡ Model your record here
      await firebase.recipeCollection.add({
        createdOn: now,
        userId: firebase.auth.currentUser.uid,
        username: state.userProfile.name,
      });
    },

    async signup({ dispatch }, form) {
      // sign user up
      const { user } = await firebase.auth.createUserWithEmailAndPassword(
        form.email,
        form.password
      );

      // ๐Ÿ‘‡ Add this to your login form as the submit function
      //    login() {
      //      this.$store.dispatch("login", {
      //        email: this.loginForm.email,
      //        password: this.loginForm.password
      //      });
      //    },

      // create user profile object in userCollection
      await firebase.usersCollection.doc(user.uid).set({
        name: form.name,
      });

      dispatch("fetchUserProfile", user);
    },
  },
});

export default store;

V-Router

// ๐Ÿ‘‡ Add this to your imports
import { auth } from "../firebase";

// ...

// ๐Ÿ‘‡ An example route that requires user to be authenticated
const routes = [
  {
    path: "/",
    name: "Home",
    component: Home,
    meta: {
      requiresAuth: true,
    },
  },
];

// ...

// ๐Ÿ‘‡ Add this just before your export
router.beforeEach((to, from, next) => {
  const requiresAuth = to.matched.some((x) => x.meta.requiresAuth);

  if (requiresAuth && !auth.currentUser) {
    // ๐Ÿ‘‡ Direct the user to this path if not logged in
    next("/login");
  } else {
    next();
  }
});

References

These sources helped me get this working the first time:

๐Ÿงช Experiment ๐Ÿงช | ๐Ÿ’ฅ Fail ๐Ÿ’ฅ | ๐Ÿง  Learn ๐Ÿง 
ยฉ 2024, Built with Nuxt