nuxt-community / firebase-module

🔥 Easily integrate Firebase into your Nuxt project. 🔥
https://firebase.nuxtjs.org
MIT License
642 stars 98 forks source link

Support delayed service initialization #231

Closed pimlie closed 4 years ago

pimlie commented 4 years ago

Is your feature request related to a problem? Please describe.

At the moment if you use this module all firebase services are included / initiated on every page. I dont want / need this

Describe the solution you'd like

Support delayed loading / initialization of firebase services at the user's request

Describe alternatives you've considered

Do everything manually without this module

Additional context

For example, I only need fireAuth when a user decides to login (am using custom token based login). If a user isn't logged in then I dont need to include fireAuth yet. Some with realtimeDb, I am only using it on a single page

lupas commented 4 years ago

Hey @pimlie

Thanks for the feature request. You're right, currently this is not possible with the way the module is built.

We could - probably best additionally to the all-in solution - export specific functions that instantiate a service when requested, so one could import that wherever needed and it automatically sets up the service with the provided credentials from nuxt.config.

That would require quite some brain-cells and refactoring I assume, I'll see what I can do, currently bit low on my time budget. So mostly likely won't be today or tomorrow.

Let me know if you already have a plan on how we best could achieve this.

pimlie commented 4 years ago

There are multiple options for an API, am currently thinking about using something like this (but still a wip):

export default async (ctx, inject) => {
  let firebaseReady = false
  let firebase, session

  const fire = {
    ready: async () => {
      if (!firebaseReady) {
        firebase = await import('firebase/app')

        <% if (typeof options.sessions === 'object') { %>
        const { res } = ctx
        if (process.server && res && res.locals && res.locals.user) {
          const { default: SessionManager } = await import('@nuxtjs/firebase/lib/services/SessionManager')

          const manager = new SessionManager(firebase, {
            config,
            sessions: <%= serialize(options.sessions) %>
          })

          // Resolve the firebase app corresponding to the server user
          session = await manager.startSession(res.locals.user.uid)
          res.locals._session = session
          res.locals._manager = manager
        } else {
          session = firebase.apps.find(a => a.name === '[DEFAULT]') || firebase.initializeApp(config)
        }
        <% } else { %>
        if (!firebase.apps.length) {
          firebase.initializeApp(config)
        }
        session = firebase.apps[0]
        <% } %>
        firebaseReady = true
      }

      return session
    }
  }

    <% if (options.services.auth) { %>
  /** --------------------------------------------------------------------------------------------- **/
  /** -------------------------------------- FIREBASE AUTH ---------------------------------------- **/
  /** --------------------------------------------------------------------------------------------- **/
  let authReady = false

  fire.auth = null
  // fire.authModule = null
  fire.authReady = async () => {
    if (!authReady) {
      await fire.ready()
      await <%= writeImportStatement('realtimeDb') %>

      await new Promise(async (resolve) => {
        try {
          await <%= writeImportStatement('auth') %>
          fire.auth = session.auth()
          // fire.authModule = firebase.auth

          await fire.auth.setPersistence(firebase.auth.Auth.Persistence.SESSION)

          const unsubscribe = fire.auth.onAuthStateChanged((user) => {
            authReady = true
            unsubscribe()

            resolve(fire.auth)
          })
        } catch (err) {
          resolve()
        }
      })
    }

    return fire.auth
  }
  <% } %>

  ctx.$fire = fire
  inject('fire', fire)

Then in my app I can do:

const auth = await ctx.$fire.authReady()
auth.signInWithCustomToken(token)
// or
await ctx.$fire.ready()
await ctx.$fire.authReady()
ctx.$fire.auth.signInWithCustomToken(token)

But still eg thinking about whether I think its a good idea to also keep injecting $fireAuth, it is 'short' but why support two ways of accessing the same object. You can just do ctx.$fire.auth.session.

Was also thinking about replace ctx.$fire.auth with the full session and then adding ready on the session object, but dont think we should add our own methods to the firebase auth api

edit: updated implementation