Cloud Function error "Cannot read property 'data' of undefined" - javascript

I recently started playing with cloud functions and I am getting this error:
> TypeError: Cannot read property 'data' of undefined
> at exports.createPost.functions.firestore.document.onCreate (/srv/index.js:15:37)
> at cloudFunction (/srv/node_modules/firebase-functions/lib/cloud-functions.js:131:23)
> at /worker/worker.js:825:24
> at <anonymous>
> at process._tickDomainCallback (internal/process/next_tick.js:229:7)
and this is my code
const functions = require('firebase-functions');
const admin = require('firebase-admin');
const algoliasearch = require('algoliasearch');
const ALGOLIA_APP_ID = "";
const ALGOLIA_ADMIN_KEY = "";
const ALGOLIA_INDEX_NAME = "Posts";
admin.initializeApp(functions.config().firebase);
//const firestore = admin.firestore;
exports.createPost = functions.firestore
.document('User/{UserId}/Posts/{PostsID}')
.onCreate( async (snap, context) => {
const newValue = snap.after.data();
newValue.objectID = snap.after.id;
var client = algoliasearch(ALGOLIA_APP_ID, ALGOLIA_ADMIN_KEY);
var index = client.initIndex(ALGOLIA_INDEX_NAME);
index.saveObject(newValue);
});
The onCreate function triggers at the right time and the problem is just the error. I have done my research and couldn't figure it out. I hope I can get some help.
Thanks in advance :).

onCreate functions receive a DocumentSnapshot type argument as its first parameter. It looks like your function is not actually expecting that. Since you're trying to use a property called after, it looks like your code is expecting a Change type argument, which will never be the case for onCreate events. Change type objects are delivered to onUpdate or onWrite events only so you can detect the before and after states of a document.
If you want the data from a newly created document in an onCreate type trigger, you should code like this:
exports.createPost = functions.firestore
.document('User/{UserId}/Posts/{PostsID}')
.onCreate( async (snap, context) => {
const newValue = snap.data();
// use the document properties of newValue here

Related

Flutter Firebase Functions Error: Database not defined

I've created a delete oldFiles function for my Database that deletes nodes from my chat messages. I've used the example function provided by Firebase and updated it to fit my use. My database structure is databaseName/messages/{pushId} and I've added const functions = require('firebase-functions') and const admin = require('firebase-admin') and admin.initializeApp(). Here is what I have...
exports.deleteOldItems = functions.database.ref('messages/{pushId}').onWrite(async (change) => {
const ref = change.after.ref.parent; // reference to the parent
const now = Date.now();
const cutoff = (DateTime.now().millisecondsSinceEpoch - CUT_OFF_TIME);
const oldItemsQuery = ref.orderByChild('timestamp').endAt(cutoff);
const snapshot = await oldItemsQuery.once('value');
// create a map with all children that need to be removed
const updates = {};
snapshot.forEach(child => {
updates[child.key] = null;
});
// execute all updates in one go and return the result to end the function
return ref.update(updates);
});
When I review my Function logs, I'm getting the following errors...
ReferenceError: DateTime is not defined
at exports.deleteOldItems.functions.database.ref.onWrite (/srv/index.js:17:18)
at cloudFunction (/srv/node_modules/firebase-functions/lib/cloud-functions.js:131:23)
at /worker/worker.js:825:24
at
at process._tickDomainCallback (internal/process/next_tick.js:229:7)
And my Functions are finishing with status: error. Any ideas to what may be going on?
DateTime isn't a valid JavaScript object or identifier. If you want to work with dates and times, you will need to work with Date, as you are in the line just above where you have DateTime. You should probably review the JavaScript documentation for Date to learn how it works.

Increment Realtime Database count on new user with Firebase Cloud Function

* UPDATED: THIS WORKS. SEE ANSWER BELOW *
I'm trying to write a Firebase Cloud Function that increments a Realtime Database /userCount value whenever a new user is created.
I've tried the following, but am getting "TypeError: userCountRef.transaction is not a function" in incrementCountOnNewUser.
Transactions are working for my other function incrementCountOnOpen when the value of garage is set to true, but the ref is derived from the after event object.
Any suggestions on how to do this?
const functions = require('firebase-functions');
const admin = require('firebase-admin');
admin.initializeApp();
// const userCountRef = functions.database.ref("/userCount"); // does NOT work
const userCountRef = admin.database().ref('/userCount'); // THIS WORKS!
exports.incrementCountOnNewUser = functions.auth.user().onCreate((user) => {
return userCountRef.transaction(count => count + 1);
});
exports.incrementCountOnOpen = functions.database.ref("/garage").onUpdate(({after}) => {
const countRef = after.ref.parent.child('count');
const newValue = after.val();
return newValue
? countRef.transaction(count => count + 1)
: null;
});
It turns out that the code above works! I had switched from the commented out code (which does NOT work). I guess it didn't wait long enough for it propagate after I published, because I see it working now!
Sorry for the confusion.

Cloud function works locally, but not after deploying it

I am writing my very first Cloud Function which replaces the word "happy" with a smile.
const functions = require("firebase-functions");
const admin = require('firebase-admin');
admin.initializeApp();
exports.emojify = functions.database.ref("/messages/{pushId}/text").onWrite((change, context) => {
const original = change.before.val();
const emojified = emojifyText(original);
return admin.database().ref().set(emojified);
});
function emojifyText(text) {
let t = text;
t = t.replace(/\b(H|h)appy\b/ig, "😀");
console.log("Result:", t);
return t;
};
I found out I can test before deploying by running firebase functions:shell and doing like this:
firebase > emojify({ before: "Happy!" })
'Successfully invoked function.'
firebase > info: User function triggered, starting execution
info: Result: 😀!
info: Execution took 2949 ms, user function completed successfully
It works. However, when testing with my Android app, the logs of my function will show:
TypeError: Cannot read property 'replace' of null
at emojifyText (/user_code/index.js:15:13)
at exports.emojify.functions.database.ref.onWrite (/user_code/index.js:8:23)
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)
I don't understand.
After some new attempts, my code is the following:
const functions = require("firebase-functions");
const admin = require('firebase-admin');
admin.initializeApp();
exports.emojify = functions.database.ref("/messages/{pushId}/text").onWrite((change, context) => {
// if (!change.before.val()) { return null; }
// ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
// I tried with this and without it, but neither helped.
const original = change.after.val();
const emojified = original.replace(/\b(H|h)appy\b/ig, "😀");
return admin.database().ref("/messages/{pushId}/text").update(emojified);
});
The closest I got was to actually make it erase everything in the base, including the path messages and replace it with the written text, with the text replaced by the emoji. Something like:
But it was using set() instead of update(), which didn't show any sign of modifying anything.
const original = change.before.val(); is the data before the write. So if there was no data at the "/messages/{pushId}/text" node before you write there, the variable original will be null.
You shoud change to:
const original = change.after.val(); which is the data after the write, i.e. your new data, which you want to "emojify".
Update following your comments below
You should use the update() method (doc here), as follows:
return admin.database().ref("/messages/" + context.params.pushId + "/").update({ text: emojified });

FieldValue undefined when using functions and Firestore

I have the following function:
const functions = require('firebase-functions');
const admin = require('firebase-admin');
const FieldValue = require('firebase-admin').FieldValue;
module.exports = functions.firestore
.document('students/{studentId}')
.onDelete(event => {
const student = event.data.previous.data();
const { id, semester } = student;
const classId = student.class;
const deleteObj = {};
deleteObj[id] = FieldValue.delete(); //Line 12, this is where the error orccurs
return admin
.firestore()
.collection('semesters')
.doc(semester)
.collection('students')
.doc(classId)
.update(deleteObj);
});
Every time i run it I get the following error:
TypeError: Cannot read property 'delete' of undefined
at module.exports.functions.firestore.document.onDelete.event (/user_code/deleteStudent.js:12:37)
Looking at the docs I cant really see what I am doing wrong?
// Get the `FieldValue` object
var FieldValue = require("firebase-admin").FieldValue;
// Create a document reference
var cityRef = db.collection('cities').doc('BJ');
// Remove the 'capital' field from the document
var removeCapital = cityRef.update({
capital: FieldValue.delete()
});
Update
So, using the web equivalent seems to work: admin.firestore.FieldValue.delete(). But that seems like a bug since i'm in a nodejs environment? Can any Firebaser confirm or deny wether that is the case or not? I'd gladly file a bug report.
Turns out it was a mistake in the documentation, the correct import should have been const FieldValue = require('firebase-admin').firestore.FieldValue;
Update
It should be said that Firebase responded within hours and are correcting the docs asap.
It worked!
Require firebase-admin
const admin = require('firebase-admin')
Add this constant
const fieldValue = admin.firestore.FieldValue;
Now use
fieldValue.delete()
For more reference : otcollect.com/post
Here you can find the solution on github.
Using typescript with
"firebase-admin": "^11.0.0"
"firebase-functions": "^3.22.0"
you have to use
import { FieldValue } from 'firebase-admin/firestore'
Same for TimeStamp

Cloud Function for Firebase Type Error - Cannot Read Property

I'm trying to write a Cloud Function that creates a record whenever someone uses our legacy app to create a record (we have changed the Firebase backend architecture and want to slowly migrate users). However, I'm getting the following error in my logs:
TypeError: Cannot read property 'update' of undefined
at exports.makeNewComment.functions.database.ref.onWrite.event (/user_code/index.js:14:92)
at /user_code/node_modules/firebase-functions/lib/cloud-functions.js:35:20
at process._tickDomainCallback (internal/process/next_tick.js:129:7)
Here is the script in question:
//required modules
var functions = require('firebase-functions');
const admin = require('firebase-admin');
// Listens for new comments added to /comments/ and adds it to /post-comments/
exports.makeNewComment = functions.database.ref('comments/{commentId}').onWrite(event => {
// Grab the current value of what was written to the Realtime Database.
const commentId = event.params.commentId;
const comment = event.data.val();
// You must return a Promise when performing asynchronous tasks inside a Functions such as
// writing to the Firebase Realtime Database.
//return event.data.ref.parent.child('post-comments').set(comment);
return functions.database.ref('post-comments/' + comment['postID'] + '/' + commentId).update(comment).then(url => {
return functions.database.ref('user-comments/' + comment['postedBy'] + '/' + commentId).update(comment);
});
});
//initialize
admin.initializeApp(functions.config().firebase);
Thanks!
You can't use functions.database.ref() in the middle of a function to get a ref to somewhere in your database. That's only for defining a new Cloud Function.
If you want a ref to somewhere in your database, you can use event.data.ref or event.data.adminRef to get a ref to the location where the event triggered. You could then use the root property of that to rebuild a new ref to somewhere else in the database. Or you can use your admin object to build a new ref.
It might be helpful to look at some sample code to get a sense of how things work.
Based on Doug's answer, you can replace functions.database.ref with event.data.ref.root.
var functions = require('firebase-functions');
const admin = require('firebase-admin');
exports.makeNewComment = functions.database.ref('comments/{commentId}').onWrite(event => {
const commentId = event.params.commentId;
const comment = event.data.val();
return event.data.ref.root.child('post-comments/' + comment['postID'] + '/' + commentId).update(comment).then(url => {
return event.data.ref.root.child('user-comments/' + comment['postedBy'] + '/' + commentId).update(comment);
});
});
admin.initializeApp(functions.config().firebase);

Categories

Resources