Firebase-functions: Deployment error Can't determine Firebase Database URL.

Created on 27 Aug 2018  Â·  33Comments  Â·  Source: firebase/firebase-functions

Version info

"firebase-admin": "~6.0.0",
"firebase-functions": "^2.0.5",
> firebase --version                    
4.2.0

Steps to reproduce

in my package.json

{
  "name": "functions",
  "description": "Cloud Functions for Firebase",
  "scripts": {
    "lint": "./node_modules/.bin/eslint .",
    "serve": "firebase serve --only functions",
    "shell": "firebase experimental:functions:shell",
    "start": "npm run shell",
    "deploy": "firebase deploy --only functions",
    "logs": "firebase functions:log"
  },
  "dependencies": {
    "@google-cloud/storage": "^1.7.0",
    "@google-cloud/vision": "^0.5.0",
    "child-process-promise": "^2.2.0",
    "firebase-admin": "~6.0.0",
    "firebase-functions": "^2.0.5",
    "mkdirp": "^0.5.1",
    "mkdirp-promise": "^5.0.1"
  },
  "devDependencies": {
    "eslint": "^5.4.0",
    "eslint-plugin-promise": "^4.0.0"
  },
  "engines": {
    "node": "8"
  },
  "private": true,
  "main": "src/index.js"
}

in my index.js

'use strict';
const admin = require('firebase-admin');
admin.initializeApp();

/**
 * Storage triggers
 */
const storageTriggers = require('./storageTriggers');
exports.blurOffensiveImages = storageTriggers.blurOffensiveImages;

/**
 * Database triggers
 */
const databaseTriggers = require('./databaseTriggers');
exports.sendPhotoCommentNotification = databaseTriggers.sendPhotoCommentNotification;

in my databaseTriggers.js

'use strict';

const functions = require('firebase-functions');
const admin = require('firebase-admin');
const db = admin.database();
...

in my storageTriggers.js which is basically https://github.com/firebase/functions-samples/tree/Node-8/moderate-images

'use strict';

const admin = require('firebase-admin');
const functions = require('firebase-functions');
const mkdirp = require('mkdirp-promise');
const vision = require('@google-cloud/vision')();
const spawn = require('child-process-promise').spawn;
const path = require('path');
const os = require('os');
const fs = require('fs');
...

Were you able to successfully deploy your functions?

Previously yes, but not sure what changed from the previous deploy.

Actual behavior

firebase deploy --only functions

=== Deploying to 'vape-tool'...

i  deploying functions
i  functions: ensuring necessary APIs are enabled...
✔  functions: all necessary APIs are enabled
i  functions: preparing functions directory for uploading...
i  functions: packaged functions (74.13 KB) for uploading
✔  functions: functions folder uploaded successfully
i  functions: updating Node.js 8 function blurOffensiveImages(us-central1)...
i  functions: updating Node.js 8 function sendPhotoCommentNotification(us-central1)...
âš   functions[blurOffensiveImages(us-central1)]: Deployment error.
Function load error: Code in file src/index.js can't be loaded.
Is there a syntax error in your code?
Detailed stack trace: Error: Can't determine Firebase Database URL.
    at FirebaseDatabaseError.FirebaseError [as constructor] (/srv/node_modules/firebase-admin/lib/utils/error.js:39:28)
    at new FirebaseDatabaseError (/srv/node_modules/firebase-admin/lib/utils/error.js:190:23)
    at DatabaseService.ensureUrl (/srv/node_modules/firebase-admin/lib/database/database.js:75:15)
    at DatabaseService.getDatabase (/srv/node_modules/firebase-admin/lib/database/database.js:52:26)
    at FirebaseApp.database (/srv/node_modules/firebase-admin/lib/firebase-app.js:231:24)
    at FirebaseNamespace.fn (/srv/node_modules/firebase-admin/lib/firebase-namespace.js:280:45)
    at Object.<anonymous> (/srv/src/databaseTriggers.js:20:18)
    at Module._compile (module.js:652:30)
    at Object.Module._extensions..js (module.js:663:10)
    at Module.load (module.js:565:32)
âš   functions[sendPhotoCommentNotification(us-central1)]: Deployment error.
Function load error: Code in file src/index.js can't be loaded.
Is there a syntax error in your code?
Detailed stack trace: Error: Can't determine Firebase Database URL.
    at FirebaseDatabaseError.FirebaseError [as constructor] (/srv/node_modules/firebase-admin/lib/utils/error.js:39:28)
    at new FirebaseDatabaseError (/srv/node_modules/firebase-admin/lib/utils/error.js:190:23)
    at DatabaseService.ensureUrl (/srv/node_modules/firebase-admin/lib/database/database.js:75:15)
    at DatabaseService.getDatabase (/srv/node_modules/firebase-admin/lib/database/database.js:52:26)
    at FirebaseApp.database (/srv/node_modules/firebase-admin/lib/firebase-app.js:231:24)
    at FirebaseNamespace.fn (/srv/node_modules/firebase-admin/lib/firebase-namespace.js:280:45)
    at Object.<anonymous> (/srv/src/databaseTriggers.js:20:18)
    at Module._compile (module.js:652:30)
    at Object.Module._extensions..js (module.js:663:10)
    at Module.load (module.js:565:32)


Functions deploy had errors. To continue deploying other features (such as database), run:
    firebase deploy --except functions

Error: Functions did not deploy properly.
bug

Most helpful comment

I can confirm that simply requiring firebase-functions before initializeApp() fixes the issue.

LOADING TIME: {"projectId":"******","databaseURL":"https://******.firebaseio.com","storageBucket":"******.appspot.com","cloudResourceLocation":"us-central"}
DEFAULT APP OPTIONS: { projectId: '******',
  databaseURL: 'https://******.firebaseio.com',
  storageBucket: '******.appspot.com',
  cloudResourceLocation: 'us-central',
  credential: 
   ApplicationDefaultCredential {
     credential_: MetadataServiceCredential { httpClient: HttpClient {} } } }

Looks like in the prod environment it is the firebase-functions package that sets the environment variable. So if you initialize Admin SDK before requiring that, your code is going to break.

All 33 comments

My hunch is that databaseTrigger.js is getting loaded before admin.initializeApp(); is done. Could you try factoring out the initialization of the admin app into another file, and then require that file in databaseTrigger.js?

I refactored everything to one file

const admin = require('firebase-admin');
admin.initializeApp();
/**
 * Firestore triggers
 */

const functions = require('firebase-functions');
const mkdirp = require('mkdirp-promise');
const vision = require('@google-cloud/vision')();
const spawn = require('child-process-promise').spawn;
const path = require('path');
const os = require('os');
const fs = require('fs');

/**
 * When an image is uploaded we check if it is flagged as Adult or Violence by the Cloud Vision
 * API and if it is we blur it using ImageMagick.
 */
exports.blurOffensiveImages = functions.storage.object().onFinalize(async (object) => {
    const file = admin.storage().bucket(object.bucket).file(object.name);

    // Check the image content using the Cloud Vision API.
    const data = await vision.detectSafeSearch(file);
    const safeSearch = data[0];
    console.log('SafeSearch results on image', safeSearch);

    if (safeSearch.adult || safeSearch.violence) {
        return blurImage(object.name, object.bucket, object.metadata);
    }
    return null;
});

/**
 * Blurs the given image located in the given bucket using ImageMagick.
 */
async function blurImage(filePath, bucketName, metadata) {
    const tempLocalFile = path.join(os.tmpdir(), filePath);
    const tempLocalDir = path.dirname(tempLocalFile);
    const bucket = admin.storage().bucket(bucketName);

    // Create the temp directory where the storage file will be downloaded.
    await mkdirp(tempLocalDir);
    console.log('Temporary directory has been created', tempLocalDir);
    // Download file from bucket.
    await bucket.file(filePath).download({destination: tempLocalFile});
    console.log('The file has been downloaded to', tempLocalFile);
    // Blur the image using ImageMagick.
    await spawn('convert', [tempLocalFile, '-channel', 'RGBA', '-blur', '0x8', tempLocalFile]);
    console.log('Blurred image created at', tempLocalFile);
    // Uploading the Blurred image.
    await bucket.upload(tempLocalFile, {
        destination: filePath,
        metadata: {metadata: metadata}, // Keeping custom metadata.
    });
    console.log('Blurred image uploaded to Storage at', filePath);
    fs.unlinkSync(tempLocalFile);
    console.log('Deleted local file', filePath);
}

and even then it throws

=== Deploying to 'vape-tool'...

i  deploying functions
i  functions: ensuring necessary APIs are enabled...
✔  functions: all necessary APIs are enabled
i  functions: preparing functions directory for uploading...
i  functions: packaged functions (74.65 KB) for uploading
✔  functions: functions folder uploaded successfully
i  functions: updating Node.js 8 function blurOffensiveImages(us-central1)...
i  functions: updating Node.js 8 function sendPhotoCommentNotification(us-central1)...
âš   functions[blurOffensiveImages(us-central1)]: Deployment error.
Function load error: Code in file src/index.js can't be loaded.
Is there a syntax error in your code?
Detailed stack trace: Error: Can't determine Firebase Database URL.
    at FirebaseDatabaseError.FirebaseError [as constructor] (/srv/node_modules/firebase-admin/lib/utils/error.js:39:28)
    at new FirebaseDatabaseError (/srv/node_modules/firebase-admin/lib/utils/error.js:190:23)
    at DatabaseService.ensureUrl (/srv/node_modules/firebase-admin/lib/database/database.js:75:15)
    at DatabaseService.getDatabase (/srv/node_modules/firebase-admin/lib/database/database.js:52:26)
    at FirebaseApp.database (/srv/node_modules/firebase-admin/lib/firebase-app.js:231:24)
    at FirebaseNamespace.fn (/srv/node_modules/firebase-admin/lib/firebase-namespace.js:280:45)
    at Object.<anonymous> (/srv/src/index.js:72:18)
    at Module._compile (module.js:652:30)
    at Object.Module._extensions..js (module.js:663:10)
    at Module.load (module.js:565:32)


Functions deploy had errors. To continue deploying other features (such as database), run:
    firebase deploy --except functions

Error: Functions did not deploy properly.
npm ERR! code ELIFECYCLE
npm ERR! errno 1
npm ERR! functions@ deploy: `firebase deploy --only functions`
npm ERR! Exit status 1
npm ERR! 
npm ERR! Failed at the functions@ deploy script.
npm ERR! This is probably not a problem with npm. There is likely additional logging output above.

npm ERR! A complete log of this run can be found in:
npm ERR!     /Users/stasbar/.npm/_logs/2018-09-17T08_52_19_215Z-debug.log

and here is the log file:


0 info it worked if it ends with ok
1 verbose cli [ '/usr/local/bin/node', '/usr/local/bin/npm', 'run', 'deploy' ]
2 info using [email protected]
3 info using [email protected]
4 verbose run-script [ 'predeploy', 'deploy', 'postdeploy' ]
5 info lifecycle functions@~predeploy: functions@
6 info lifecycle functions@~deploy: functions@
7 verbose lifecycle functions@~deploy: unsafe-perm in lifecycle true
8 verbose lifecycle functions@~deploy: PATH: /usr/local/lib/node_modules/npm/node_modules/npm-lifecycle/node-gyp-bin:/Users/stasbar/Firebase/VapeTool/functions/node_modules/.bin:/usr/local/bin:/usr/bin:/bin:/usr/sbin:/sbin:/usr/local/MacGPG2/bin:/usr/local/sbin:/usr/local/bin:/Users/stasbar/.fastlane/bin:/Users/stasbar/Library/Android/sdk/platform-tools:/Users/stasbar/Library/flutter/bin:/Users/stasbar/.fastlane/bin:/Applications/Android Studio.app/Contents/MacOS:/Users/stasbar/Library/Android/sdk//tools
9 verbose lifecycle functions@~deploy: CWD: /Users/stasbar/Firebase/VapeTool/functions
10 silly lifecycle functions@~deploy: Args: [ '-c', 'firebase deploy --only functions' ]
11 silly lifecycle functions@~deploy: Returned: code: 1  signal: null
12 info lifecycle functions@~deploy: Failed to exec deploy script
13 verbose stack Error: functions@ deploy: `firebase deploy --only functions`
13 verbose stack Exit status 1
13 verbose stack     at EventEmitter.<anonymous> (/usr/local/lib/node_modules/npm/node_modules/npm-lifecycle/index.js:304:16)
13 verbose stack     at emitTwo (events.js:126:13)
13 verbose stack     at EventEmitter.emit (events.js:214:7)
13 verbose stack     at ChildProcess.<anonymous> (/usr/local/lib/node_modules/npm/node_modules/npm-lifecycle/lib/spawn.js:55:14)
13 verbose stack     at emitTwo (events.js:126:13)
13 verbose stack     at ChildProcess.emit (events.js:214:7)
13 verbose stack     at maybeClose (internal/child_process.js:925:16)
13 verbose stack     at Process.ChildProcess._handle.onexit (internal/child_process.js:209:5)
14 verbose pkgid functions@
15 verbose cwd /Users/stasbar/Firebase/VapeTool/functions
16 verbose Darwin 17.7.0
17 verbose argv "/usr/local/bin/node" "/usr/local/bin/npm" "run" "deploy"
18 verbose node v8.11.3
19 verbose npm  v6.2.0
20 error code ELIFECYCLE
21 error errno 1
22 error functions@ deploy: `firebase deploy --only functions`
22 error Exit status 1
23 error Failed at the functions@ deploy script.
23 error This is probably not a problem with npm. There is likely additional logging output above.
24 verbose exit [ 1, true ]

Thanks for sharing! I'm intrigued by this log line: at Object.<anonymous> (/srv/src/index.js:72:18). What's on line 72? When I copied and pasted your code into a text editor, there were only 58 lines.

Can you also add this line somewhere in the global scope just to print out what process.env.FIREBASE_CONFIG look like?

throw new Error(JSON.stringify(process.env.FIREBASE_CONFIG))

Then running deploy again will lead to an error that prints out what Firebase project configuration values, does it have a databaseURL field and does it have the correct URL?

I wanted this code to be shorter so I pasted only the first function, here is the whole index.js content:

'use strict';
// const {admin} = require('./admin');
const admin = require('firebase-admin');
admin.initializeApp();
const db = admin.database();
const functions = require('firebase-functions');

/**
 * Firestore triggers
 */

const mkdirp = require('mkdirp-promise');
const vision = require('@google-cloud/vision')();
const spawn = require('child-process-promise').spawn;
const path = require('path');
const os = require('os');
const fs = require('fs');

/**
 * When an image is uploaded we check if it is flagged as Adult or Violence by the Cloud Vision
 * API and if it is we blur it using ImageMagick.
 */
exports.blurOffensiveImages = functions.storage.object().onFinalize(async (object) => {
    const file = admin.storage().bucket(object.bucket).file(object.name);

    // Check the image content using the Cloud Vision API.
    const data = await vision.detectSafeSearch(file);
    const safeSearch = data[0];
    console.log('SafeSearch results on image', safeSearch);

    if (safeSearch.adult || safeSearch.violence) {
        return blurImage(object.name, object.bucket, object.metadata);
    }
    return null;
});

/**
 * Blurs the given image located in the given bucket using ImageMagick.
 */
async function blurImage(filePath, bucketName, metadata) {
    const tempLocalFile = path.join(os.tmpdir(), filePath);
    const tempLocalDir = path.dirname(tempLocalFile);
    const bucket = admin.storage().bucket(bucketName);

    // Create the temp directory where the storage file will be downloaded.
    await mkdirp(tempLocalDir);
    console.log('Temporary directory has been created', tempLocalDir);
    // Download file from bucket.
    await bucket.file(filePath).download({destination: tempLocalFile});
    console.log('The file has been downloaded to', tempLocalFile);
    // Blur the image using ImageMagick.
    await spawn('convert', [tempLocalFile, '-channel', 'RGBA', '-blur', '0x8', tempLocalFile]);
    console.log('Blurred image created at', tempLocalFile);
    // Uploading the Blurred image.
    await bucket.upload(tempLocalFile, {
        destination: filePath,
        metadata: {metadata: metadata}, // Keeping custom metadata.
    });
    console.log('Blurred image uploaded to Storage at', filePath);
    fs.unlinkSync(tempLocalFile);
    console.log('Deleted local file', filePath);
}

/**
 * Firestore triggers
 */

/**
 * Triggers when a photo gets a new comment and sends a notification to the photo's author.
 *
 * Followers add a flag to `/followers/{followedUid}/{followerUid}`.
 * Users save their device notification tokens to `/users/{followedUid}/notificationTokens/{notificationToken}`.
 */
exports.sendPhotoCommentNotification = functions.database.ref('/gear-comments/{gearUid}/{commentUid}')
    .onCreate(async (snapshot, context) => {
        const gearUid = context.params.gearUid;
        const commentUid = context.params.commentUid;
        const comment = snapshot.val();

        console.log('We have a new comment uid:', commentUid, 'for photo:', gearUid);
        const photoAuthorSnapshot = await db.ref(`/gears/${gearUid}/author`).once('value');
        const photoAuthor = photoAuthorSnapshot.val();

        if (comment.author.uid === photoAuthor.uid) {
            return console.log('Owned commented on his own photo, we can skip');
        }
        const notificationTokensSnapshot = await db.ref(`/users/${photoAuthor.uid}/notificationTokens`).once('value');
        // Check if there are any device tokens.
        if (!notificationTokensSnapshot.hasChildren()) {
            return console.log('There are no notification tokens to send to.');
        }
        console.log('There are', notificationTokensSnapshot.numChildren(), 'tokens to send notifications to.');

        const tokens = Object.keys(notificationTokensSnapshot.val());

        // Notification details.
        const payload = {
            data: {
                commentAuthorUid: comment.author.uid,
                commentAuthorDisplayName: comment.author.displayName,
                commentContent: comment.content,
                commentUid,
                gearUid,
            }
        };

        let response;
        try { // Send notifications to all tokens.
            response = await admin.messaging().sendToDevice(tokens, payload);
            console.log(`Successfully sent message ${JSON.stringify(response)}`)
        } catch (e) {
            console.error(`Error sending message`, e);
            return;
        }
        const tokensToRemove = [];
        response.results.forEach((result, index) => {
            const error = result.error;
            if (error) {
                console.error('Failure sending notification to', tokens[index], error);
                //Cleanup tokens that are no registered anymore
                if (error.code === 'messaging/invalid-registration-token' ||
                    error.code === 'messaging/registration-token-not-registered') {
                    tokensToRemove.push(notificationTokensSnapshot.ref.child(tokens[index]).remove())
                }
            }
        });
        return Promise.all(tokensToRemove);
    });

after some refactoring, it throws at Object.<anonymous> (/srv/src/index.js:5:18)
which is const db = admin.database();

after adding throw new Error(JSON.stringify(process.env.FIREBASE_CONFIG))

Error: Error occurred while parsing your function triggers.

Error: "{\"projectId\":\"vape-tool\",\"databaseURL\":\"https://vape-tool.firebaseio.com\",\"storageBucket\":\"vape-tool.appspot.com\",\"cloudResourceLocation\":\"us-central\"}"
    at Object.<anonymous> (/Users/stasbar/Firebase/VapeTool/functions/src/index.js:18:7)
    at Module._compile (module.js:652:30)
    at Object.Module._extensions..js (module.js:663:10)
    at Module.load (module.js:565:32)
    at tryModuleLoad (module.js:505:12)
    at Function.Module._load (module.js:497:3)
    at Module.require (module.js:596:17)
    at require (internal/module.js:11:18)
    at /usr/local/lib/node_modules/firebase-tools/lib/triggerParser.js:21:11
    at Object.<anonymous> (/usr/local/lib/node_modules/firebase-tools/lib/triggerParser.js:75:3)

and indeed, it match my databaseURL

Thanks for confirming, I wonder if this is a bug with firebase-admin. To isolate the issue, can you try adding this line to the top of your file: (before initializing firebase-admin)

process.env.FIREBASE_CONFIG=JSON.stringify({
   projectId: ..., // replace with your projectId
   databaseURL: ...// replace with yours 
})

Then run the following in your terminal while in the functions directory:
node ./index.js

And if you still get the same error, then it's probably a bug with firebase-admin. Then can you create another file with just the admin and database-related lines to make a more minimal repro and file an issue to https://github.com/firebase/firebase-admin-node

I did what you suggest

'use strict';
process.env.FIREBASE_CONFIG = JSON.stringify({
    projectId: 'vape-tool',
    databaseURL: 'https://vape-tool.firebaseio.com'
});

const admin = require('firebase-admin');
admin.initializeApp();
const db = admin.database();
const functions = require('firebase-functions');

which gave me different error

~/Firebase/VapeTool/functions/src(master*) » node ./index.js                                                                                                     
/Users/stasbar/Firebase/VapeTool/functions/node_modules/@google-cloud/vision/node_modules/@google-cloud/common/src/util.js:545
    throw util.missingProjectIdError;
    ^

Error: Sorry, we cannot connect to Google Cloud Services without a project ID. You may specify one with an environment variable named "GCLOUD_PROJECT". See https://googlecloudplatform.github.io/google-cloud-node/#//docs/guides/authentication for a detailed guide on creating an authenticated connection.
    at Object.<anonymous> (/Users/stasbar/Firebase/VapeTool/functions/node_modules/@google-cloud/vision/node_modules/@google-cloud/common/src/util.js:54:29)
    at Module._compile (module.js:652:30)
    at Object.Module._extensions..js (module.js:663:10)
    at Module.load (module.js:565:32)
    at tryModuleLoad (module.js:505:12)
    at Function.Module._load (module.js:497:3)
    at Module.require (module.js:596:17)
    at require (internal/module.js:11:18)
    at Object.<anonymous> (/Users/stasbar/Firebase/VapeTool/functions/node_modules/@google-cloud/vision/node_modules/@google-cloud/common/src/service.js:30:12)
    at Module._compile (module.js:652:30)

so I added envvar which gave me different errors:

~/Firebase/VapeTool/functions/src(master*) » GCLOUD_PROJECT=vape-tool node ./index.js    

[2018-09-19T09:55:48.153Z]  @firebase/database: FIREBASE WARNING: {"code":"app/invalid-credential","message":"Credential implementation provided to initializeApp() via the \"credential\" property failed to fetch a valid Google OAuth2 access token with the following error: \"getaddrinfo ENOTFOUND metadata.google.internal metadata.google.internal:80\"."} 
[2018-09-19T09:55:49.103Z]  @firebase/database: FIREBASE WARNING: {"code":"app/invalid-credential","message":"Credential implementation provided to initializeApp() via the \"credential\" property failed to fetch a valid Google OAuth2 access token with the following error: \"getaddrinfo ENOTFOUND metadata.google.internal metadata.google.internal:80\"."} 
[2018-09-19T09:55:49.587Z]  @firebase/database: FIREBASE WARNING: {"code":"app/invalid-credential","message":"Credential implementation provided to initializeApp() via the \"credential\" property failed to fetch a valid Google OAuth2 access token with the following error: \"getaddrinfo ENOTFOUND metadata.google.internal metadata.google.internal:80\"."} 
[2018-09-19T09:55:51.002Z]  @firebase/database: FIREBASE WARNING: {"code":"app/invalid-credential","message":"Credential implementation provided to initializeApp() via the \"credential\" property failed to fetch a valid Google OAuth2 access token with the following error: \"getaddrinfo ENOTFOUND metadata.google.internal metadata.google.internal:80\"."} 
^C

so I changed to Admin SDK auth:

'use strict';
process.env.FIREBASE_CONFIG = JSON.stringify({
    projectId: 'vape-tool',
    databaseURL: 'https://vape-tool.firebaseio.com'
});
const admin = require("firebase-admin");

const serviceAccount = require("./vape-tool-firebase-adminsdk.json");

admin.initializeApp({
    credential: admin.credential.cert(serviceAccount),
    databaseURL: "https://vape-tool.firebaseio.com"
});

const db = admin.database();
const functions = require('firebase-functions');

and executed

~/Firebase/VapeTool/functions/src(master*) » GCLOUD_PROJECT=vape-tool-pro node ./index.js                                                                        

Which blocks and doesn't print anything so I guess it's working

Thanks for the detailed diagnosis! I am quite puzzled by this. I know this is not a satisfying answer, but could you move const db = admin.database() into the sendPhotoCommentNotification function? Then maybe you will be able to deploy. (The trigger parsing process during deployment only executes global scope code, so code that is inside the function will not cause any issues)

✔  functions[blurOffensiveImages(us-central1)]: Successful update operation. 
✔  functions[sendPhotoCommentNotification(us-central1)]: Successful update operation. 

✔  Deploy complete!

Oh, but now it crashes inside the function

Error: Can't determine Firebase Database URL.
    at FirebaseDatabaseError.FirebaseError [as constructor] (/srv/node_modules/firebase-admin/lib/utils/error.js:39:28)
    at new FirebaseDatabaseError (/srv/node_modules/firebase-admin/lib/utils/error.js:190:23)
    at DatabaseService.ensureUrl (/srv/node_modules/firebase-admin/lib/database/database.js:75:15)
    at DatabaseService.getDatabase (/srv/node_modules/firebase-admin/lib/database/database.js:52:26)
    at FirebaseApp.database (/srv/node_modules/firebase-admin/lib/firebase-app.js:231:24)
    at FirebaseNamespace.fn (/srv/node_modules/firebase-admin/lib/firebase-namespace.js:280:45)
    at exports.sendPhotoCommentNotification.functions.database.ref.onCreate (/srv/src/databaseTriggers.js:29:26)
    at cloudFunctionNewSignature (/srv/node_modules/firebase-functions/lib/cloud-functions.js:105:23)
    at /worker/worker.js:731:24
    at <anonymous>

line 29 is const db = admin.database();

I tried with NodeJS6 but still the same

exports.sendPhotoCommentNotification = functions.database.ref('/gear-comments/{gearUid}/{commentUid}')
    .onCreate((snapshot, context) => {
        console.error(JSON.stringify(process.env.FIREBASE_CONFIG));
        const db = admin.database();
        ...
"{\"projectId\":\"vape-tool\",\"databaseURL\":\"https://vape-tool.firebaseio.com\",\"storageBucket\":\"vape-tool.appspot.com\",\"cloudResourceLocation\":\"us-central\"}"

Error: Can't determine Firebase Database URL.
    at FirebaseDatabaseError.Error (native)
    at FirebaseDatabaseError.FirebaseError [as constructor] (/user_code/node_modules/firebase-admin/lib/utils/error.js:39:28)
    at new FirebaseDatabaseError (/user_code/node_modules/firebase-admin/lib/utils/error.js:190:23)
    at DatabaseService.ensureUrl (/user_code/node_modules/firebase-admin/lib/database/database.js:75:15)
    at DatabaseService.getDatabase (/user_code/node_modules/firebase-admin/lib/database/database.js:52:26)
    at FirebaseApp.database (/user_code/node_modules/firebase-admin/lib/firebase-app.js:231:24)
    at FirebaseNamespace.fn (/user_code/node_modules/firebase-admin/lib/firebase-namespace.js:280:45)
    at exports.sendPhotoCommentNotification.functions.database.ref.onCreate (/user_code/src/databaseTriggers.js:29:26)
    at cloudFunctionNewSignature (/user_code/node_modules/firebase-functions/lib/cloud-functions.js:105:23)
    at cloudFunction (/user_code/node_modules/firebase-functions/lib/cloud-functions.js:135:20)

@laurenzlong any ideas ?

@hiranya911 Do you have any clues here as to why firebase-admin would complain about not being able to determine Firebase database URL even though process.env.FIREBASE_CONFIG shows the right values?

Any update on this topic?
I just decided to update my firebase functions project with some new features, and I can't seem to serve it locally at all, getting same database exception

Nope, I'm still waiting. All my functions are dead now.

@stasbar I fixed my problem by replacing functions.config().firebase to JSON.stringify(process.env.FIREBASE_CONFIG)

@stasbar Can you also try passing projectId in when you initializeApp?

admin.initializeApp({
    projectId: 'vape-tool',
    credential: admin.credential.cert(serviceAccount),
    databaseURL: "https://vape-tool.firebaseio.com"
});

And I noticed when you're setting the GCLOUD_PROJECT, you set it to vape-tool-pro instead of vape-tool.

Are you working with two different projects or multiple databases?

@kevinajian
I just wanted to 'obfuscate' my IDs, but never mind now.
I'm working with one project, one database.

@kevinajian btw. admin.initializeApp({with object argument}) is used for Admin SDK only, right ?

@AAverin Are you aware that JSON.stringify(process.env.FIREBASE_CONFIG), just change an object to string? How could that fix your problem?

@stasbar yes the initializeApp is for the Admin SDK. Did that change anything?

@stasbar yes the initializeApp is for the Admin SDK. Did that change anything?

with AdminSDK it seems to be working fine.

process.env.FIREBASE_CONFIG = JSON.stringify({
    projectId: 'vape-tool',
    databaseURL: "https://vape-tool.firebaseio.com"
});

const admin = require("firebase-admin");

const serviceAccount = require("../vape-tool-firebase.json");

admin.initializeApp({
    projectId: 'vape-tool',
    credential: admin.credential.cert(serviceAccount),
    databaseURL: "https://vape-tool.firebaseio.com"
});
const db = admin.database();
console.log("Working fine");

```bash
$ node crash.js
Working fine
^C

I have fixed the issue by adding

process.env.FIREBASE_CONFIG = JSON.stringify({
    projectId: 'vape-tool',
    databaseURL: "https://vape-tool.firebaseio.com",
    storageBucket: "vape-tool.appspot.com"
});

at the top of index.ts

@stasbar I ran into this issue as well. I feel it would help to keep this issue open until it has been fixed in firebase library. Ideally we shouldn't need to do anything apart from admin.initializeApp()

That's right. I hope my workaround worked in your case too.

Yes it did. Thanks!
But just need to be sure that my if else condition will work when deployed to production environment as well.

if (process.env.GCLOUD_PROJECT === '<dev-project>') {
    process.env.FIREBASE_CONFIG = JSON.stringify({
        projectId: process.env.GCLOUD_PROJECT,
        databaseURL: "...",
        storageBucket: "..."  
    });
} else {
    process.env.FIREBASE_CONFIG = JSON.stringify({
        projectId: process.env.GCLOUD_PROJECT,
        databaseURL: "...",
        storageBucket: "..."
    });
}

@stasbar thank you for your feedback and I'm glad the workaround helped. This is still a puzzling issue and we will look into why this is happening. Internal bug reference: 120908637

I'm not able to reproduce this in the Functions emulator. Trying with the production environment next. My function:

'use strict';
// const {admin} = require('./admin');
const admin = require('firebase-admin');

admin.initializeApp();

const functions = require('firebase-functions');

const mkdirp = require('mkdirp-promise');
const vision = require('@google-cloud/vision');
const spawn = require('child-process-promise').spawn;
const path = require('path');
const os = require('os');
const fs = require('fs');

exports.sendPhotoCommentNotification = functions.database.ref('/gear-comments/{gearUid}/{commentUid}')
    .onCreate(async (snapshot, context) => {
        console.log('DEFAULT APP OPTIONS:', admin.app().options);
        console.log('FIREBASE_CONFIG:', process.env.FIREBASE_CONFIG);
        const db = admin.database();
        return db.ref('scores').once('value').then((snap) => {
          console.log(snap.val());
        })
    });

Emulator output:

firebase > sendPhotoCommentNotification({author: {uid: 'foo'}}, {params: {uid: 'foo'}})
'Successfully invoked function.'
firebase > info: User function triggered, starting execution
info: DEFAULT APP OPTIONS: { projectId: '*******',
  databaseURL: 'https://*******.firebaseio.com',
  storageBucket: '*******.appspot.com',
  cloudResourceLocation: 'us-central',
  credential: 
   ApplicationDefaultCredential {
     credential_: RefreshTokenCredential { refreshToken: [Object], httpClient: HttpClient {} } } }
info: FIREBASE_CONFIG: {"projectId":"*******","databaseURL":"https://*******.firebaseio.com","storageBucket":"*******.appspot.com","cloudResourceLocation":"us-central"}
info: {key: 'value'}
info: Execution took 480 ms, user function completed successfully

I have a repro in production environment:


FIREBASE_CONFIG: {"projectId":"*******","databaseURL":"https://*******.firebaseio.com","storageBucket":"*******.appspot.com","cloudResourceLocation":"us-central"} 
DEFAULT APP OPTIONS: { credential: 
   ApplicationDefaultCredential {
     credential_: MetadataServiceCredential { httpClient: HttpClient {} } } }

It seems FirebaseApp is getting initialized before the environment variable becomes visible. @laurenzlong any idea how that could happen?

Ok that is indeed the case. I added a log statement just before the initializeApp():

console.log('LOADING TIME:', process.env.FIREBASE_CONFIG);
admin.initializeApp();

And the output:

LOADING TIME: undefined

So when the app gets initialized the env variable is not available to the runtime. I have a feeling that we need to load firebase-functions for this to get set. Gonna try that next.

I can confirm that simply requiring firebase-functions before initializeApp() fixes the issue.

LOADING TIME: {"projectId":"******","databaseURL":"https://******.firebaseio.com","storageBucket":"******.appspot.com","cloudResourceLocation":"us-central"}
DEFAULT APP OPTIONS: { projectId: '******',
  databaseURL: 'https://******.firebaseio.com',
  storageBucket: '******.appspot.com',
  cloudResourceLocation: 'us-central',
  credential: 
   ApplicationDefaultCredential {
     credential_: MetadataServiceCredential { httpClient: HttpClient {} } } }

Looks like in the prod environment it is the firebase-functions package that sets the environment variable. So if you initialize Admin SDK before requiring that, your code is going to break.

@stasbar @nikhilag If possible can you try the suggested fix of putting
const functions = require('firebase-functions');
before calling admin.initializeApp();?

@kevinajian Yes, it fixed the problem.

Thanks! Will close this out. We will work on adding better documentation for this issue.

Fixed for me using:
databaseURL in place of databaseUrl (case sensitive).

Was this page helpful?
0 / 5 - 0 ratings