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

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.

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)
});

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

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!!!

Error: The default Firebase app does not exist

I'm tryin to do a simple function about Reading Data Once. but I get error like this:
index.js
var firebase = require("firebase");
var config = {
apiKey: "some api key",
authDomain: "fitto-exercise.firebaseapp.com",
databaseURL: "https://fitto-exercise.firebaseio.com",
projectId: "fitto-exercise",
storageBucket: "fitto-exercise.appspot.com",
messagingSenderId: "some number"
};
firebase.initializeApp(config);
and the search.js is here:
var firebase = require('.');
var admin = require("firebase-admin");
var db = admin.database();
var ref = db.ref("exercises/exercise");
ref.once("58.967", function(data) {
console.log("Got it!");
});
index.js and search.js in the same directory
I'm still inexperienced about this stuff maybe there are some noob mistakes.
This is my database looks like:
A couple problems here. If you're trying to require() some other file, it needs to have an export defined. In index.js (if that's what you're trying to require), doesn't have an export.
I'd suggest using just a single file for now, until you get your most basic code to work.
Also, according to the API documentation for once(), the method accepts, as its first argument:
One of the following strings: "value", "child_added", "child_changed",
"child_removed", or "child_moved."
"58.967" isn't one of them, and I don't know what you were intending to do there. But you probably want to use "value" to get the value of a child in the database.

getting api keys from module.exports

I have two files a config.js and main.js I am storing api keys in my config.js
like this
function getGoogleApiKey(){
return 'KeyGoogle';
}
function getApiKey(){
return 'keyApi'
}
function getApiKey2(){
return 'keyApi2'
}
module.exports = {
getGoogleApiKey,
getApiKey,
getApiKey2,
}
I would like to get specific keys from the config.js file when I need it. I want to use some keys on my main.js
Here is my main.js.
const {config} = require('./config.js');
const googlePlaces = new GooglePlaces(config.getGoogleApiKey, 'json');
const awesome = new awesome(config.getApiKey);
I am note sure how to get the keys, I also tried it in this way but I get errors.
const {getGoogleApiKey, getApiKey, getApiKey2} = require('./config.js');
const googlePlaces = new GooglePlaces(getGoogleApiKey, 'json');
This line:
const {config} = require('./config.js');
is pulling out a config property from the value returned by require('./config.js'), which is non-existent in config.js.
Instead, just use this:
const config = require('./config.js');
which will assign the exported value (the module.exports object), and will work as expected.
Secondly, functions are being exported and not primitive (string) properties, so one or the other will need to be changed: export string properties directly or convert main.js to use the appropriate function call notation.
For example:
const googlePlaces = new GooglePlaces(config.getGoogleApiKey(), 'json');
const awesome = new awesome(config.getApiKey());

NodeJS (Express) - project structure and mongo connection

I started a new project from scratch with ExpressJS.
Everything works fine but now I begin to have a dozen of 'app.get(....)' function and I need to give the project a structure.
What I have in mind is quite simple, it should have a folder named 'routes' containing a file such as 'module1.js', with all of the app.get related to that module. (like I've seen in many examples)
The issue is how to tell Express to route 'http://url/module1/' to that route file and how to pass it a param variable, containing for instance the mongodb connection.
what I tried is :
var params = {
db: myMongoConnection
};
var mod1 = require('routes/module1');
app.use('/module1', mod1);
but now I still miss the 'params'.
If I try to pass it as an argument to the require method i get an error saying it needs middleware.
Another issue is related to the fact that the myMongoConnection is valid in the connection callback, so I think i need to require and use the route.js inside the MongoClient connect callback.
Any idea?
thanks a lot
For custom modules, create a folder, call it modules
In its index.js, expose the modules that you need.
Something like,
var mods = [
'mod1',
'mod2',
];
function init() {
var expose = {};
var params = {
db: myMongoConnection
};
mods.forEach(mods, function (mod) {
expose[mod] = require('./' + mod)(params);
});
return expose;
}
// export init
module.exports = init;
In mod1.js, wrap the params
module.exports = function(params) {
// all your functions here will have access to params.
}
Then in, server/app.js, require this and set it in the app.
app.set('mods', require('path-to/modules'));
Now, you can access all your modules, using app.get('mods').moduleName.methodname

Resources