How to access multiple Realtime Database instances in Cloud Functions for Firebase
S

5

18

I'm using multiple databases in a Firebase project. Cloud functions for the main (default) database work great, however, I cannot make them work for a secondary database. For example I want to make a read request on a node with admin privileges:

//this works
admin.database().ref(nodePath).once('value')...

This works in the main database, however, if I want to execute the command on another database, it doesn't work:

//this doesn't work
admin.database(secondaryDatabaseUrl).ref(nodePath).once('value')...

Although the functions are deployed, I get an error on the console when trying to execute the cloud function.

Here's the code for the cloud function with an https trigger:

const functions = require('firebase-functions');
const admin = require('firebase-admin');
admin.initializeApp(functions.config().firebase);
const secureCompare = require('secure-compare');

exports.testFunction= functions.https.onRequest((req, res) => {
  const key = req.query.key;
  // Exit if the keys don't match
  if (!secureCompare(key, functions.config().cron.key)) {
    console.error('keys do not match');
    res.status(403).send('error1');
    return;
  }
  //test read request
  //the line below crashes the function
  return admin.database('https://secondary_db_url.firebaseio.com').ref(`/testNode`).once('value').then(dataSnapshot=> {
    console.log('value', dataSnapshot.val());
    return;
  }).catch(er => {
    console.error('error', er);
    res.status(403).send('error2');
  });
});

Below is the error log in the Firebase console:

TypeError: ns.ensureApp(...).database is not a function
    at FirebaseNamespace.fn (/user_code/node_modules/firebase-admin/lib/firebase-namespace.js:251:42)
    at exports.testFunction.functions.https.onRequest (/user_code/index.js:16:16)
    at cloudFunction (/user_code/node_modules/firebase-functions/lib/providers/https.js:26:41)
    at /var/tmp/worker/worker.js:671:7
    at /var/tmp/worker/worker.js:655:9
    at _combinedTickCallback (internal/process/next_tick.js:73:7)
    at process._tickDomainCallback (internal/process/next_tick.js:128:9)

If I don't specify the secondary database URL, the function will make the read request on my main database which works great:

//this works
return admin.database().ref(`/testNode`).once('value').then(dataSnapshot=> {
...

I'm using the latest SDK versions: "firebase-admin": "^5.5.1" and "firebase-functions": "^0.7.3"

So, how do I get an instance of a secondary database in cloud functions using admin privileges?

Sapers answered 18/12, 2017 at 2:25 Comment(2)
Is that really the only helpful thing to see in the logs? Please show complete code that demonstrates the problem. Are you catching errors from promises?Stepheniestephens
Hi Doug, you can check my updated question. Actually the error logs in the Firebase console started to appear after a few minutes, that's why I didn't specify them earlier.Sapers
D
23

Here's how to access database by URL using Admin SDK:

let app = admin.app();
let ref = app.database('https://secondary_db_url.firebaseio.com').ref();

Here's an example from Admin SDK integration tests: https://github.com/firebase/firebase-admin-node/blob/master/test/integration/database.js#L52

Disoperation answered 19/12, 2017 at 2:9 Comment(4)
Are these multiple databases on same project or from different firebase projects?Tyrrell
They are from same project.Disoperation
How can I add a secondary database in the firestore for same project? Is it possible?Tyrrell
@Tyrrell https://mcmap.net/q/110603/-how-to-create-multi-environment-db-39-s-with-firestore this answer from mr Stevenson might help for multiple firestore instances idea! quite late but hope it helps future devs [:Cherisecherish
T
12

With cloud functions > 1.1 now, here is the documentation link that saved my life on this issue.

https://firebase.google.com/docs/database/usage/sharding#connect_your_app_to_multiple_database_instances

So, it looks like this at the top of my my cloud function index.js :

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

const dev = admin.initializeApp({
  databaseURL: "https://appdev.firebaseio.com"
}, 'dev');

const v2 = admin.initializeApp({
  databaseURL: "https://appv2.firebaseio.com"
}, 'v2');

and then, in my clond functions functions code I can do :

//will change stuff on default database
admin.database().ref().child(`stuff/${stuffId}`).set(myStuff)

//will change stuff on my dev database
admin.database(dev).ref().child(`stuff/${stuffId}`).set(myStuff)

//will change stuff on my v2 database
admin.database(v2).ref().child(`stuff/${stuffId}`).set(myStuff)
Threefold answered 5/7, 2018 at 14:15 Comment(1)
admin.database(v2).ref().child(stuff/${stuffId}).set(myStuff)Formation
S
11

So it looks like you are trying to access multiple databases using the javascript web client API. Passing the URL of the database to the API like this doesn't work with the Admin SDK:

admin.database('https://secondary_db_url.firebaseio.com').ref(`/testNode`)

Instead, you have to initialize a second app, give it a name, and pass that app around to the Admin SDK APIs. Here's a complete sample that writes the same data to two different database instances in the same project:

const functions = require('firebase-functions')
const admin = require('firebase-admin')
admin.initializeApp(functions.config().firebase)

const otherConfig = Object.assign({}, functions.config().firebase)
otherConfig.databaseURL = 'https://your-other-db.firebaseio.com/'
const otherApp = admin.initializeApp(otherConfig, 'otherAppName')

exports.foo = functions.https.onRequest((req, res) => {
    const data = { foo: 'bar' }
    const p1 = admin.database().ref('data').set(data)
    const p2 = admin.database(otherApp).ref('data').set(data)
    Promise.all([p1, p2]).then(() => {
        res.send("OK")
    })
    .catch(error => {
        res.status(500).send(error)
    })
})
Stepheniestephens answered 18/12, 2017 at 22:7 Comment(4)
Thanks, that worked! Hiranya Jayathilaka's answer worked as well.Sapers
So I tried const db2 = admin.app()database('https://secondary_db_url.firebaseio.com').ref(); and tried updating, it showed log for successful updation but there was no change in the secondary db? why there was no error if this doesn't work?Starlight
this is outdated now with the end of the realtime database beta. see my answer below for the new way.Threefold
Is this the correct answer in 2022? The blog articles linked in one of the other answers seems to suggest separate app instance is not required. Could you please confirm/ ThanksGetaway
S
6

Updating this while on Firebase Functions v3.14.0. None of this answers worked for me so I implemented this solution

instance Registers a function that triggers on events from a specific Firebase Realtime Database instance

functions.database.instance('my-app-db-2').ref('/foo/bar')

Use the name of the database instance and it works, no need for the url. functions.database.ref used without instance watches the default instance for events.

Stamp answered 13/5, 2021 at 10:57 Comment(0)
S
0

So if both the answers doesn't work.

What happened with me is both the method worked without any error but second instance of database was not getting updated.

I updated npm and firebase CLI it worked.

Also @Dough Stevenson you Passing the URL of the database to the API like this **does** work with the Admin SDK

And this is a good blog from Firebase about the same Firebase Blog : Easier scaling with multi-database support!

Starlight answered 20/2, 2018 at 11:12 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.