Creating a document in firestore using cloud functions - firebase

After authenticating a user in my app i want to create a cloud functions that creates a user profile document for them in my firestore userProfile collection.
This is my entire index.js file for the cloud function
// The Cloud Functions for Firebase SDK to create Cloud Functions and setup triggers.
const functions = require('firebase-functions');
// The Firebase Admin SDK to access the Firebase Realtime Database.
const admin = require('firebase-admin');
admin.initializeApp(functions.config().firebase);
//function that triggers on user creation
//this function will create a user profile in firestore database
exports.createProfile = functions.auth.user().onCreate(event => {
// Do something after a new user account is created
return admin.firestore().ref(`/userProfile/${event.data.uid}`).set({
email: event.data.email
});
});
Here is the error i am receiving
TypeError: admin.firestore(...).ref is not a function
at exports.createProfile.functions.auth.user.onCreate.event (/user_code/index.js:13:30)
at Object.<anonymous> (/user_code/node_modules/firebase-functions/lib/cloud-functions.js:59:27)
at next (native)
at /user_code/node_modules/firebase-functions/lib/cloud-functions.js:28:71
at __awaiter (/user_code/node_modules/firebase-functions/lib/cloud-functions.js:24:12)
at cloudFunction (/user_code/node_modules/firebase-functions/lib/cloud-functions.js:53:36)
at /var/tmp/worker/worker.js:695:26
at process._tickDomainCallback (internal/process/next_tick.js:135:7)
In the firestore cloud database I have a collection called userProfile where a document should be created with the unique id given to a user after authentication

admin.firestore() returns an instance of a Firestore object. As you can see from the API docs, the Firestore class doesn't have a ref() method. You're probably confusing it with the Realtime Database API.
Firestore requires you to organize documents within collections. To reach into a document, you could do this:
const doc = admin.firestore().doc(`/userProfile/${event.data.uid}`)
Here, doc is a DocumentReference. You can then set the contents of that document like this:
doc.set({ email: event.data.email })
Be sure to read the Firestore documentation to understand how to set up Firestore - there are many places where it's different than Realtime Database.

Here is my code. When I will create the new user below function will run.
const functions = require('firebase-functions');
const admin = require('firebase-admin');
admin.initializeApp();
exports.createProfile = functions.auth.user().onCreate((user) => {
var userObject = {
displayName : user.displayName,
email : user.email,
};
return admin.firestore().doc('users/'+user.uid).set(userObject);
// or admin.firestore().doc('users').add(userObject); for auto generated ID
});

Related

Firebase Admin SDK within Firebase Functions CLI - Error fetching access token

I'm trying to use firebase-admin sdk to update my users passwords manually, the idea is to use a onCreate trigger to achieve this by creating a new document in firestore (with the right rules obviously).
According to firebase documentation i don't need to use anything else than this to autenticate from my firebase functions environment:
const functions = require('firebase-functions');
const admin = require('firebase-admin');
admin.initializeApp();
In order to test the function i just manually added the document right from the firebase console ui for firestore, and as i can see the trigger is just doing it's work, the problem is when updatin the user password using the firebase-admin sdk.
I'm getting the next error message from the logs:
Error updating user: { Error: Credential implementation provided to
initializeApp() via the "credential" property failed to fetch a valid
Google OAuth2 access token with the following error: "Error fetching
access token
this is the whole firebase cloud function if you want to see how it's implemented:
'use strict';
const functions = require('firebase-functions');
const admin = require('firebase-admin');
admin.initializeApp();
triggerNewDocument();
function triggerNewDocument() {
exports.updateUserData = functions.firestore
.document('updateUserPasswords/{userId}')
.onCreate((snap, context) => {
// Get an object representing the document
// e.g. {'name': 'Marie', 'age': 66}
const newValue = snap.data();
console.log(newValue);
// access a particular field as you would any JS property
const uid = newValue.uid;
const newPassword = newValue.password;
return updateUserPassword(uid, newPassword);
// perform desired operations ...
});
}
function updateUserPassword(uid, newPassword) {
admin.auth().updateUser(uid, {
password: newPassword,
})
.then(function(userRecord) {
// See the UserRecord reference doc for the contents of userRecord.
return userRecord.toJSON();
})
.catch(function(error) {
return error;
});
}
Is there anything may i be missing here?, thanks in advance for any hint or help you could provide.
The whole issue was somehow the service account autogenerated with the admin-sdk was inactive.
Anyway i had to disable and enable the service account at least 3 times to make it work, hopefully this can be helpful for anyone having the same issue.
A simple mistake

accessing realtime DB from firebase functions

We are using firebase functions and firebase real time DB for our mobile app. We do send emails when someone place an order which is implemented using firebase DB triggers like below:
exports.on_order_received = functions.database.ref("/orders/{id}")
.onCreate((change, context) => {
console.log("start of on_order_received")
...
Above trigger works just fine for us. Now, we have some requirement where we do not have a DB trigger in the picture. It's a http request like below
exports.daily_sales_report = functions.https.onRequest((req, res) => {
//query data from firebase
The question is how do we access the real time db objects here? or in other words how do i get the access to the /orders node ? I tried like below
exports.daily_sales_report = functions.https.onRequest((req, res) => {
//query data from firebase
var ref = functions.database.ref('orders')
ref.orderByValue().limitToLast(3).on("value", function(snapshot) {
snapshot.forEach(function(data) {
console.log("The " + data.key + " dinosaur's score is " + data.val());
});
})
but this does not work. I get error "orderByValue() is not a function"
You should use the Firebase Admin SDK. It has the ability to read and write your database. In fact, when you write a database trigger, the refs it gives you to work with actually come from the Admin SDK, so it's the same API. You just need to initialize it yourself when using HTTP type functions:
// at the top of the file:
const admin = require('firebase-admin');
admin.initializeApp();
// in your function:
const root = admin.database().ref();
// root is now a Reference to the root of your database.
You'll have to use the admin and not the functions to access the database() to read the data.
(Please ensure you've access to firebase-admin sdk, use import or require as appropriate depending on whether you are using TypeScript or JavaScript)
// The Firebase Admin SDK to access the Firebase Realtime Database.
import * as admin from 'firebase-admin';
or
// The Firebase Admin SDK to access the Firebase Realtime Database.
const admin = require('firebase-admin');
Try this:
exports.daily_sales_report = functions.https.onRequest((req, res) => {
//query data from firebase
/* Do not use functions */
// var ref = functions.database.ref('orders')
/* Instead use the admin */
var ref = admin.database().ref('orders')
ref.orderByValue().limitToLast(3).on("value", function(snapshot) {
snapshot.forEach(function(data) {
console.log("The " + data.key + " dinosaur's score is " + data.val());
});
})
orderByValue() is not defined in functions.database - but is actually available in admin.database().ref()

Firebase Functions - push notification when new document is created

I have the following Firestore DB structure:
users
$USER_ID
notifications
$DOC1
$DOC2
$DOC3
I want to push a new notification when a document is created at the user notification collection.
It should be something like this, but I don't know of any way to this for each $UID:
exports.newSubscriberNotification = functions.firestore
.document('users/$UID/notifications')
.onCreate(async event => {
How can I use Firebase Functions to do this? If there is no way, any suggestions for a workaround?
You should use the following code to trigger your Cloud Function:
exports.newSubscriberNotification = functions.firestore
.document('users/{userId}/notifications/{docId}')
.onCreate((snap, context) => {
//You get the values of the newly created doc as follows:
const newValue = snap.data();
console.log(newValue);
//You get the parameters as follows:
const userId = context.params.userId;
//console.log(userId);
const docId = context.params.docId;
//console.log(docId);
// You perform here the notification sending
});
For the code for the notification sending, have a look at this official Firebase Cloud Function sample: https://github.com/firebase/functions-samples/blob/master/fcm-notifications/functions/index.js

Firebase Functions cannot always save a user to Realtime Database

I use Firebase auth and realtime database in my Android app. This is the code that I use in Firebase functions to save the user email into the realtime database when they register in the app with email:
'use strict';
const functions = require('firebase-functions');
const admin = require('firebase-admin');
admin.initializeApp(functions.config().firebase);
exports.initializeUserProfile = functions.auth.user().onCreate(user => {
const userUid = user.uid;
return admin.auth().getUser(userUid).then(userRecord => {
const userProfile = {
email: userRecord.email
};
return admin.database().ref(`/profiles/${userUid}`).set(userProfile);
}).catch(error => {
console.log("Error fetching user data: ", error);
});
});
exports.removeUserProfile = functions.auth.user().onCreate(user => {
const userUid = user.uid;
return admin.database().ref(`/profiles/${userUid}`).remove();
});
When I register an user in the android app (I use the built in registration UI for Firebase), it gives me no error in the Functions logs:
My problem is that although I don't have an error in the log and the user was added to the Firebase Authentication section, the Realtime database doesn't contain the node with the email. The problem is very sporadic. Sometimes it registers it fine into the realtime database, but sometimes it doesn't (like in the log of Jun 25). In the Android app I try to query the database node of the user after registration to display they email and there I get an error (maybe it is an bug in my app, but anyhow, that code up there should be run on server side and the email should be in the Firebase Realtime Database).
What I also don't know is that why do I have those removeUserProfile calls in the log as I didn't remove any user from the Authentication database or from the Realtime database.
Actually, your two Cloud Functions are triggered with exactly the same event, i.e. onCreate(user). So it is normal that they are triggered (almost) simultaneously and that you see the two invocations in the log.
Since you write that "The problem is very sporadic" what is probably happening is that the new record is first created at /profiles/${userUid} by the initializeUserProfile Cloud Function BUT is then removed by the removeUserProfile Cloud Function.
So you should change the trigger of the removeUserProfile Cloud Function to onDelete():
exports.removeUserProfile = functions.auth.user().onDelete((user) => {
const userUid = user.uid;
return admin.database().ref(`/profiles/${userUid}`).remove();.
});

Setting a firestore document when a new user is created with firebase auth using node.js

I'm using firebase cloud functions, firebase auth and firestore.
I've done this before with firebase database but just not sure with firestore how to set a document in the users collection to the uid of a newly created firebase auth user.
const functions = require('firebase-functions');
const admin = require('firebase-admin');
admin.initializeApp(functions.config().firebase);
const db = admin.firestore()
exports.createUser = functions.auth.user().onCreate(event => {
const uid = event.data.uid;
console.log(uid);
db.collection('users').doc(uid);
});
The above completes ok in the logs but the uid isn't getting set in the database. Do I need to call set at some stage?
const collection = db.collection("users")
const userID = "12345" // ID after created the user.
collection.doc(userID).set({
name : "userFoo", // some another information for user you could save it here.
uid : userID // you could save the ID as field in document.
}).then(() => {
console.log("done")
})
Note, that the onCreate return has changed, it does return the user now, so event.data.uid isn't valid anymore.
The full function should look something like this, it will create a document with the user's uid in the "users" root-collection.
exports.createUser = functions.auth.user().onCreate((user) => {
const { uid } = user;
const userCollection = db.collection('users');
userCollection.doc(uid).set({
someData: 123,
someMoreData: [1, 2, 3],
});
});

Resources