Cant get Firebase in Node JS app to run - javascript

I am trying to setup firebase JS client with NodeJS. So far here is my code
var firebase = require('firebase/app');
require('firebase/database');
var config = {
apiKey: "MY_SECRET_KEY_fhcWICPI",
authDomain: "my_fir_app.firebaseapp.com",
databaseURL: "https://my_fir_app.firebaseio.com",
};
var firApp = firebase.initializeApp(config);
firebase.database.enableLogging(true)
// Get a reference to the database service
var database = firebase.database();
Then here is one of my Firebase functions to save data to the real time database.
/**
* This will save the authors of stories in Firebase
* #param {String} id The ID of the author
* #param {String} firstName The authors first name
* #param {String} lastName The authors last name
* #param {Timestamp} dateCreated The unix time stamp when the author was created
*/
function saveStoryAuthor(id, firstName, lastName, dateCreated) {
database.ref('mystoriesdb/authors/' + id).set({
first_name: firstName,
last_name: lastName,
date_created : dateCreated
});
}
Finally, somewhere in the middle of my code am calling this function as
...
saveStoryAuthor('MZ8XWXNrkG', 'Dennis', 'Richie')
...
However, this is what I get in the logs (since I have enabled logging)
$ node index.js
p:0: Browser went online.
p:0: Making a connection attempt
getToken() completed. Creating connection.
c:0:0: Connection created
p:0: Failed to get token: Error: No transports available
p:0: data client disconnected
p:0: Trying to reconnect in 326.9669258513522ms
0: onDisconnectEvents
p:0: Making a connection attempt
getToken() completed. Creating connection.
c:0:1: Connection created
p:0: Failed to get token: Error: No transports available
I am probably doing something wrong. Could someone help.

It seems you havent created a service account in order to add firebase to your project with node js
Check out the documentation here.
var admin = require("firebase-admin");
var serviceAccount = require("path/to/serviceAccountKey.json");
admin.initializeApp({
credential: admin.credential.cert(serviceAccount),
databaseURL: "https://<DATABASE_NAME>.firebaseio.com"
});

You need to change
var firebase = require('firebase/app');
to
var firebase = require('firebase');

Solution: Add the following to the root of your Webpack configuration:
resolve: {
mainFields: ['main']
}
Explanation:
The firebase/database package defines different entry points: main, browser, and module. NodeJS uses main. Webpack is a hipster and uses module.
The Firebase guys made some NodeJS-specific configuration (such as setting up WebSocket as a transport layer) in the entry point defined by main. So, after bundling, that special NodeJS-specific code isn't set up, so it errors out. Telling Webpack to just use main like Node resolves this issue.

Related

How do I use Firebase Admin SDK to change data in Firebase Realtime Database while using Cloud Functions?

I want to reset a specific value in my Firebase Realtime Database every day at 12:00 AM. To do this, I'm using the Firebase Admin SDK to change the data in Firebase Realtime Database and Cloud Functions to trigger the change at 12:00 AM every day.
This is an example structure of my Firebase Realtime Database:
{
"users": {
"fa54487d9cbb4214b00db80e2118e4e6": {
"daily": 10
}
}
}
This is the code in my index.js:
// The Cloud Functions for Firebase SDK to create Cloud Functions and setup triggers.
var functions = require('firebase-functions');
// The Firebase Admin SDK to access Cloud Firestore.
var admin = require('firebase-admin');
// Fetch the service account key JSON file contents
var serviceAccount = require("serviceAccountKey.json");
// Initialize the app with a service account, granting admin privileges
admin.initializeApp({
credential: admin.credential.cert(serviceAccount),
databaseURL: "https://databaseName.firebaseio.com"
});
// As an admin, the app has access to read and write all data, regardless of Security Rules
var db = admin.database();
var ref = db.ref("users");
// Reset today GHG emissions at 12:00 AM everyday
exports.dailyReset = functions.pubsub.schedule('0 0 * * *').onRun((context) => {
usersRef.child("{userId}").set({
daily: 0
});
});
Deploy Error:
! functions[dailyReset(us-central1)]: Deployment error.
Function failed on loading user code. This is likely due to a bug in the user code. Error message: Error: please examine your function logs to see the error cause: https://cloud.google.com/functions/docs/monitoring/logging#viewing_logs. Additional troubleshooting documentation can be found at https://cloud.google.com/functions/docs/troubleshooting#logging. Please visit https://cloud.google.com/functions/docs/troubleshooting for in-depth troubleshooting documentation.
Firebase Console Functions Logs:
Error: function terminated. Recommended action: inspect logs for termination reason.
Additional troubleshooting documentation can be found at https://cloud.google.com/functions/docs/troubleshooting#logging Function cannot be initialized.
{"#type":"type.googleapis.com/google.cloud.audit.AuditLog","status":{"code":3,"message":"Function failed on loading user code. This is likely due to a bug in the user code.
Error message: Error: please examine your function logs to see the error cause: https://cloud.google.com/functions/docs/monitoring/logging#viewing_logs.
Additional troubleshooting documentation can be found at https://cloud.google.com/functions/docs/troubleshooting#logging.
The script won't deploy when I use firebase deploy as my function is giving me an error. Can someone tell me how to fix my code?
This won't work:
exports.dailyReset = functions.pubsub.schedule('0 0 * * *').onRun((context) => {
usersRef.child("{userId}").set({
daily: 0
});
});
There is nothing here that interprets the {userId} in that path, so the database updates the literal path "/users/{userId}", which is not what you want.
If you know what user ID you want to update, you should use that value in the path:
exports.dailyReset = functions.pubsub.schedule('0 0 * * *').onRun((context) => {
let usersRef = admin.database().ref("users");
usersRef.child("theActualUserIdYouWantToUpdate").set({
daily: 0
});
});
If you don't know what user ID to update, you'll need to query the database to determine that.
If you want to loop over all users, you can do:
exports.dailyReset = functions.pubsub.schedule('0 0 * * *').onRun((context) => {
return usersRef.once("value").then((snapshot) => {
let updates = {};
snapshot.forEach((userSnapshot) => {
updates[userSnapshot.key+"/daily"] = 0
});
return usersRef.update(updates);
});
});
If you are new to JavaScript or interacting with the Realtime Database in it, Cloud Functions for Firebase is not the best way to learn it. I recommend first reading the Firebase documentation for Web developers and/or taking the Firebase codelab for Web developer. They cover many basic JavaScript, Web and Firebase interactions. You could also use the Admin SDK in a local Node.js process, which can be debugged with a local debugger. After those you'll be much better equipped to write code for Cloud Functions too.

How to put a trigger to another Firebase Database from Firebase Functions?

I'm trying to trigger my database function when something is wrote to a database from an associate.
I know that I need a Service Account created in the other database and the .json file that firebase gives to do the connection properly. Right now I'm giving all the permissions for getting sure than my errors don't come from this.
With what I found on the documentation and with other information on internet, this is how I login to the other database:
var adminAbi = require("firebase-admin");
var functionsAbi = require('firebase-functions');
const serviceAccount = require(`./serviceacountfile.json`);
adminAbi.initializeApp({
credential: adminAbi.credential.cert(serviceAccount),
databaseURL: 'https://DATABASEURL.firebaseio.com/',
},'test' );
And this is my trigger:
exports.copyDatabasess = functionsAbi.database.instance('test').ref('/messages/{user_id}/{now}').onWrite(event =>{
if (!event.data.exists()) {
return;
}
console.log('copydatabase', event.params.body);
// Grab the current value of what was written to the Realtime Database.
const original = event.data.val();
});
With this code I'm getting right now this error when I deploy:
! functions[copyDatabasess]: Deployment error. Failed to configure
Firebase Realtime Database trigger: unknown error, HTTP code 401
I really can't find useful informations about this error and how to solve it. If someone knows something about this would be much appreciated.
Thanks in advice.
You can't put a trigger on a database that's not in the same project as your functions. instance() only works with database shards in the same project.

Having trouble connecting to my Firebase database - Node & Express

I am still learning programming in general so sorry if I don't make sense.
I am trying to connect to my firebase database but I get a PERMISSION_DENIED error. The database in my firebase is set to Test mode so anyone should be able to access it.
I have added all the npm packages needed based on the firebase docs as well.
Let me know if I need to provide more information.
I am not sure what I am doing wrong here. Would anyone know? Any help is appreciated.
Here is my module file
var express = require('express');
var firebase = require('firebase');
// Initialize Firebase
var config = {
apiKey: "apikey",
authDomain: "authdomain",
databaseURL: "databaseurl",
storageBucket: "storagebucket"
};
firebase.initializeApp(config);
var db = firebase.database();
var ref = db.ref("/users");
ref.on("value", function(snapshot) {
console.log(snapshot.val());
}, function (errorObject) {
console.log("The read failed: " + errorObject.code);
});
var index = require('./routes/index');
app.use('/', index);
module.exports = app;
Here is my routes/index.js
var express = require('express');
var router = express.Router();
/* GET home page. */
router.get('/', function(req, res, next) {
res.render('index', { title: 'The Title' });
});
module.exports = router;
Asynchronous listeners: Data stored in a Firebase Realtime Database is retrieved by attaching an asynchronous listener to a
database reference. The listener is triggered once for the initial
state of the data and again anytime the data changes. An event
listener may receive several different types of events.
Helpful link https://firebase.google.com/docs/database/admin/retrieve-data
You need to create a reference variable that corresponds to your database path
var ref = db.ref("server/saving-data/fireblog/posts");
and then you'll attach an asynchronous callback to read the data at the reference
ref.on("value", function(snapshot) {
console.log(snapshot.val());
}, function (errorObject) {
console.log("The read failed: " + errorObject.code);
});
I believe your issue with PERMISSION_DENIED is that you're using
var db = firebase.database();
instead of
var db = admin.database();
So I figured out a way to properly connect to my firebase database. I am not sure if this is the best way but the first thing I did was delete the current database and recreated it (not sure if this helped or was needed but I just wanted a fresh install just in-case something was wrong before.)
Then inside my database dashboard in firebase I went to the "Gear Icon => Project Settings" next to "Project Overview" header on the upper left of the dashboard screen. From here under Firebase Admin SDK I clicked "Generate New Private Key" button on the bottom.
This gave me a .json file which was downloaded onto my computer. I changed the downloaded files name to something more simple like myfirstapp-firebase-db.json. I added this file into the folder where my node js is being stored.
Before I started writing the code to connect to my firebase database, I had to make sure that my "Database => Rules" were set to true for read & write privileges in my firebase project.
Database Rules setup:
{
"rules": {
".read": true,
".write": true
}
}
After everything needed to configure firebase was taken care of, I simply configured my module.js file to properly connect. Below is the code necessary to connect to firebase. The two important things were "serviceAccount" and "databaseURL".....
var firebase = require('firebase');
// Initialize firebase
firebase.initializeApp({
serviceAccount: "./<your-service-account-url>-firebase-db.json",
databaseURL: "https://<your-database-url>.firebaseio.com/"
});
var db = firebase.database();
var ref = db.ref("/users");
..... The "serviceAccount" is a route to the .json file downloaded from the step "Generate New Private Key" above. You can find the databaseURL inside the "Database" dashboard on the top of the white block inside firebase. I simply copied and pasted that url into the databaseURL.
Then I put firebase.database() into a var = db and then specified the ref.
At this point my connection was successful and when I did a node modules.js it showed me in the console everything that is in the ref database. Make sure to have some pre populated fields in the database for the console to show you all the items inside. I hope this may be helpful to someone and if anyone knows of a better way of doing this I would love to know your suggestions!

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

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?
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
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)
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)
})
})
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.
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!

Update data in firebase using nodejs

I have the following problem, I am sending data every minute to a firebase
database in a cron tab that is running in nodejs, I only send the information when there are changes, but when there are no changes the database continues to receive information, This is my code
let admin = require('firebase-admin');
let prev_res = {};
admin.initializeApp({
credential: admin.credential.cert(serviceAccount),
databaseURL: 'mydburl'
});
cron.schedule('* * * * *', function(){
let connection = mysql.createConnection({
host : 'myhost',
user : 'myuser',
password : 'mypass',
database : 'mydb'
});
connection.query("MY QUERY", function(err, rows, fields){
if (!err){
if(JSON.stringify(rows) != JSON.stringify(prev_res)){
let db = admin.database();
let ref = db.ref('path');
ref.set(rows);
console.log("Updated data");
} else {
console.log("without changes");
}
prev_res = rows;
}
});
});
Does the firebase admin have some kind of cache or something like that?
This is actually a strange problem with Firebase Authentication.
The issue resides in the permissions for the user you are including in your credentials.
Besides creating a Service Account with the correct permissions, you need to add this user to your IAM Accounts with the editor role.
Restart your App after doing this and you should stop seeing:
"FIREBASE WARNING: Provided authentication credentials are invalid.
This usually indicates your FirebaseApp instance was not initialized
correctly."

Categories

Resources