smartexpert
smartexpert

Reputation: 3035

Firebase cloud function Firestore trigger onWrite not behaving as expected when testing locally

I am testing Firestore triggered Firebase Cloud Function locally in Node using Firebase Functions Shell.

When working with a onWrite trigger and passing a new document by calling updateMonth({foo:'new'}) from the functions shell, I can't get a reference to the new document.

exports.updateMonth = functions.firestore
.document('users/{userId}').onWrite((change, context) => {
    const document = change.after.exists ? change.after.data() : null;
    console.log("change.after.exists", change.after.exists)
    console.log("change.before.exists", change.before.exists)
    const oldDocument = change.before.data();
    console.log("Old:",oldDocument)
    return true
})

When calling with both a before and after to emulate a document update updateMonth({before:{foo:'old'},after:{foo:'new'}}) it works as expected. However, when called with just updateMonth({foo:'new'}), both before and after documents don't seem to exist :

i  functions: Preparing to emulate functions.
Warning: You're using Node.js v8.4.0 but Google Cloud Functions only supports v6.11.5.
+  functions: hi
+  functions: helloWorld
+  functions: updateMonth
firebase > updateMonth({before:{foo:'old'},after:{foo:'new'}})
'Successfully invoked function.'
firebase > info: User function triggered, starting execution
info: change.after.exists true
info: change.before.exists true
info: Old: { foo: 'old' }
info: Execution took 20 ms, user function completed successfully

firebase > updateMonth({foo:'new'})
'Successfully invoked function.'
firebase > info: User function triggered, starting execution
info: change.after.exists false
change.before.exists false
Old: undefined
Execution took 2 ms, user function completed successfully

I'm not sure how I can get a reference to the new document being created. I would expect the change.after.exists to be true in that case.

Upvotes: 6

Views: 5690

Answers (2)

smartexpert
smartexpert

Reputation: 3035

After many iterations, it turns out the solution was really simple. To successfully emulate a new document creation that will trigger onWrite when emulating cloud functional locally using firebase functions shell, you need to explicitly provide the after property as in updateMonth({after:{foo:'newVal'}}):

firebase > updateMonth({after:{foo:'newVal'}})
'Successfully invoked function.'
firebase > info: User function triggered, starting execution
change.after.exists true
change.before.exists false
New: { foo: 'newVal' }
info: Execution took 291 ms, user function completed successfully

The documentation at https://firebase.google.com/docs/functions/local-emulator#invoke_firestore_functions does not explicity make this clear hence the confusion. Perhaps this may be a nice enhancement to add to firebase-tools firebase functions:shell.

Upvotes: 3

Doug Stevenson
Doug Stevenson

Reputation: 317522

With onWrite and onUpdate triggers, you need to declare the "before" and "after" state of the document in all cases. You can't try to simplify the API by omitting the "before" or "after" keys. Try this, for example, if you just want to test document creation:

updateMonth({
    after:{foo:'new'}
})

Also, if you have code that's only interested in document creation, it's likely easier to just write on onCreate trigger rather than trying to figure out the document state in an onWrite. Personally, I avoid onWrite and use the other three, because they're easier to reason about what change was actually made.

Upvotes: 7

Related Questions