Update realtime database in Firebase - javascript

I am creating a API which can update value from realtime database (Firebase). Using ClaudiaJS to create API. Basically, API will update the number of student of a class by year.
What I have done:
Realtime Database (Firebase)
class
|__2015
| |__numberOfStudent: 50
|__2016
|__numberOfStudent: 60
Export to JSON like this:
{
"class": {
"2015": {
"numberOfStudent": 50
},
"2016": {
"numberOfStudent": 60
}
}
}
Javascript file (ClaudiaJS):
var ApiBuilder = require('claudia-api-builder');
api = new ApiBuilder();
module.exports = api;
var admin = require("firebase-admin");
var serviceAccount = require("./xxxxxxx-firebase-adminsdk-nxxxxx.json");
admin.initializeApp({
credential: admin.credential.cert(serviceAccount),
databaseURL: "https://xxxxxx.firebaseio.com"
});
api.post('/addmore/{year}/{number}', function (request) {
var database = admin.database().ref('class');
//Params
var year = request.pathParams.year;
var number = request.pathParams.number;
var ref = year + '/numberOfStudent'; // '2015/numberOfStudent'
database.update({
ref : parseInt(number) // "2015/numberOfStudent" : 60
});
return 'Update successfully';
});
When I run the api in Postman:
http://xxxxapi.com/addmore/2015/55
What happened: API replied 'Update successfully', but the database didn't get any update. It seems that the code database.update() doesn't work at all.
Any suggestion is very appreciated

The update call happens asynchronously, so currently the return happens before update completes.
update returns a Promise, so try the following:
return database.update({ref : parseInt(number)})
.then(() => {
// Each then() should return a value or throw
return 'Update successful';
})
.catch(error => {
// handle the error however you like
console.error(error);
});

Related

Writing data to Firestore on create user not working

I am using the VueCLI & Firebase (Auth & Firestore).
Essentially the function is being called properly and registers the user in the database correctly, however, the promises aren't being called (e.g. .then & .catch) on the .createUserWithEmailAndPassword function.
No error is returned and nothing is registered in the firestore, has really stumped me.
However, sometimes it works exactly as expected?? But often irregularly.
submit () {
var v = this
firebase.auth().createUserWithEmailAndPassword(v.form.email, v.form.password)
.then(function () {
// set account doc
var account = {
email: v.form.email,
name: v.form.name,
picture: v.form.picture
}
db.collection('Users').doc(v.form.email).set(account).then(() => {
console.log('Database Details Defined')
// Set user display name
v.$store.commit('setUserDisplayName', v.form.name)
// Set user display name
v.$store.commit('setUserImage', v.form.picture)
// Set user as signed in
v.$store.commit('changeLoginState', true)
// Set user email
v.$store.commit('setUserEmail', v.form.email)
// console.log('Vue Store Details Defined')
localStorage.setItem('name', v.$store.state.displayName)
localStorage.setItem('email', v.$store.state.email)
localStorage.setItem('userImage', v.$store.state.userImage)
localStorage.setItem('loggedin', v.$store.state.isLoggedin)
// console.log('Local Storage Details Defined')
// Redirect user to dashboard
v.$router.replace('/')
v.registering = false
}).catch((error) => {
v.databaseError = 'Database Error: ' + error.code + ' - ' + error.message
v.registering = false
})
})
.catch(function (error) {
v.registrationError = 'Registration Error: ' + error.code + ' - ' + error.message
v.registering = false
})
}
You have a function getDB() that returns the value the Firestore instance so getDB().collection("users")... may have worked as intended. I don't see where db is initialized in this specific file so console.log(db) might help to check what is it's value.
firebase.firestore().collection('Users').doc(v.form.email).set(account)
This worked as intended because firebase.firestore() will return the Firestore instance for sure. I'd recommend exporting Firebase services directly and not in functions. For example you could create a file firebase.js and initialize Firebase there:
var firebase = require('firebase/app')
require('firebase/firestore')
const firebaseConfig = { }
if (!firebase.apps.length) {
firebase.initializeApp(config);
}
const auth = firebase.auth()
const db = firebase.firestore()
export {db, auth}
You need to initialize Firebase only once as shown. It'll be much more clear rather than initializing in the getDB function.

I cannot get data from firebase database

I am new to firebase. I'm trying to retreive data from a real time database using a node.js server sending associated credentials to firebase, but something gets broken after once('value') is called: its returned promise never gets resolved and server stops itself logging this message: "Process exited with code 3221226505".
I wrote the following code:
async function testFirebase1(firebaseCredentialsObj, path) {
let firebase = require('firebase')
firebase.initializeApp(firebaseCredentialsObj);
var database = firebase.database();
var ref = database.ref(path);
console.log(ref.toString());
try {
// Attempt 1
var prom = await ref.once('value');
const data = prom.;
console.log('data ' + data)
// Attempt 2
prom.then((snapshot) => {
console.log('snapshot ' + snapshot)
}).catch((error) => { console.log(error)} )
} catch (error) {
console.log(error)
}
}
No error ever gets catched.
I also tried to get data as an admin, but i got the same failing result
async function testFirebase3(firebaseCredentials, serviceAccountKey, databaseURL, path) {
const admin=require('firebase-admin');
const serviceAccount = serviceAccountKey;
admin.initializeApp({
credential: admin.credential.cert(serviceAccount),
databaseURL: databaseURL
});
var db=admin.database();
var userRef=db.ref(path);
const prom = await userRef.once('value');
console.log(prom)
}
Promise returned from once() method keep beeing pendent. This is its log:
[[PromiseStatus]]:'pending'
[[PromiseValue]]:undefined
Server is supposed to get databases'data in json format and send it to the client.
Why is this happening?
Based on your code, you are mixing traditional Promise chaining and async/await syntax together which is leading to your confusion.
Note: In the below snippets, I use the database query coding style I describe at the end of this answer.
SDK Initialization
To start with, in both testFirebase1 and testFirebase3, you initialize the default Firebase app instance in the function. If you call either function only once, you won't experience any problems, but any time you call them another time, they will always throw an error stating that the app has already been initialized. To solve this, you can lazily load these libraries using the following functions:
function lazyFirebase(options, name = undefined) {
const firebase = require('firebase');
// alternatively use the Promise-based version in an async function:
// const firebase = await import('firebase');
try {
firebase.app(name);
} catch (err) {
firebase.initializeApp(options, name);
}
return firebase;
}
function lazyFirebaseAdmin(options, name = undefined) {
const admin = require('firebase-admin');
// alternatively use the Promise-based version in an async function:
// const admin = await import('firebase-admin');
try {
admin.app(name);
} catch (err) {
const cred = options.credential;
if (typeof cred === "string") {
options.credential = admin.credential.cert(cred)
}
admin.initializeApp(options, name);
}
return admin;
}
Important Note: Neither of the above functions checks whether they use the same options object to initialize them. It just assumes they are the same configuration object.
Correcting testFirebase1
In testFirebase1, you are initializing the default Firebase app instance and then starting the process of the getting the data from the database. Because you haven't returned the promise from the ref.once('value') in the function, the caller will get a Promise<undefined> that resolves before the database call completes.
async function testFirebase1(firebaseCredentialsObj, path) {
let firebase = require('firebase')
// bug: throws error if initializeApp called more than once
firebase.initializeApp(firebaseCredentialsObj);
// bug: using `var` - use `const` or `let`
var database = firebase.database();
var ref = database.ref(path);
console.log(ref.toString());
try {
// Attempt 1
// bug: using `await` here, makes this a DataSnapshot not a Promise<DataSnapshot>
// hence `prom` should be `snapshot`
// bug: using `var` - use `const` or `let`
var prom = await ref.once('value');
// bug: syntax error, assuming this was meant to be `prom.val()`
const data = prom.;
console.log('data ' + data)
// Attempt 2
// bug: a `DataSnapshot` doesn't have a `then` or `catch` method
// bug: if `prom` was a `Promise`, you should return it here
prom
.then((snapshot) => {
console.log('snapshot ' + snapshot)
})
.catch((error) => {
console.log(error)
})
} catch (error) {
console.log(error)
}
}
Correcting these problems (and making use of my coding style when dealing with RTDB queries) gives:
async function testFirebase1(firebaseCredentialsObj, path) {
const firebase = lazyFirebase(firebaseCredentialsObj);
const snapshot = await firebase.database()
.ref(path)
.once('value');
// returns data at this location
return snapshot.val();
}
Correcting testFirebase3
In testFirebase3, you are initializing the default Firebase Admin app instance and correctly waiting for the data from the database. Because you haven't returned the data from the database, the caller will get a Promise<undefined> that resolves when the database call completes but without the containing data.
async function testFirebase3(firebaseCredentials, serviceAccountKey, databaseURL, path) {
const admin = require('firebase-admin');
// note: unnecessary line, either call `serviceAccountKey` `serviceAccount` or use `serviceAccountKey` as-is
const serviceAccount = serviceAccountKey;
// bug: throws error if initializeApp called more than once
// bug: `firebaseCredentials` is unused
// note: when initializing the *default* app's configuration, you
// should specify all options to prevent bugs when using
// `admin.messaging()`, `admin.auth()`, `admin.storage()`, etc
// as they all share the default app instance
admin.initializeApp({
credential: admin.credential.cert(serviceAccount),
databaseURL: databaseURL
});
// bug: using `var` - use `const` or `let`
var db=admin.database();
var userRef=db.ref(path);
// bug: using `await` here, makes this a DataSnapshot not a Promise<DataSnapshot>
// hence `prom` should be `snapshot`
const prom = await userRef.once('value');
// bug: logging a `DataSnapshot` object isn't useful because it
// doesn't serialize properly (it doesn't define `toString()`,
// so it will be logged as "[object Object]")
console.log(prom)
}
Correcting these problems (and making use of my coding style when dealing with RTDB queries) gives:
async function testFirebase3(firebaseCredentials, serviceAccountKey, databaseURL, path) {
const admin = lazyFirebaseAdmin({
...firebaseCredentials, // note: assuming `firebaseCredentials` is the complete app configuration,
credential: serviceAccountKey,
databaseURL: databaseURL
});
const snapshot = await admin.database()
.ref(path)
.once('value');
return snapshot.val();
}

TypeError: this.path.replace is not a function at new FirestoreDelete

Trying to remove the users`s data calling a function from app.
'use strict';
const functions = require('firebase-functions');
const firebase_tools = require('firebase-tools');
const admin = require('firebase-admin');
const serviceAccount = require('./myapp.json');
admin.initializeApp({
credential: admin.credential.cert(serviceAccount),
databaseURL: "https://myapp.firebaseio.com"
});
let db = admin.firestore();
exports.mintAdminToken = functions.https.onCall((data, context) => {
const uid = data.uid;
return admin
.auth()
.createCustomToken(uid, { admin: true })
.then(function(token) {
return { token: token };
});
});
exports.recursiveDelete = functions
.runWith({
timeoutSeconds: 540,
memory: '1GB'
})
.https.onCall((data, context) => {
if (!(context.auth && context.auth.token )) {
throw new functions.https.HttpsError(
'permission-denied',
'Must be an administrative user to initiate delete.'
);
}
let path = data.path;
console.log(
`User ${context.auth.uid} has requested to delete path ${path}`
);
return firebase_tools.firestore
.delete(path, {
project: process.env.GCLOUD_PROJECT,
recursive: true,
yes: true,
token: functions.config().fb.token
})
.then(() => {
return {
path: path
};
});
});
and I pass the path like:
Map<String, Object> data = new HashMap<>();
data.put("path", "./users/rnAjpK4LLSMMlENZqe4l3F2");
result:
Function execution took 540003 ms, finished with status: 'timeout'
probably the problem is in path. if I change for this line:
let path = admin.firestore().doc('users/' + context.auth.uid);
Im getting an error
Unhandled error TypeError: this.path.replace is not a function at new FirestoreDelete
is the problem with "path"?
what will be the right path to delete then?
I use this example https://github.com/firebase/snippets-node/tree/master/firestore/solution-deletes but here is nothing about it
UPDATED:
with
String path = "./users/rnAjpK4LLSMMlENAgrZqe4l3F2";
or
String data = "./users/rnAjpK4LLSMMlENAgrZqe4l3F2";
an error
Unhandled error { FirebaseError: Must specify a path. at Object.reject (/srv/node_modules/firebase-tools/lib/utils.js:82:27)
solution
const id = context.auth.uid;
const path = `users/${id}`;
As far as I can see from reading the code of the delete function, the path you pass in has to be a single string value of the collection or document to delete. The function will then delete all data under that collection or document.
I was accidentally sending an entire object. If you come across this question, double check you're not making the same mistake.

how to send data to firebase database in function

I'm trying to send data to firebase. The data is saved to the database after the function is performed, how can I overwrite it during the function execution?
import firebase from 'firebase';
var config = {
apiKey: "xxx",
authDomain: "xxx.firebaseapp.com",
databaseURL: "https://xxx.firebaseio.com",
projectId: "xxx",
storageBucket: "xxx.appspot.com",
messagingSenderId: "xxx"
};
firebase.initializeApp(config);
var db = firebase.database();
var sleep = require('sleep');
function run(TIME) {
db.ref('/test/').child('status').set('1');
sleep.sleep(TIME);
db.ref('/test/').child('status').set('2');
sleep.sleep(TIME);
db.ref('/test/').child('status').set('3');
sleep.sleep(TIME);
db.ref('/test/').child('status').set('4');
};
//========================================<<<< Now I see status in Firebase
run(5);
The set() method is asynchronous and returns a promise that resolves when write to server is complete, as explained in the doc here.
From you comment above I understand you want to have a status "WORKING - before starting function and DONE after".
So you should do something along the following lines:
var status = '';
var adaNameRef = firebase.database().ref('users/ada/name');
status = 'WORKING';
adaNameRef.set({ first: 'Ada', last: 'Lovelace' })
.then(function() {
status = 'DONE';
})
.catch(function(error) {
console.log('Synchronization failed');
});
If you want to "write multiple values to the Database at once", you should use the update() method. See here and here.
Similarly to the set() method, the update() method is asynchronous and returns a promise that resolves when write to server is complete, so you would use the same logic to update the value of status
UPDATE following your comment
1. Send status 'WORKING' to FB 2. Set Relay to ON 3. Wait x seconds 4. Send status 'DONE' to FB 5. Set Relay to OFF
If I understood correctly, this should work (not tested however):
var adaNameRef = firebase.database().ref('users/ada/name');
adaNameRef.set({ status: 'WORKING'})
.then(function() {
// Set Relay to ON ... don't know exactly how you "set the relay"
sleep.sleep(x);
return adaNameRef.set({ status: 'DONE'})
})
.then(function() {
// Set Relay to OFF
})
.catch(function(error) {
console.log(error);
});

FCM onWrite() function firing twice and sending two notifications?

I am having issues sending multiple FCM notifications in my iOS app.
I am using Firebase and it's Realtime Database Trigger function, and am able to trigger the Firebase code when a new node is added to the database and when the node is updated.
The problem is that the first time the function runs on write, it runs twice and sends two notifications.
When a node is updated it only runs once and sends only 1 notification, which is the expected behavior.
Below is my javascript code and JSON structure of the database write.
Can anyone please shed any light onto why my function might be fired twice?
var functions = require('firebase-functions');
var admin = require('firebase-admin')
var userDeviceToken = ""
var sharedUserID = ""
admin.initializeApp({
credential: admin.credential.applicationDefault(),
databaseURL: "https://userlocation-aba20.firebaseio.com/"
});
var payloadStart = {
notification: {
title: "Name of App",
body: "Someone has shared a journey with you."
},
};
var options = {
priority: "high"
}
var payloadEnd = {
notification: {
title: "Name of App",
body: "A shared journey has ended."
},
};
exports.newEntry = functions.database.ref('/StartedJourneys/{fireUserID}')
.onWrite(event => {
const original = event.data.val()
console.log(original.SharedWithUserID)
console.log(original.JourneyEnded)
console.log(event.data.changed())
console.log(event.data.exists())
console.log(event.data.previous)
console.log(event.params)
var payload = payloadStart
if (original.JourneyEnded) {
payload = payloadEnd
}
sharedUserID = original.SharedWithUserID
console.log(sharedUserID)
var db = admin.database()
var ref = db.ref('/UserTokens')
return ref.orderByKey().equalTo(sharedUserID).on("child_added", function(snapshot) {
const deviceToken = snapshot.val()
admin.messaging().sendToDevice(deviceToken, payload, options)
.then(function(response) {
console.log("Successfully sent message:", response);
})
.catch(function(error) {
console.log("Error sending message:", error);
});
})
})
"StartedJourneys" : {
"nFQhaMkjDeSHDAZCklzN7LoGGc22" : {
"CurrentLat" : 37.543821,
"CurrentLong" : -122.239187,
"DestinationLat" : 37.5232217,
"DestinationLong" : -122.2520166,
"DestinationName" : "Nob Hill",
"JourneyEnded" : false,
"SharedWithUser" : "Lisa",
"SharedWithUserID" : "mSJoMJPWWBZEnbq8X05BHwrSd2M2"
}
},
EDIT: I have reduced the scope of the question in hope of getting a response. Thanks in advance!
**EDIT: Added screenshot of the 2 console logs triggered by the function. I should only be seeing one of these.
Firebase logs

Categories