I am following a tutorial where I am adding some Firebase Cloud Functions to my project (step 5). I have successfully deployed my cloud function to firebase but nothing happens when I add a new product manually in the Firebase Database console. I discovered that the Firebase cloud function is triggered but it is getting an error: "TypeError: Cannot read property 'productId' of undefined"
What am I doing wrong?
const functions = require('firebase-functions');
const admin = require("firebase-admin");
admin.initializeApp(functions.config().firebase);
exports.sendMessage = functions.firestore
.document('products/{productId}')
.onCreate(event => {
const docId = event.params.productId; // <-- error here
const name = event.data.data().name;
const productRef = admin.firestore().collection('products').doc(docId)
return productRef.update({ message: `Nice ${name}! - Love Cloud Functions`})
});
That tutorial must be out of date. Some things have changed in the Functions SDK when it released version 1.0. You can read about those changes here.
Database triggers are now passed two parameters instead of one. The new context parameter contains the value of wildcards in the reference path:
exports.sendMessage = functions.firestore
.document('products/{productId}')
.onCreate((snapshot, context) => {
const docId = context.params.productId;
If you want to continue with that tutorial, you'll have to manually convert all of its old stuff to new stuff.
OK. So thanks to Dough Stevensson's answer notifying me that the syntax was old I have now a solution:
const admin = require('firebase-admin');
const functions = require('firebase-functions');
admin.initializeApp(functions.config().firebase);
var db = admin.firestore();
exports.sendMessage = functions.firestore
.document('products/{productId}')
.onCreate((snapshot, context) => {
const docId = context.params.productId;
const productRef = db.collection('products').doc(docId)
return productRef.update({ message: `Nice ${name}!`})
});
Related
I've tried many methods such as
const admin = require("firebase-admin");
admin.initializeApp();
const db = admin.firestore();
const docRef = db.collection("users").doc(dynamicDocID).get()
const docRef = db.collection("users").doc(dynamicDocID)
as well as many other and keep getting undefined or a promise that never seems to be resolved
Cant seem to find proper docs on this if anything
Since Cloud Functions for Firebase are written in Node.js, have a look at the Node.js examples in the Firestore documentation.
Based on that:
const docRef = db.collection("users").doc(dynamicDocID)
const document = await docRef.get()
console.log(document.id, document.data())
Or if you can't use await:
const docRef = db.collection("users").doc(dynamicDocID)
return docRef.get().then((document) => {
console.log(document.id, document.data())
})
When I use Firebase Cloud Functions in my Flutter app to create a document inside a collection it works:
const functions = require('firebase-functions');
const admin = require('firebase-admin');
exports.onCreatePost = functions.firestore
.document("/posts/{postId}")
.onCreate(async (snap, context) => {
const doc = snap.data()
const creatorId = doc.creatorId
admin.firestore().collection('feeds').doc(creatorId).set({
Id: creatorId,
isRead: false,
timestamp: admin.firestore.FieldValue.serverTimestamp(),
})
});
But when I try to add the same document inside a subcollection in that document, it does not work:
const functions = require('firebase-functions');
const admin = require('firebase-admin');
exports.onCreatePost = functions.firestore
.document("/posts/{postId}")
.onCreate(async (snap, context) => {
const doc = snap.data()
const creatorId = doc.creatorId
admin.firestore().collection('feeds').doc(creatorId).collection('feedItems').doc(context.params.postId).set({
Id: creatorId,
isRead: false,
timestamp: admin.firestore.FieldValue.serverTimestamp(),
})
});
What am I doing wrong? I do see that the cloud function was completed successfully in the logs, but the docment is not created in my Cloud Firestore.
I would expect neither function to work reliably, because you aren't returning a promise that resolves after the asynchronous work is complete. If you don't return a promise, then Cloud Functions might terminate your function before it's done.
Minimally, you should return the promise returned by set().
return admin.firestore()
.collection('feeds')
.doc(creatorId)
.collection('feedItems')
.doc(context.params.postId)
.set(...)
You should also check the Cloud Functions log for errors. Errors will not show up in your app since the code is running completely outside of it.
I suggest also reviewing the documentation on this.
I wrote a very simple Cloud Function to add some fields in Firestore with some info from the newly created FirebaseUser in FirebaseAuth.
In Firestore, I have a collection named "highscore". Everytime, a new user is created, I want to add a document with the firebaseusers uid as document, and 2 fields, like:
highscore/uid/score & highscore/uid/usernick (e.g highscore/fgt38gudg9/430 & highscore/fgt38gudg9/cooldude45)
This is my function:
const functions = require('firebase-functions');
const admin = require('firebase-admin');
admin.initializeApp();
const db = admin.database()
//On user creation, trigger: Add information to /highscore/uid
exports.onUserCreation = functions.auth.user().onCreate((user) => {
const collection = db.collection("highscore")
const userid = user.uid
const usernick = user.displayName
collection.doc(userid).set({
score: 0
user: usernick
})
})
However, when the function is triggered, I run into this error:
TypeError: db.collection is not a function
at exports.onUserCreation.functions.auth.user.onCreate (/srv/index.js:11:24)
at cloudFunctionNewSignature (/srv/node_modules/firebase-functions/lib/cloud-functions.js:120:23)
at /worker/worker.js:825:24
at <anonymous>
at process._tickDomainCallback (internal/process/next_tick.js:229:7)
I can't figure this out. Any suggestions?
I think you are using Firstore:
const db = admin.firestore()
admin.database() gets you a reference to the Realtime Database instance for your project. What you want to use instead is admin.firestore().
Also, you will want to return the promise that you get from set(), otherwise, the operation might not complete before the function terminates.
return collection.doc(userid).set({
score: 0
user: usernick
})
Be sure to read the documentation about terminating functions to understand your obligations in dealing with async work represented by promises.
Getting the following error:
"Cannot read property 'userName' of undefined
at Promise.all.then.result"
Also Getting Error
"The behavior for Date objects stored in Firestore is going to change
AND YOUR APP MAY BREAK.
To hide this warning and ensure your app does not break, you need to add the
following code to your app before calling any other Cloud Firestore methods:
const firestore = new Firestore();
const settings = {/* your settings... */ timestampsInSnapshots: true};
firestore.settings(settings);
With this change, timestamps stored in Cloud Firestore will be read back as
Firebase Timestamp objects instead of as system Date objects. So you will also
need to update code expecting a Date to instead expect a Timestamp. For example:
// Old:
const date = snapshot.get('created_at');
// New:
const timestamp = snapshot.get('created_at');
const date = timestamp.toDate();
Please audit all existing usages of Date when you enable the new behavior. In a
future release, the behavior will change to the new behavior, so if you do not
follow these steps, YOUR APP MAY BREAK."
However in my android project the place where i have defined the "Date" variable i have place the "#ServerTimestamp" on top.
Appreciate the help guys.
Code:
/*eslint-disable */
const functions = require('firebase-functions');
const admin = require('firebase-admin');
admin.initializeApp(functions.config().firebase);
exports.sendNotification = functions.firestore.document('notifications/{userEmail}/userNotifications/{notificationId}').onWrite((change, context) => {
const userEmail = context.params.userEmail;
const notificationId = context.params.notificationId;
return admin.firestore().collection("notifications").doc(userEmail).collection("userNotifications").doc(notificationId).get().then(queryResult => {
const senderUserEmail = queryResult.data().senderUserEmail;
const notificationMessage = queryResult.data().notificationMessage;
const fromUser = admin.firestore().collection("users").doc(senderUserEmail).get();
const toUser = admin.firestore().collection("users").doc(userEmail).get();
return Promise.all([fromUser, toUser]).then(result => {
const fromUserName = result[0].data().userName;
const toUserName = result[1].data().userName;
const tokenId = result[1].data().tokenId;
const notificationContent = {
notification: {
title: fromUserName + " is shopping",
body: notificationMessage,
icon: "default"
}
};
return admin.messaging().sendToDevice(tokenId, notificationContent).then(result => {
console.log("Notification sent!");
//admin.firestore().collection("notifications").doc(userEmail).collection("userNotifications").doc(notificationId).delete();
});
});
});
});
Make sure the document you're request actually exists. data() will return undefined if it doesn't. You can use the exists property on the resulting DataSnapshot to check if a document was actually found.
I'm currently trying to send a confirmation email with sendgrid API from a firebase function.
The API is not the problem though, it seems to work fine, my problem is that I can't get the child oncreate's value (Firebase function log):
TypeError: Cannot read property 'participant_id' of undefined
at exports.SendEmail.functions.database.ref.onCreate.event (/user_code/index.js:15:38)
at Object.<anonymous> (/user_code/node_modules/firebase-functions/lib/cloud-functions.js:112: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:82:36)
at /var/tmp/worker/worker.js:716:24
at process._tickDomainCallback (internal/process/next_tick.js:135:7)
And here's my code:
const functions = require('firebase-functions');
const admin = require('firebase-admin');
admin.initializeApp(functions.config().firebase);
const SENDGRID_API_KEY = functions.config().sendgrid.key;
const sgMail = require('#sendgrid/mail');
sgMail.setApiKey(SENDGRID_API_KEY);
exports.SendEmail = functions.database.ref('participants/{participant_id}').onCreate(event => {
const participant_id = event.params.participant_id;
const db = admin.database();
return db.ref(`participants/${participant_id}`).once('value').then(function(data){
const user = data.val();
const email = {
to: user.email,
from: 'recrutement.cisssme16#ssss.gouv.qc.ca',
subject: "Bienvenue au Blitz d'embauche 2018!",
templateId: 'dd3c9553-5e92-4054-8919-c47f15d3ecf6',
substitutionWrappers: ['<%', '%>'],
substitutions: {
name: user.name,
num: user.num
}
};
return sgMail.send(email)
})
.then(() => console.log('email sent to', user.email))
.catch(err => console.log(err))
});
This is not my first firebase function. I even copied pasted previous working codes which worked fined and I still get an undefined value!
What's the problem here? Did firebase changed event.params?
Also my participant_id is an integer value (ex.: 3827), if that changes something.
Thanks in advance!
The signature of the function is wrong, take a look at this example on Handle event data:
exports.makeUppercase = functions.database.ref('/messages/{pushId}/original')
.onCreate((snapshot, context) => {
console.log('Uppercasing', context.params.pushId, original);
});
So just adjust your onCreate function and you'll be all set :)