DOUG STEVENSON: Hey, Firebase developers.
My name is Doug, and welcome back
to this mini series about Firebase realtime database
triggers in Cloud Functions.
In my last video, I implemented a database
trigger using onCreate that changes the word
"pizza" to a pizza emoji for all new messages added to a chat
room.
It works great for that.
But it doesn't work if someone edits their message
after it was added.
That's because onCreate triggers only fire when
a new node appears in the database that
matches the configured path.
If I want to write some code that responds us
to changes at the same location in the database,
I can use an onUpdate trigger for that.
It's similar to onCreate, but there are some key differences.
Let's take a quick look at that in the console.
Here you can see a series of messages
populated by a script I wrote.
The word "pizza" has already been
replaced by the emoji in a couple places.
If I edit one of the messages and add
the word "pizza" in there, that doesn't get replaced.
But I can make that happen with an additional trigger.
So I'll switch into VS Code to add that to my project.
First, take a look at that onCreate trigger
from last time.
It gets a hold of the raw data of the new message,
pulls the text property out of it,
and uses the add pizzazz function to do the replacement.
Then it writes the updated string back to the database.
onUpdate triggers are similar to onCreate triggers,
so I'll just copy the function definition and paste it below.
Then I'll change its name to onMessage update and change
onCreate to onUpdate.
The handler function for onCreate
gave me a snapshot and a context.
But onUpdate is different.
If I hover over onUpdate, it shows me
that the handler function here gets
a change object and a context.
The context is the same as onCreate,
but the change object is different.
The change object type itself is a generic type.
Here you can see that it's parameterized
by the data snapshot type, which is the same as the snapshot
type that was delivered to onCreate.
So what's up with this change object?
onUpdate triggers fire when something
has changed in the database at the location of the trigger.
This change object is telling you
the contents of that location both before and after
the update.
You can use this to figure out exactly what data
changed in the database at that location.
There are two properties on change objects
called before and after.
For this change object, they're going
to be data snapshot objects, as required by the generic type
of the change.
OK, let's use them.
I'm really interested in the new contents of the database
after the update.
So I'll use the after property to get that snapshot
and call the val method on it to get the raw data.
Then I can do the same thing I did in onCreate
and pull the text string out of it,
call the add pizzazz method with it, and store the result.
Then I can use the ref from the after snapshot
to write the new text back to the database.
There's one more thing I want to do while I'm here,
and that's add another child value
to the database with the time stamp of the edit.
That's pretty easy.
Oh, and remember that update returns a promise,
so we need to return that promise from the function
so that Cloud Functions waits for everything
to finish before cleaning up.
And we're done, right?
We're definitely not done.
There's actually one huge problem here,
but it's not obvious at first.
Think about what this function is doing.
It's triggering when there's an update
at a location in the database, then making another update
to that same location.
This means the function is going to effectively trigger itself
again with that second change, which
is going to cause another change, and another, forever.
I'm sure I don't have to tell you that's crazy bad right
there, because this is the sort of thing that
can cost you a lot of money for all those function invocations.
And you'll need to scramble to fix it.
So we obviously need a way to stop this madness.
Fortunately, we can do that with some additional code.
What we need to do here is compare the message text
in the before and after snapshots.
So I'll get a hold of the data before the change
and then check to see if the message
text was changed at all.
If there's no change, there's nothing
for this function to do.
The change that triggered this function is probably just
the new time edited time stamp.
In that case, I can just return null instead of a promise
to tell Cloud Functions there's no additional work to wait on.
I think this should do what I want now.
So I'll deploy it.
And when that's done, I'll give it
a try in the Firebase console.
In the console, I'll change the text for one of the messages,
add pizza to it, and see that pizza
get immediately emojified.
Nice.
It's important to think carefully ahead of time when
writing an onUpdate trigger that writes back
to the same location of the trigger.
Otherwise, you could get into a bad situation
with an infinite loop that costs you time and money to fix.
The same applies to onWrite triggers,
if you choose to use one.
But you won't have this problem with onCreate or onDelete
triggers.
However, with all kinds of database triggers,
you do have to think about what happens when multiple triggers
fire at the same time, and write code correctly
in that situation.
And I'll show you how to do that next time,
along with a new onDelete trigger.
So if you haven't already, be sure to subscribe
right here to the Firebase channel on YouTube
and find out when that's ready.
And I'll see you then.
[MUSIC PLAYING]