Nuxt With Firebase V9 Modular (Tree Shake)

Apr 12, 2022

Seems like @nuxtjs/firebase is not going to support firebase v9 modular syntax, and there might not be support for firebase v10.

So the best way forward is to use Firebase directly without depending on a nuxt plugin.

Install Firebase.

npm install firebase

Edit nuxt.config.js

  • load firebaseClient on client side only
const config = {  plugins: [    { src: '~/plugins/firebaseClient', mode: 'client' },  ]}export default config

Create plugins/firebaseClient.js

  • load firebase and analytics by default
  • load auth, firestore, etc. dynamically when required
import { initializeApp, getApps } from "firebase/app"import { getAnalytics } from "firebase/analytics";const firebaseConfig = {  apiKey: "AIza******",  authDomain: "*****.firebaseapp.com",  projectId: "*****",  storageBucket: "*****.appspot.com",  messagingSenderId: "9613*****",  appId: "1:9613*****:web:5ec9*****",  measurementId: "G-C7*****"}const firebaseApp = initializeApp(firebaseConfig);// only load analytics at productionlet analytics = nullif (process.env.NODE_ENV == 'production') {  analytics = getAnalytics(firebaseApp);}export default async ({ app, store }, inject) => {  const fireModule = {    firestore: { }  }  const fire = {    app,    analytics,    // dynamic load auth when required    async getApp() {      return this.app    },    async getAuth() {      if (!this.auth) {        const { getAuth, onAuthStateChanged } = await import("firebase/auth")        const firebaseApp = await this.getApp()        this.auth = getAuth(firebaseApp)        onAuthStateChanged(this.auth, async (authUser) => {          const claims = authUser ? (await authUser.getIdTokenResult(true)).claims : null          if (authUser) {            // I only run app check when user signin            await fire.getAppCheck()          }          // perform your action here          // await store.dispatch('onAuthStateChangedAction', { authUser, claims })        })      }      return this.auth    },    async getFirestore() {      if (!this.firestore) {        // switch to firebase/firestore/lite if onSnapshot is not used for the entire application        const { getFirestore, doc, collection, getDoc, Timestamp, GeoPoint, deleteField } = await import("firebase/firestore")        // simulate similar syntax with @nuxtjs/firebase        fireModule.firestore.Timestamp = Timestamp        fireModule.firestore.GeoPoint = GeoPoint        fireModule.firestore.FieldValue = {          delete: deleteField        }        const firebaseApp = await this.getApp()        const db = getFirestore(firebaseApp, {})        db.getItem = async (collectionName, docId) => {          const docSnap = await getDoc(doc(collection(db, collectionName), docId))          if (docSnap.exists()) {            const item = docSnap.data()            item.id = docSnap.id            return item          }        }        this.firestore = db      }      return this.firestore    },    async getFunctions() {      if (!this.functions) {        const { getFunctions } = await import('firebase/functions')        const firebaseApp = await this.getApp()        this.functions = getFunctions(firebaseApp)      }      return this.functions    },    async getAppCheck() {      if (!this.appCheck) {        const { initializeAppCheck, ReCaptchaV3Provider } = await import("firebase/app-check");        const firebaseApp = await this.getApp()        this.appCheck = initializeAppCheck(firebaseApp, {          // site key          provider: new ReCaptchaV3Provider('6Lel******'),          // Optional argument. If true, the SDK automatically refreshes App Check          // tokens as needed.          isTokenAutoRefreshEnabled: true        });      }      return this.appCheck    },  }  // simulate similar syntax with @nuxtjs/firebase  inject('fire', fire)  inject('fireModule', fireModule)}

Usage in page or component.

<script>import { collection, doc, query, where, limit, getDocs, setDoc } from "firebase/firestore";export default {  props: ['id'],  async mounted() {    const id = this.id    // since we are loading dynamically, await this.firestore() syntax is required    const db = await this.$fire.getFirestore()    // else, you could preload/assign fire.firestore in plugins/firebaseClient.js    // const db = this.$fire.firestore    // load single document using helper function    const place = await db.getItem('place', id)    // query    const snapshot = await getDocs(query(collection(db, 'place'), where('parent_ids', 'array-contains', id), where('active', '==', true), limit(9)))    const places = await snapshot.docs.map(doc => {      const item = doc.data()      item.id = doc.id      return item    })    // update with merge    let ref = doc(collection(db, 'place'), id)    const data = {      modified: this.$fireModule.firestore.Timestamp.now(),    }    await setDoc(ref, data, { merge: true })  }}</script>

NOTE: You can load firebase and analytics dynamically as well.

NOTE: JavaScript Dynamic Import with Tree Shaking (via async).

References:

❤️ Is this article helpful?

Buy me a coffee ☕ or support my work via PayPal to keep this space 🖖 and ad-free.

Do send some 💖 to @d_luaz or share this article.

✨ By Desmond Lua

A dream boy who enjoys making apps, travelling and making youtube videos. Follow me on @d_luaz

👶 Apps I built

Travelopy - discover travel places in Malaysia, Singapore, Taiwan, Japan.