Firebase-functions: HTTP Trigger sometimes returns 'The default Firebase app already exists'

Created on 20 Jan 2018  路  1Comment  路  Source: firebase/firebase-functions

Version info

firebase-functions: 0.7.5

firebase-tools: 3.16.0

firebase-admin: 5.5.0

Test case

Disclaimer: I have a unique issue where I need to initialize the app for each request. Each client will have their own firebase app and therefore their own credentials to manage their own Admin SDK. That is why the admin.initializeApp is inside the /addUser block. I suspect this could be part of the issue.

```const express = require('express');
const app = express();
const functions = require('firebase-functions');
const Promise = require('promise');
const cors = require('cors');
const admin = require('firebase-admin');
const bodyParser = require('body-parser');
const fs = require('fs');
const staffRoles = ['admin', 'sysadmin', 'teacher', 'staff', 'superadmin'];

app.use(bodyParser.urlencoded({ extended: true }));
app.use(bodyParser.json());
app.use(cors());

app.post('/addUser', function(req, res){
var userRecordUID = null;
var db = null;
var newUser = null;
var uid = null;

getSDKCredentials(req.body.state, req.body.district)
    .then(function(response){
        var serviceAccount = JSON.parse(response);
        // intialize school with credentials

        admin.initializeApp({
            credential: admin.credential.cert(serviceAccount),
            databaseURL: 'https://' + serviceAccount.project_id + '.firebaseio.com'
        });

        db = admin.database();

        // verify user with token
        return admin.auth().verifyIdToken(req.body.token)
            .then(function(decodedToken){
                return decodedToken;
            }, function(error){
                return Promise.reject({
                    msg: error.message,
                    error: error.code
                });
            });
    }, function(error){
        console.log(req.body.state + ' - ' + req.body.district + ': credentials not found. Error - 1003');
        return Promise.reject({msg: 'Your school needs additional setup. Please contact support to correct this issue.', error: '1003'});
    })
    .then(function(decodedToken){
        uid = decodedToken.uid;

        // get varified user
        return db.ref('users-public/' + uid).once('value')
            .then(function(snapshot){
                return snapshot.val();
            });
    })
    .then(function(verifiedUser){
        if(staffRoles.indexOf(verifiedUser.role) === -1){
            console.log(req.body.state + ' - ' + req.body.district + ': Error 1004. UID-' + uid + ' ' + verifiedUser.firstName + ' ' + verifiedUser.lastName + ' role-' + verifiedUser.role);
            return Promise.reject({msg: 'You are not authorized to perform this task.', error: '1004'});
        } else {
            newUser = req.body.newUser;
            // creat new user with email and password
            return admin.auth().createUser({
                email: newUser.email,
                password: newUser.password
            })
            .then(function(userRecord){
                return userRecord;
            }, function(error){
                return Promise.reject({msg: newUser.email + ' - ' + error.message, error: error.code});
            });
        }
    })
    .then(function(userRecord){
        userRecordUID = userRecord.uid;
        var updatedUserData = {};
        if(newUser.role === 'student'){

            updatedUserData['users-public/' + userRecord.uid] = {
                email: newUser.email,
                firstName: newUser.firstName,
                lastName: newUser.lastName,
                role: newUser.role,
                yog: newUser.yog,
                dob: newUser.dob || null
            };
            updatedUserData['students-private/' + userRecord.uid] = {
                firstName: newUser.firstName,
                lastName: newUser.lastName,
                address1: newUser.address1 || null,
                address2: newUser.address2 || null,
                city: newUser.city || null,
                state: newUser.state || null,
                zip: newUser.zip || null,
                guardians: newUser.guardians || null
            };
            updatedUserData['passwords/' + userRecord.uid] = newUser.password;

            // add student info to database
            return db.ref().update(updatedUserData)
                .then(function(){
                    return Promise.resolve();
                }, function(error){
                    console.log('write error');
                    return Promise.reject('write error');
                });

        } else {
            // staff member to database
            return db.ref('users-public/' + userRecord.uid).set({
                email: newUser.email,
                firstName: newUser.firstName,
                lastName: newUser.lastName,
                role: newUser.role,
                officialTitle: newUser.officialTitle,
                _title: newUser._title,
                ext: newUser.ext
            })
            .then(function(){
                    return Promise.resolve();
                }, function(error){
                    return Promise.reject('write error');
                });
        }
    })
    .then(function(){
        // success!
        res.status(200).json({
            msg: 'User ' + newUser.firstName + ' ' + newUser.lastName + ' was successfully created'
        });
    })
    .catch(function(error){
        console.log(error);
        res.json(error);
    });

});

function getSDKCredentials(state, district){
var filePath = './credentials/' + state + '-' + district + '.json';
return new Promise(function(resolve, reject){
fs.readFile(filePath, 'utf8', function(error, response){
if(error){
return reject(error);
} else {
return resolve(response);
}
});
});
}

function promiseError(details){
this.msg = details.msg;
this.error = details.error;
}

exports.api = functions.https.onRequest(app);

### Steps to reproduce

HTTP POST Request to `https://my.cloud.function.url/api/addUser`
I was testing errors stepping through each promise to make sure I was getting the proper response in the .catch block. I noticed that SOMETIMES the response returned is 

{ Error: The default Firebase app already exists. This means you called initializeApp() more than once without providing an app name as the second argument. In most cases you only need to call initializeApp() once. But if you do want to initialize multiple apps, pass a second argument to initializeApp() to give each app a unique name. at FirebaseAppError.Error (native) at FirebaseAppError.FirebaseError [as constructor] (/user_code/node_modules/firebase-admin/lib/utils/error.js:39:28) at FirebaseAppError.PrefixedFirebaseError [as constructor] (/user_code/node_modules/firebase-admin/lib/utils/error.js:85:28) at new FirebaseAppError (/user_code/node_modules/firebase-admin/lib/utils/error.js:119:28) at FirebaseNamespaceInternals.initializeApp (/user_code/node_modules/firebase-admin/lib/firebase-namespace.js:55:23) at FirebaseNamespace.initializeApp (/user_code/node_modules/firebase-admin/lib/firebase-namespace.js:312:30) at /user_code/index.js:26:10 at tryCallOne (/user_code/node_modules/promise/lib/core.js:37:12) at /user_code/node_modules/promise/lib/core.js:123:15 at flush (/user_code/node_modules/promise/node_modules/asap/raw.js:50:29) errorInfo: { code: 'app/duplicate-app', message: 'The default Firebase app already exists. This means you called initializeApp() more than once without providing an app name as the second argument. In most cases you only need to call initializeApp() once. But if you do want to initialize multiple apps, pass a second argument to initializeApp() to give each app a unique name.' }, codePrefix: 'app' }
```

Sometimes I can run my request receiving the expected error any number of times in a row. For instance, I am testing for the auth/email-already-exists error, I might get the aforementioned error about the Firebase app already exists, or I will get the auth/email-already-exists error. There doesn't seem to be any pattern that returns one over the other. Sometimes it will work as expected, sometimes it won't.

Were you able to successfully deploy your functions?

Yes

Expected behavior

Should return my custom responses and never 'Firebase App Already Exists'

Actual behavior

Randomized as expected responses with 'Firebase App Already Exists' responses

Most helpful comment

Solved!

For future reference if anybody else needs this.

First I created a function to get the desired app.

var apps = {};
function getApp(name){
  if(apps[name]){
    return;
  }
  // if app doesn't exist yet, initialize
  apps[name] = admin.initializeApp(config, name);
  return;
}

Then you can specify which app you are going to use when the route is called.

// retreive stored app
var thisApp = apps[name];
// retreive auth
var thisAppAuth = thisApp.auth();
// retreive database
var thisAppDb = thisApp.database();

>All comments

Solved!

For future reference if anybody else needs this.

First I created a function to get the desired app.

var apps = {};
function getApp(name){
  if(apps[name]){
    return;
  }
  // if app doesn't exist yet, initialize
  apps[name] = admin.initializeApp(config, name);
  return;
}

Then you can specify which app you are going to use when the route is called.

// retreive stored app
var thisApp = apps[name];
// retreive auth
var thisAppAuth = thisApp.auth();
// retreive database
var thisAppDb = thisApp.database();
Was this page helpful?
0 / 5 - 0 ratings