Firebase Push Notifications with Next.js

Viewed 273

I've created a project with next and I'm trying to use Firebase's notification service. The thing is that I found this article but I'm getting an error about getMessaging() method.

My package.json

{
  "name": "firebase-noti",
  "version": "0.1.0",
  "private": true,
  "scripts": {
    "dev": "next dev",
    "build": "next build",
    "start": "next start",
    "lint": "next lint"
  },
  "dependencies": {
    "bootstrap": "^5.1.3",
    "firebase": "^9.9.0",
    "next": "12.2.2",
    "react": "18.2.0",
    "react-bootstrap": "^2.4.0",
    "react-dom": "18.2.0"
  },
  "devDependencies": {
    "eslint": "8.20.0",
    "eslint-config-next": "12.2.2"
  }
}

My firebase.js

import { initializeApp } from "firebase/app";
import { getMessaging, getToken, onMessage } from "firebase/messaging";

var firebaseConfig = {
  apiKey: "AIzaSyAjWIJe9AaL6fDZVn9tRajF-BUexEPFZyA",
  authDomain: "react-notif-40228.firebaseapp.com",
  projectId: "react-notif-40228",
  storageBucket: "react-notif-40228.appspot.com",
  messagingSenderId: "790644971731",
  appId: "1:790644971731:web:dc0c5d007d2b961af3dc26",
};

const firebaseApp = initializeApp(firebaseConfig);
const messaging = getMessaging(firebaseApp);

export const fetchToken = (setTokenFound) => {
  return getToken(messaging, { vapidKey: "BHGPr3pJQSflJAJtTIVXbmcEXlPV_HP29TZQRcqrGCN10gKIa-ojIJmtvM9kQGcsNKsWIA6ezKFG8Bd6LTjaVc0" })
    .then((currentToken) => {
      if (currentToken) {
        console.log("current token for client: ", currentToken);
        setTokenFound(true);
        // Track the token -> client mapping, by sending to backend server
        // show on the UI that permission is secured
      } else {
        console.log(
          "No registration token available. Request permission to generate one."
        );
        setTokenFound(false);
        // shows on the UI that permission is required
      }
    })
    .catch((err) => {
      console.log("An error occurred while retrieving token. ", err);
      // catch error while creating client token
    });
};

My index.js

import styles from "../styles/Home.module.css";
import { useState } from "react";
import "bootstrap/dist/css/bootstrap.min.css";
import { fetchToken } from "./firebase";

export default function Home() {
  const [show, setShow] = useState(false);
  const [isTokenFound, setTokenFound] = useState(false);
  fetchToken(setTokenFound);

  return (
    <div className={styles.container}>
      <main className={styles.main}>
        {isTokenFound && <>Notification permission enabled </>}
        {!isTokenFound && <>Need notification permission ❗️</>}
      </main>
    </div>
  );
}

And the code is always throwing this error Service messaging is not available but when I console.log firebaseApp is ok

FirebaseAppImpl {
  _isDeleted: false,
  _options: {
    apiKey: 'AIzaSyAjWIJe9AaL6fDZVn9tRajF-BUexEPFZyA',
    authDomain: 'react-notif-40228.firebaseapp.com',
    projectId: 'react-notif-40228',
    storageBucket: 'react-notif-40228.appspot.com',
    messagingSenderId: '790644971731',
    appId: '1:790644971731:web:dc0c5d007d2b961af3dc26'
  },
  _config: { name: '[DEFAULT]', automaticDataCollectionEnabled: false },
  _name: '[DEFAULT]',
  _automaticDataCollectionEnabled: false,
  _container: ComponentContainer {
    name: '[DEFAULT]',
    providers: Map(8) {
      'platform-logger' => [Provider],
      'heartbeat' => [Provider],
      'fire-core-version' => [Provider],
      'fire-core-esm2017-version' => [Provider],
      'fire-js-version' => [Provider],
      'fire-js-all-app-version' => [Provider],
      'app' => [Provider],
      'messaging' => [Provider]
    }
  }
}

I am so lost and I do not know if it is about next or firebase problem because this code on a React project works.

1 Answers

So I'm not an expert either but getMessaging when called on the server side gives errors because it needs access to the "window" object. Correct my terminologies, if incorrect.

I create a separate function which I called from a layout file on the client-side and that ran perfectly fine.

const firebaseCloudMessaging = {
init: async () => {
    try {
        const messaging = getMessaging(app)
        // const tokenInLocalForage = await localforage.getItem('fcm_token')

        // if (tokenInLocalForage !== null) {
        //     return tokenInLocalForage
        // }

        const status = await Notification.requestPermission()
        if (status && status === 'granted') {
            const fcmToken = await getToken(messaging, { vapidKey: process.env.NEXT_PUBLIC_FIREBASE_CLOUD_MESSGING_PUBLIC_KEY })

            if (fcmToken) {
                localforage.setItem('fcm_token', fcmToken)
                return fcmToken
            }
        }
    } catch (err) {
        console.log(err)
        return null
    }
}

}

Something like that.

Related