Can't determine Firebase Database URL when trying to read Firebase Database from within Node.js Firebase function - node.js

I am using Flutter to build an app that accesses the Firebase database. All good from the Flutter side....but I am new to Node.js and Cloud Functions. I am trying to create a Node.js function to react to a deletion event of a record on one Firebase Database node and then delete records from two other Firebase Database nodes and image files from Firestore.
I am reacting to the trigger event with a functions.database.onDelete call, no problem, but falling at the very next hurdle i.e.trying to read admin.database to get a snapshot.
I have created a dummy function that uses .onUpdate to pick up a trigger event (don't want to keep having to recreate my data as I would if I used .onDelete) and then tries to read my Firebase Database to access a different node. The trigger event is picked up fine but I don't seem to have a database reference Url to do the read...yet it is the same database. Output on the console log from a call to process.env.FIREBASE_CONFIG shows the Url is present.
The included function code also has commenting to show the various outputs I get on the console log.
I am going crazy over this.....PLEASE can anyone tell me where I am going wrong. Been searching Google, Stackoverflow, Firebase docs for the last two days :-(
const admin = require("firebase-admin"); // Import Admin SDK
const functions = require("firebase-functions"); // Import Cloud Functions
admin.initializeApp({
credential: admin.credential.cert(
require("./user-guy-firebase-adminsdk.json")
)
});
exports.testDeleteFunction = functions.database
.ref("/user-guys/{userGuyId}")
// Using .onUpdate as I don't want to keep restoring my data if I use .onDelete
.onUpdate((snapshot, context) => {
const userData = snapshot.after.val();
const userId = userData.userId;
console.log('userId: ' + userId); // Gives correct console log output: userId: wajzviEcUvPZEcMUbbkPzcw64MB2
console.log(process.env.FIREBASE_CONFIG);
// Gives correct console log output:
// {"projectId":"user-guy","databaseURL":"https://user-guy.firebaseio.com","storageBucket":"user-guy.appspot.com","cloudResourceLocation":"us-central"}
// I have tried each of the four following calls and received the console log message as indicated after each.
//
// var root = admin.database.ref(); // Console Log Message: TypeError: admin.database.ref is not a function
// var root = admin.database().ref(); // Console Log Message: Error: Can't determine Firebase Database URL.
// var root = admin.database.ref; // Fails at line 31 below with the message indicated.
// var root = admin.database().ref; // Console Log Message: Error: Can't determine Firebase Database URL.
console.log(root.toString); // Console Log Message: TypeError: Cannot read property 'toString' of undefined.
// This is intended to read a chat thread for two users but processing never gets here.
var database = root.child('chats').child('0cSLt3Sa0FS26QIvOLbin6MFsL43GUPYmmAg9UUlRLnW97jpMCAkEHE3');
database
.once("value")
.then(snapshot => {
console.log(snapshot.val());
}, function (errorObject) {
console.log("The read failed: " + errorObject.code);
});
return null; // Will do stuff here once working.
});
Error messages shown in code comments.

If you want to use the configuration in FIREBASE_CONFIG, you should initialize the Admin SDK with no parameters:
admin.initializeApp();
This will use the default service account for your project, which should have full read and write access to the database.

You need to add your database url in admin.initializeApp
admin.initializeApp({
databaseURL:"your_database_url"
});

select Realtime Database in firebase and copy your url add in settings in fire config or watch this video https://www.youtube.com/watch?v=oOm_9y3vb80
config example
const config = {
apiKey: "",
authDomain: "",
projectId: "",
databaseURL: "https://youUrl.firebaseio.com/",
storageBucket: "",
messagingSenderId: "",
appId: "",
measurementId: ""
};

See:
https://firebase.google.com/docs/admin/setup#initialize_without_parameters
https://firebase.google.com/docs/functions/database-events
Try initialize without parameters.
The SDK can also be initialized with no parameters. In this case, the SDK uses Google Application Default Credentials and reads options from the FIREBASE_CONFIG environment variable. If the content of the FIREBASE_CONFIG variable begins with a { it will be parsed as a JSON object. Otherwise the SDK assumes that the string is the name of a JSON file containing the options.
const admin = require("firebase-admin"); // Import Admin SDK
const functions = require("firebase-functions"); // Import Cloud Functions
admin.initializeApp();

Same issue 3 years later...
After DAYS! I found out all of the Flutter documents have code for Firestore instead of Firebase.
Basically there are two products. Firestore Database, and Real-time Database. You are calling the Real-time Database methods, but probably have a Firestore database.
Try admin.firebase().ref('/some_collection').push();
Basically everywhere you're calling .database(), replace it with .firebase() if you are using Firebase. Yes, the Flutter tutorials are mis-leading!!!

Related

Firestore simple node script on pc doesn't work even though same code runs perfectly on firebase functions

Hello I am running a small script that I want to run locally since max timeout of firebase functions is 9 minutes and that is not enough for me (I have to run a large scale update on data types).
So the code is basically:
const admin = require('firebase-admin');
admin.initializeApp();
const db = admin.firestore();
db.collection('users')
.get()
.then(querySnapshot => {
querySnapshot.docs.forEach(doc => {
// update doc
});
});
But querySnapshot.docs has 0 elements. I checked the same code in functions and it works properly. What could be the cause of this? If this is not possible are there any workarounds where I can bypass timeout using cloud functions?
Firebase is initialized correctly both in my machine and directory. I tried a clean initialized directory too. Same code when passed to an firebase function endpoint and ran once works perfectly fine.
If you run a script written with the Admin SDK locally on your computer you need to initialize the SDK with an exported service account key file as explained in the doc.
The doc details how to:
Generate a private key file in JSON format from the Firebase console
Set an environment variable to the file path of the JSON file that contains your service account key.
Then you can do as follows:
const admin = require('firebase-admin');
admin.initializeApp({
credential: applicationDefault()
});
const db = admin.firestore();
db.collection('users')
.get()
.then(querySnapshot => {
return Promise.all(querySnapshot.docs.map(doc => doc.ref.update( {...} ))):
})
.then(() => {...})
.catch(...)
Note that to update several docs within a loop via the asynchronous update() method you'll need to use Promise.all(), as shown above.
When I initialized like this everything seemed to work fine. The reason why applicationDefault doesn't work is I think because doc says it works in google environments.
const admin = require('firebase-admin');
var serviceAccount = require('path/to/key.json')
admin.initializeApp({
credential: admin.credential.cert(serviceAccount)
});

Firebase Cloud Function Keep return "Request failed with status code 404"

I created some functions in firebase cloud functions, but all of them are works. But i have a new function which is not work properly. I don't know why but i think it has same pattern with others.
this is my code:
const functions = require('firebase-functions');
const appVideo = express();
const cors = require('cors')({ origin: true });
appVideo.use(cors);
appVideo.get('/update-video', async(req, res) => {
console.log('updateStatusVideo idCourse', req.query.idCourse, ' idMateri: ', req.query.idMateri, ' idVideo:', req.query.idVideo);
res.status(200).send('Oke')
})
exports.video = functions.https.onRequest(appVideo)
I often call partial deploy like
firebase deploy --only functions:video. But when i execute the functions https through browser it often return
Request failed with status code 404
other weird things is when i inspect the browser and switch to console, i found
Failed to load resource: the server responded with a status of 500 ()
this is the url of function in firebase:
https://us-central1-my-apps.cloudfunctions.net/video [modified for confidential]
Please help
When you export this line:
exports.video = functions.https.onRequest(appVideo);
You define a Cloud Function called video that is deployed as https://us-central1-PROJECT_ID.cloudfunctions.net/video where PROJECT_ID is whatever your Firebase Project ID is.
Because you use a express application for this exported function, any URL that is handled must first start with https://us-central1-PROJECT_ID.cloudfunctions.net/video (the BASE_URL).
This line:
appVideo.get('/update-video', ...)
attaches a listener to BASE_URL/update-video, which becomes https://us-central1-PROJECT_ID.cloudfunctions.net/video/update-video.
If you want to use just https://us-central1-PROJECT_ID.cloudfunctions.net/video as-is, you'll need to change to using
appVideo.get('/', ...)
If you want to use just https://us-central1-PROJECT_ID.cloudfunctions.net/update-video, you'll need to change to using
appVideo.get('/', ...)
and
exports.update = {};
exports.update.video = functions.https.onRequest(appVideo);
// "update.video" is deployed as "update-video"
Note: This last part abuses deploying groups to get the desired URL

keep fetching data up to date

I have just a question I want to ask if anybody have an idea about it.
I'm building a full stack application backed by nodejs and using typescript for it, in my nodejs app I'm making a fetch for an API that later on I will serve it to the user but I have one small issue, I'm using node-fetch for now but the data which are fetched are changing all the time eg. now I have 10 entries, after 5 seconds I have 30 entries, so is there a way or mechanism to make my fetching to the data with nodejs up to date by fetching them in the background?
Thanks in advance!
Easiest solution to implement and good in actual sense for making your web app realtime https://pusher.com/
This is how you can handle pusher within your NodeJS App
import Pusher from 'pusher'
//Below are the keys that you will get from pusher when you go to getting started
// within your Dashboard
const pusher = new Pusher({
appId: "<Your app id provided by pusher>",
key: "<Key id provided by pusher>",
secret: "<Secret key given by pusher>",
cluster: "<cluster given by pusher",
useTLS: true
});
Now you want to setup a changeStream for your Collection in MongoDB
const db = mongoose.collection;
db.once('open', ()=>{
const postCollection = db.collection('posts')//This will be dependent on the collection you want to watch
const changeStream = postCollection.watch()//Make sure the collection name above are acurate
changeStream.on('change', (change)=>{
const post = change.fullDocument;//Change bring back content that change in DB Collection
if (change.operationType === 'insert'){
pusher.triger('<write channel for your pusher>', '<event in this case inser>', {
newPost:post
})
}
})
})
By that setup your pusher and backend is working now is time to setup frontend
If your usin VanillaJS the Pusher getting started has code for you
If your using ReactJS here's is the code below
import Pusher from 'pusher-js'
useEffect(()=>{
Pusher.logToConsole = true;
var pusher = new Pusher('<Key received from pusher>', {
cluster: '<cluster received from pusher>'
});
var channel = pusher.subscribe('<channel name that you wrote in server');
channel.bind('<event that you wrote in sever',(data)=> {
alert(JSON.stringify(data)); // This will be the data entries coming as soon as they enter DB then you can update your state by using spread operators to maintain what you have and also add new contents
});
//Very important to have a clean-up function to render this once
return ()=>{
pusher.unbind();
pusher.unsubscribe_all();
}
})
Now like this you have everything being realtime

Is it possible to access firebase on both server side and client side in NodeJS?

I'm working an app that need to access the firebase database on both client-side (to catch "on" event to grab recently added data) and server-side (to add record to the database). I'm not sure that I did it correctly that use 2 db instances are required on both client and server OR create 1 on the server and use it on the client cause I got a warning on the local side saying that Firebase is already defined in the global scope.
This is how I declare the db instance on server-side, this works perfectly fine:
var admin = require("firebase-admin");
var serviceAccount = require("../config/sterminal-0000-firebase-adminsdk-bvuit-ce5ee771bc.json");
admin.initializeApp({
credential: admin.credential.cert(serviceAccount),
databaseURL: "https://sterminal-0000.firebaseio.com"
});
module.exports.admin = admin;
...
const {admin} = require('../extra/firebase');
let db = admin.database();
This is how I declare the db instance on client-side:
Include the libs to pug template
script(src="https://www.gstatic.com/firebasejs/7.14.2/firebase-app.js")
script(src="https://www.gstatic.com/firebasejs/7.14.2/firebase-database.js")
script(src="/javascripts/local.js" crossorigin="anonymous")
And in local script:
var firebaseConfig = {
apiKey: "AIzaSyCkip7lcHNNodJNhrO5n0Hog5Kvs100000",
authDomain: "sterminal-00000.firebaseapp.com",
databaseURL: "https://sterminal-00000.firebaseio.com",
projectId: "sterminal-8bf73",
storageBucket: "sterminal-00000.appspot.com",
messagingSenderId: "961511900000",
appId: "1:96151100000:web:f3cec40f38f7b7d4000000"
};
firebase.initializeApp(firebaseConfig);
firebase.database().ref("messages").on("child_added", function (snap) {
console.log(snap.val().message);
}); // ----> DOES NOT WORK
I got a warning on local-side:
logger.ts:115 [2020-04-27T18:43:48.559Z] #firebase/app:
Warning: Firebase is already defined in the global scope. Please make sure
Firebase library is only loaded once.
Yes, you can access Firebase Realtime Database from both client-side Node.js and server-side Node.js. But the SDK are separate, and you can't include both of them in a single project. The latter seems to be what is leading to the error you now have.

Algolia Nodejs Firebase syncing - .on('child_added') won't get called

I have a NodeJS script in which I'm watching trees in my Firebase database to see if an entry has been added/changed/removed via the child_added, childed_changed, and child_removed properties of the Firebase Admin .on() function in order to sync entries with my Algolia database.
The problem is that when I add/change/remove an item on these trees, the functions themselves never get fired.
However, when I start the script, I also have a .once('value', initialImport) function I call that works fine.
So for example, here is my relevant child_added code in my NodeJS script:
var dotenv = require('dotenv');
var firebaseAdmin = require("firebase-admin");
var algoliasearch = require('algoliasearch');
// load values from the .env file in this directory into process.env
dotenv.load();
var serviceAccount = require("./serviceAccountKey.json");
firebaseAdmin.initializeApp({
credential: firebaseAdmin.credential.cert(serviceAccount),
databaseURL: process.env.FIREBASE_DATABASE_URL
});
var database = firebaseAdmin.database();
var algolia = algoliasearch(process.env.ALGOLIA_APP_ID, process.env.ALGOLIA_API_KEY);
var users = algolia.initIndex('users');
var bandsRef = database.ref("/social/bands");
bandsRef.on('child_added', addOrUpdateBandIndexRecord);
/**
* Add or update a band index record
*/
function addOrUpdateBandIndexRecord(dataSnapshot) {
// Get Firebase object
var firebaseObject = dataSnapshot.val();
// Specify Algolia's objectID using the Firebase object key
firebaseObject.objectID = dataSnapshot.key;
// Add or update object
bands.saveObject(firebaseObject, function(err, content) {
if (err) {
throw err;
}
console.log('Firebase<>Algolia band object saved', firebaseObject.objectID);
});
}
This never runs :/ Is there something I'm doing wrong? This used to work once upon a time.
UPDATE: The issue I was having was that I was also calling a .once() on the ref that I was adding the .on('child_added') and this was causing a conflict. Once I removed the code for .once(), it solved the syncing issue. In order to do an initial import, you'll most likely want that code in a separate node file. This is what worked for me, anyway.

Resources