Trying to implement the Firebase library into a NextJS project seems quite difficult.
I sometimes get the following error:
__WEBPACK_IMPORTED_MODULE_0_firebase_app__.messaging
But the most common ones are the ones I'll explain here.
Here's my config:
import * as firebase from "firebase/app";
import "firebase/firestore";
import "firebase/storage";
import "firebase/messaging";
const config = {
...
};
const db = !firebase.apps.length
? firebase.initializeApp(config).firestore()
: firebase.app().firestore();
db.settings({ timestampsInSnapshots: true });
if (typeof window !== "undefined") {
db.enablePersistence().catch(function(err) {
if (err.code == "failed-precondition") {
console.log("failed-precondition");
} else if (err.code == "unimplemented") {
console.log("unimplemented");
}
});
}
const storage = firebase.storage();
const messaging = firebase.messaging();
export { db, storage, messaging };
the firestore db variable works fine.
But just initializing the messaging variable gives me the following error and crashes the app:
ReferenceError: self is not defined
at isSupported (/path/to/project/node_modules/@firebase/messaging/dist/index.cjs.js:2093:5)
at Object.factoryMethod [as messaging] (/path/to/project/node_modules/@firebase/messaging/dist/index.cjs.js:2074:14)
at FirebaseAppImpl._getService (/path/to/project/node_modules/@firebase/app/dist/index.node.cjs.js:138:66)
at FirebaseAppImpl.(anonymous function) [as messaging] (/path/to/project/node_modules/@firebase/app/dist/index.node.cjs.js:327:31)
at Object.serviceNamespace [as messaging] (/path/to/project/node_modules/@firebase/app/dist/index.node.cjs.js:312:32)
at Object../lib/firebase.config.ts (/path/to/project/.next/server/bundles/pages/_app.js:124:72)
at __webpack_require__ (/path/to/project/.next/server/bundles/pages/_app.js:23:31)
at Object../pages/_app.tsx (/path/to/project/.next/server/bundles/pages/_app.js:147:79)
at __webpack_require__ (/path/to/project/.next/server/bundles/pages/_app.js:23:31)
at Object.0 (/path/to/project/.next/server/bundles/pages/_app.js:541:18)
at __webpack_require__ (/path/to/project/.next/server/bundles/pages/_app.js:23:31)
at /path/to/project/.next/server/bundles/pages/_app.js:70:18
at Object.<anonymous> (/path/to/project/.next/server/bundles/pages/_app.js:71:12)
at Module._compile (internal/modules/cjs/loader.js:689:30)
at Module.m._compile (/path/to/project/node_modules/ts-node/src/index.ts:439:23)
at Module._extensions..js (internal/modules/cjs/loader.js:700:10)
If I try to use the storage via getInitialProps aka server side I get the following error:
/path/to/project/node_modules/@firebase/storage/dist/index.cjs.js:672
this.xhr_ = new XMLHttpRequest();
^
ReferenceError: XMLHttpRequest is not defined
Only happens on server side though.
Steps to reproduce the behavior, please provide code snippets or a repository:
1.
yarn create next-app --example with-firebase-hosting-and-typescript with-firebase-hosting-and-typescript-app
yarn add firebaseimport * as firebase from "firebase/app";
import "firebase/firestore";
import "firebase/storage";
import "firebase/messaging";
const config = {
apiKey: "replace",
authDomain: "replace",
databaseURL: "replace",
projectId: "replace",
storageBucket: "replace",
messagingSenderId: "replace"
};
const db = !firebase.apps.length
? firebase.initializeApp(config).firestore()
: firebase.app().firestore();
db.settings({ timestampsInSnapshots: true });
if (typeof window !== "undefined") {
db.enablePersistence().catch(function(err) {
if (err.code == "failed-precondition") {
console.log("failed-precondition");
} else if (err.code == "unimplemented") {
console.log("unimplemented");
}
});
}
const storage = firebase.storage();
// const messaging = firebase.messaging();
export { db, storage };
Note I'm commenting messaging because otherwise it would crash the app with the self is not defined
import * as React from "react";
import App from "../components/App";
import { storage } from "../initFirebase";
class Index extends React.Component {
static async getInitialProps() {
console.log(await storage.ref("path/to/file.jpg").getDownloadURL());
}
render() {
return (
<App>
<p>Index Page</p>
</App>
);
}
}
export default Index;
yarn dev go to localhost:3000 and you'll get the referenceError: XMLHttpRequest is not defined errorShould work as expected.
@jthegedus could you have a look
hey @timneutkens I made a very simple repo replicating the bug:
https://github.com/johhansantana/firebase-nextjs-error
Just install dependencies and run it
static async getInitialProps() {
console.log("server");
console.log(await storage.ref("path/to/image.jpg").getDownloadURL());
return {};
}
The issue here is that you are using the firebase/storage package on the server side of the application.
firebase/storage relies upon XMLHttpRequest packaged with the browser which doesn't exist in a NodeJS environment. Here is the firebase-js-sdk ticket.
if you move the getDownloadURL() call to the client side it should work.
async componentDidMount() {
console.log("client");
console.log(await storage.ref("path/to/image.jpg").getDownloadURL());
}
The same thing is occurring with the firebase/messaging package. firebase.messaging() is intended for client-side use only. The time at which your lib/firebase.config.js is being evaluated is a server-side NodeJS environment, not the required Browser environment. I would suggest either poly-filling as mentioned in that ticket or doing something like this:
// lib/firebase.config.js
const storage = firebase.messaging();
const setupMessaging = () => {
return firebase.messaging();
};
export { storage, setupMessaging};
// page/index.js
async componentDidMount() {
console.log("client");
setupMessaging();
console.log(await storage.ref("path/to/image.jpg").getDownloadURL());
}
The reason that the two packages error at a different time is because of the dependencies are different and appear at different parts of the API. It seems messaging requires the browsers global or something in it's constructor whereas storage requires the browsers XMLHttpRequest in getDownloadURL().
I may work on a more comprehensive example elsewhere (setting up messaging is difficult at the best of times) with all the standard firebase features. Hope this helped.
thanks @jthegedus
I may work on a more comprehensive example elsewhere (setting up messaging is difficult at the best of times) with all the standard firebase features.
This would be nice if you have the time.
I don't think firebase seems to be the right tool to use with nextjs giving how hacky one has to be in order for it to work. I'll look for a different solution.
Thanks!
Most helpful comment
The issue here is that you are using the
firebase/storagepackage on the server side of the application.firebase/storagerelies uponXMLHttpRequestpackaged with the browser which doesn't exist in a NodeJS environment. Here is thefirebase-js-sdkticket.if you move the
getDownloadURL()call to the client side it should work.The same thing is occurring with the
firebase/messagingpackage.firebase.messaging()is intended for client-side use only. The time at which yourlib/firebase.config.jsis being evaluated is a server-side NodeJS environment, not the required Browser environment. I would suggest either poly-filling as mentioned in that ticket or doing something like this:The reason that the two packages error at a different time is because of the dependencies are different and appear at different parts of the API. It seems
messagingrequires the browsersglobalor something in it's constructor whereasstoragerequires the browsersXMLHttpRequestingetDownloadURL().I may work on a more comprehensive example elsewhere (setting up
messagingis difficult at the best of times) with all the standard firebase features. Hope this helped.