Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to fix firebase database initialised multiple times due to React SSR initialised database and cloud function firebase initialised database?

I have updated the question as found the root cause of the issue. As I have hosted my React SSR app which uses firebase database in the client serving by one of the cloud function named app throwing an error of Error: FIREBASE FATAL ERROR: Database initialized multiple times. Please make sure the format of the database URL matches with each database() call.. When I comment out one by one and deploy, works perfectly. But when I deploy together doesn't work. How do I separate these two keeping both at the same repo?

ORIGINAL Question: Why firebase cloud function throwing an error of 'The default Firebase app does not exist.'? So I am trying out firebase function for the first time. admin.messaging() throwing me the following error. Help me figure out why? If I look at the console I get results till console.log('deviceToken', deviceToken);

so whats wrong in const messageDone = await admin.messaging().sendToDevice(deviceToken, payload);?

const functions = require('firebase-functions');
const admin = require('firebase-admin');
exports.updateUnreadCount = functions.database.ref('/chats/{chatId}/{messageId}')
  .onCreate(async(snap, context) => {
    const appOptions = JSON.parse(process.env.FIREBASE_CONFIG);
    appOptions.databaseAuthVariableOverride = context.auth;
    const adminApp = admin.initializeApp(appOptions, 'app');
    const { message, senderId, receiverUid } = snap.val();
    console.log(message, senderId, receiverUid);
    console.log('------------------------');
    const deleteApp = () => adminApp.delete().catch(() => null);
    try {
      const db = adminApp.database();
      const reciverUserRef = await db.ref(`users/${receiverUid}/contacts/${senderId}/`);
      console.log('reciverUserRef', reciverUserRef);
      const deviceTokenSnapshot = await reciverUserRef.child('deviceToken').once('value');
      const deviceToken = await deviceTokenSnapshot.val();
      console.log('deviceToken', deviceToken);

      const payload = {
        notification: {
          title: 'Test Notification Title',
          body: message,
          sound: 'default',
          badge: '1'
        }
      };
      const messageDone = await admin.messaging().sendToDevice(deviceToken, payload);
      console.log('Successfully sent message: ', JSON.stringify(messageDone));
      return deleteApp().then(() => res);
    } catch (err) {
      console.log('error', err);
      return deleteApp().then(() => Promise.reject(err));
    }
  });

Update1: According to this https://firebase.google.com/docs/cloud-messaging/send-message#send_to_a_topic, admin.messaging().sendToDevice(deviceToken, payload) APIs are only available in the Admin Node.js SDK? So switched to

const payload = {
        data: {
          title: 'Test Notification Title',
          body: message,
          sound: 'default',
          badge: '1'
        },
        token: deviceToken
      };
      const messageDone = await admin.messaging().send(payload);

Which is not working either. Getting an error Error: The default Firebase app does not exist. Make sure you call initializeApp() before using any of the Firebase services. Any lead will be helpful.

EDIT: Finally got the function working. My index.js is exporting to functions, follwoing

exports.app = functions.https.onRequest(app); //React SSR
exports.updateChat = functions.database.ref('/chats/{chatId}/{messageId}').onCreate(updateChat);

exports.app is a react ssr function, which I am using to host my site. This uses database too. and throwing error of multiple database instance. When I comment out one by one and deploy, works perfectly. But when I deploy together doesn't work. How do I separate these two keeping both at the same repo? Any suggestions, please?

like image 810
Subhendu Kundu Avatar asked Sep 10 '19 14:09

Subhendu Kundu


1 Answers

You can initialise db outside export function.

const admin = require('firebase-admin');
const adminApp = admin.initializeApp(appOptions, 'app')
//continue code

Update:

const admin = require('firebase-admin');
const adminApp = admin.initializeApp(options);
async function initialize(options, apps = 'app') {
    try {
      const defaultApp = adminApp.name
      if(defaultApp) { 
          const adminApp1 = admin.initializeApp(apps);
      }else {
        const adminApp1 = admin.initializeApp(options, apps);
      }
    }catch(err) {
      console.error(err);
    }
  }

Modify this snippet as per your need and try it out

It abstracts initialize of app in another function. Just call this function at appropriate place in your code.

like image 58
Shubham Avatar answered Oct 18 '22 10:10

Shubham