Firebase-js-sdk: WebpackError: ReferenceError: IDBIndex is not defined while building with Gatsby.JS (ver #2.15.21)

Created on 1 Oct 2019  路  5Comments  路  Source: firebase/firebase-js-sdk


[REQUIRED] Describe your environment

  • Operating System version: Ubuntu 18.04
  • Browser version: Chrome Version 77.0.3865.90 (Official Build) (64-bit)
  • Firebase SDK version: 7.0.0
  • Firebase Product: Cloudstore (auth, database, storage, etc)

[REQUIRED] Describe the problem

Steps to reproduce:

Gatsby.js cannot build the final app.js file due to issue in any of the firebase packages. While building, when it it hits import firebase from 'firebase', gatsby throws the following error :

WebpackError: ReferenceError: IDBIndex is not defined

  - idb.mjs:87 Module../node_modules/idb/lib/idb.mjs
    node_modules/idb/lib/idb.mjs:87:1

  - index.esm.js:1 Module../node_modules/@firebase/installations/dist/index.esm.js
    node_modules/@firebase/installations/dist/index.esm.js:1:1

  - index.esm.js:1 Module../node_modules/@firebase/messaging/dist/index.esm.js
    node_modules/@firebase/messaging/dist/index.esm.js:1:1

  - index.esm.js:1 Module../node_modules/firebase/dist/index.esm.js
    node_modules/firebase/dist/index.esm.js:1:1

Relevant Code:


import firebase from 'firebase';

core

Most helpful comment

So, good news, I have 2 solutions for you.

Background: this is happening during the SSR step, or build-html step, which is run in node, but targeting web. So it tries to import the browser version of all imported dependencies, and browser versions of Firebase bundle do not run well in Node.

The best solution is probably to force Webpack to import Firebase with the require method (and therefore always grab Node compatible bundles) by extending Gatsby's internal webpack config for the build-html step. To do this, add this to your gatsby-node.js file:

exports.onCreateWebpackConfig = ({
  stage,
  actions,
  getConfig
}) => {
  if (stage === 'build-html') {
    actions.setWebpackConfig({
      externals: getConfig().externals.concat(function(context, request, callback) {
        const regex = /^@?firebase(\/(.+))?/;
        // exclude firebase products from being bundled, so they will be loaded using require() at runtime.
        if (regex.test(request)) {
          return callback(null, 'umd ' + request);
        }
        callback();
      })
    });
  }
};

Gatsby's webpack config is already doing this for a number of its own deps, this is just concatting an additional function to do the same to Firebase bundles.

Another option is to explicitly dynamically import (lazy load) firebase in your code, in the highest-level component that uses firebase. You would put a statement such as this:

    import('firebase').then(firebase => {
      firebase.initializeApp({ /* firebaseConfig goes here */});
      firebase.firestore().collection('items').doc('yJd1Fs5Ampttq6QKBoYF').get()
        .then(doc => {
          // do stuff with Firestore data
        });
    });

in your componentDidMount method or in a one-time useEffect hook.

Here's an external blog post that goes into more detail about the lazy-loading solution: https://kyleshevlin.com/firebase-and-gatsby-together-at-last

All 5 comments

I couldn't figure out how to label this issue, so I've labeled it for a human to triage. Hang tight.

One way I could get it to build was degrading down to [email protected] from [email protected]

So, good news, I have 2 solutions for you.

Background: this is happening during the SSR step, or build-html step, which is run in node, but targeting web. So it tries to import the browser version of all imported dependencies, and browser versions of Firebase bundle do not run well in Node.

The best solution is probably to force Webpack to import Firebase with the require method (and therefore always grab Node compatible bundles) by extending Gatsby's internal webpack config for the build-html step. To do this, add this to your gatsby-node.js file:

exports.onCreateWebpackConfig = ({
  stage,
  actions,
  getConfig
}) => {
  if (stage === 'build-html') {
    actions.setWebpackConfig({
      externals: getConfig().externals.concat(function(context, request, callback) {
        const regex = /^@?firebase(\/(.+))?/;
        // exclude firebase products from being bundled, so they will be loaded using require() at runtime.
        if (regex.test(request)) {
          return callback(null, 'umd ' + request);
        }
        callback();
      })
    });
  }
};

Gatsby's webpack config is already doing this for a number of its own deps, this is just concatting an additional function to do the same to Firebase bundles.

Another option is to explicitly dynamically import (lazy load) firebase in your code, in the highest-level component that uses firebase. You would put a statement such as this:

    import('firebase').then(firebase => {
      firebase.initializeApp({ /* firebaseConfig goes here */});
      firebase.firestore().collection('items').doc('yJd1Fs5Ampttq6QKBoYF').get()
        .then(doc => {
          // do stuff with Firestore data
        });
    });

in your componentDidMount method or in a one-time useEffect hook.

Here's an external blog post that goes into more detail about the lazy-loading solution: https://kyleshevlin.com/firebase-and-gatsby-together-at-last

Amazing! Thank you so much for the coherent breakdown of the solution and providing not one, but 2 of them! Really appreciate it, and the first solution worked great 馃憤 !

Just a tiny comment, after adding the first fix, the project complains about some files in the '.cache' folder of gatsby while trying to run gatsby develop again. In case anyone stumbles across this, just run gatsby clean && gatsby develop and it should start right up.

Was this page helpful?
0 / 5 - 0 ratings