Using Firebase in a Vue App with Vuexfire — Querying and Replacing Documents

John Au-Yeung - Jan 26 '21 - - Dev Community

Check out my books on Amazon at https://www.amazon.com/John-Au-Yeung/e/B08FT5NT62

Subscribe to my email list now at http://jauyeung.net/subscribe/

The Vuefire library lets us add Firebase database manipulation capabilities right from our Vue app.

In this article, we’ll look at how to use Vuefire and Vuexfire to add support for Cloud Firestore database manipulation into our Vue app.

Querying the Database

We can query the database and synchronize it with the Vuex store with Vuexfire.

Sorting

To sort the data that we get from the Firebase database, we can call orderBy to sort the data.

db.js

import firebase from "firebase/app";
import "firebase/firestore";
export const db = firebase
  .initializeApp({ projectId: "project-id" })
  .firestore();
const { Timestamp, GeoPoint } = firebase.firestore;
export { Timestamp, GeoPoint };
Enter fullscreen mode Exit fullscreen mode

main.js

import Vue from "vue";
import App from "./App.vue";
import { firestorePlugin } from "vuefire";
import { vuexfireMutations, firestoreAction } from "vuexfire";
import Vuex from "vuex";
import { db } from "./db";

Vue.use(Vuex);
Vue.use(firestorePlugin);
Vue.config.productionTip = false;

const store = new Vuex.Store({
  state: {
    books: []
  },
  mutations: {
    ...vuexfireMutations
  },
  actions: {
    bindBooksRef: firestoreAction((context) => {
      return context.bindFirestoreRef(
        "books",
        db.collection("books").orderBy("title", "desc")
      );
    })
  },
  getters: {
    books: (state) => {
      return state.books;
    }
  }
});

new Vue({
  store,
  render: (h) => h(App)
}).$mount("#app");
Enter fullscreen mode Exit fullscreen mode

App.vue

<template>
  <div>{{books}}</div>
</template>
<script>
import { mapGetters, mapActions } from "vuex";

export default {
  data() {
    return {};
  },
  methods: {
    ...mapActions(["bindBooksRef"])
  },
  computed: {
    ...mapGetters(["books"])
  },
  mounted() {
    this.bindBooksRef();
  }
};
</script>
Enter fullscreen mode Exit fullscreen mode

In the bindBookRef action, we called the bindFirestoreRef method to bind the books state to our books database collection.

The orderBy method sorts the title field value in descending order.

Then state.books returns an array of books object with title sorted in descending order.

Since we called mapGetters to map the getters to computed properties, we’ll see this displayed in the template.

Filtering

We can also filter items with the where method.

For example, we can write:

main.js

import Vue from "vue";
import App from "./App.vue";
import { firestorePlugin } from "vuefire";
import { vuexfireMutations, firestoreAction } from "vuexfire";
import Vuex from "vuex";
import { db } from "./db";

Vue.use(Vuex);
Vue.use(firestorePlugin);
Vue.config.productionTip = false;

const store = new Vuex.Store({
  state: {
    books: []
  },
  mutations: {
    ...vuexfireMutations
  },
  actions: {
    bindBooksRef: firestoreAction((context) => {
      return context.bindFirestoreRef(
        "books",
        db.collection("books").where("wordCount", ">", 200)
      );
    })
  },
  getters: {
    books: (state) => {
      return state.books;
    }
  }
});

new Vue({
  store,
  render: (h) => h(App)
}).$mount("#app");
Enter fullscreen mode Exit fullscreen mode

We called where to only return the documents with wordCount bigger than 200.

Then this is what we’ll see in the states and getters.

Writing to the Database

We have to use the Firebase SDK to write to our Firebase database.

For example, we can write:

main.js

import Vue from "vue";
import App from "./App.vue";
import { firestorePlugin } from "vuefire";
import { vuexfireMutations, firestoreAction } from "vuexfire";
import Vuex from "vuex";
import { db } from "./db";

Vue.use(Vuex);
Vue.use(firestorePlugin);
Vue.config.productionTip = false;

const store = new Vuex.Store({
  state: {
    books: [],
    book: {}
  },
  mutations: {
    ...vuexfireMutations
  },
  actions: {
    bindBooksRef: firestoreAction((context) => {
      return context.bindFirestoreRef("books", db.collection("books"));
    }),

    updateBook: firestoreAction(async ({ state }, { bookId, title }) => {
      const bookSnapshot = await db.collection("books").doc(bookId).get();
      const book = bookSnapshot.data();
      const newBook = { ...book, title };
      await db.collection("books").doc(bookId).set(newBook);
      console.log("book updated");
    })
  },
  getters: {
    books: (state) => {
      return state.books;
    }
  }
});

new Vue({
  store,
  render: (h) => h(App)
}).$mount("#app");
Enter fullscreen mode Exit fullscreen mode

App.vue

<template>
  <div>
    <button @click="updateBook">update book</button>
    <div>{{books}}</div>
  </div>
</template>
<script>
import { mapGetters, mapActions } from "vuex";

export default {
  data() {
    return {};
  },
  methods: {
    ...mapActions(["bindBooksRef"]),
    updateBook() {
      this.$store.dispatch("updateBook", {
        bookId: "ei4jIGJjcmS7eSRKUxsw",
        title: "baz"
      });
    }
  },
  computed: {
    ...mapGetters(["books"])
  },
  mounted() {
    this.bindBooksRef();
  }
};
</script>
Enter fullscreen mode Exit fullscreen mode

In the Vuex store, we have the updateBook action to let us get the book we want to update by its bookId .

And we want to update the title with it.

To do that, we get the books collection with the collection method.

doc gets the document by ID.

get gets the snapshot of the query result.

Then the data method returns the actual data.

Once we did that, we update the title by creating the newBook object.

And once we did that, we call set to update the document.

In the App component, we called dispatch in the updateBook method to dispatch the updateBook Vuex store action to do the update.

Since sync the store state with the collection with bindFirestoreRef , the updated items should show automatically.

Conclusion

We can update the data by using methods from the Firebase SDK.

Since it’s async, we’ve to put the code in actions.

Also, we can sort and filter items when we do the binding of the collection to the state.

. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .