DOUG STEVENSON: Hi there.
My name is Doug, and I'm a developer advocate
with the Firebase team.
I know that many of you are using Firebase Realtime
Database as the back end for your applications.
In particular, you might be using
it to implement a chat room.
But you might be looking to add some more functionality to it
without having to duplicate logic among all your client
apps, and you might want to insulate your code from hackers
that could be trying to change the way your app works.
Turns out, you can do these things with Cloud Functions.
I have an app with multiple chat rooms
and the database looks like this.
There's a top-level node called "rooms,"
and under that child nodes with unique IDs for each room.
Then, within each room, there's another node called "messages,"
and that contains chat messages, each with a unique push ID.
Each of these messages has a child value
for the text of the message and the name of the person
who sent it.
This is fine-- nothing wrong here.
But what we just saw was obviously
a chat room about pizza driven by the dialogue
from a movie called "Inside Out," which I liked very much.
But I want to add some pizzazz to it,
and what better way to add pizzazz than to use more pizza?
This is a pizza emoji-- fun, right?
Wouldn't it be neat if all the occurrences
of the word "pizza" in this chat were replaced by this emoji?
You can code all that logic into the client,
but that requires each client to do this correctly
for each of the client platforms you support,
and you'd have to publish a new version of each client
to keep them all in sync.
Bleh.
Wouldn't you rather just add this functionality once
for all client apps, and not have
to change and publish each one?
Sounds good, right?
So let's see how to do that with Cloud Functions for Firebase.
When working with the Realtime Database,
you have four types of triggers to work with that respond
to changes in your database.
These are onCreate, onUpdate, onDelete, and onWrite.
Each of these triggers could be run on the back end managed
by Cloud Functions.
OnCreate triggers after a new node is added to the database,
onUpdate triggers after existing data is changed,
and onDelete triggers after a node is deleted.
OnWrite triggers with all of these changes,
but you have to figure out in your code which kind of change
it is.
In each of these cases, you have to specify
a location in the database that should
be affected by these triggers, and that location
could contain a wildcard path component,
which I'll explain later.
Today, I'll talk about onCreate, then the other triggers
in future videos.
But I'll skip onWrite, since it's easier
to work with the other three.
I'm going to use an onCreate trigger
to rewrite people's new messages to use that pizza emoji instead
of the word "pizza," and it's pretty easy
to do this substitution.
Let's take a look.
In my project, I already have a script
that creates the conversation I showed earlier.
It's pretty helpful for testing.
There's a bunch of boilerplate in this type script code,
but the thing to know is that it simulates
the addition of a few messages by pushing them
into the database at a pre-determined location.
I'll run it right now so you can see what it does.
When I go to the terminal and run the script,
it pushes each message to the database
followed by some delay using a hard-coded room ID.
And you can see it working here in the Firebase
console in real time as each message is individually added.
Now, back to VS code.
In order to add some pizzazz to these messages,
I'll need to use the Cloud Functions for Firebase SDK
to build and export a real-time database trigger.
Using the ref method, I'll tell it
to respond to changes under the path
"rooms," a wildcard room ID, messages,
and a wildcard message ID.
These two wildcards in curly braces
will match any child node in the path.
Since I'm writing code that should
be run whenever a node is newly created,
I'll make this an onCreate trigger.
Now, if I want to know what to pass to onCreate,
I can simply command-click the onCreate symbol
to have VS Code take me to its type script definition
inside the Functions SDK.
For Linux and Windows, it will be control-click instead.
Now I can see that onCreate requires a handler function
that itself receives two arguments--
a data snapshot and an event context--
and it returns a promise that resolves
when all of its asynchronous work is done.
Remember from my video series about promises
that all Cloud Functions that are not
HTTP functions are called background functions,
and they must return a promise that becomes
fulfilled or rejected only after all the asynchronous work
started in that function is complete.
That's what this TypeScript definition
is reminding us here--
don't get fooled by the object type called PromiseLike.
That's just an interface that says the return object must
have an appropriate then and catch method just
like a normal promise.
You can use VS Code to click into that, too, if you want.
Oh, and if there's no async work left
to be done in your handler function,
you can just return "null" instead of a promise.
But that's not going to be the case for me today.
Let's keep going, and you'll see how that works out.
OnCreate takes a handler function as its argument,
and I'll use an anonymous function here
with the fat arrow syntax.
This is where my logic will live.
As we saw, the handler function receives two arguments--
a data snapshot and an event context.
If you hover the mouse over the identifier,
you get reminded of their types.
If you want to know the string values of those wildcard values
from the path, you can use the event context object for that.
The event.params object contains properties
with the same names as those wildcards,
and their values will be the strings you're looking for.
Turns out I don't really need them in this function,
but I'll log their values anyway,
which helps with debugging if needed.
Now to get the data from the database that
was added at this location, I'll use the snapshot object.
It has a method called val that gives me
a copy of the raw data as a JavaScript object.
Now, remember that messages look like this in the database.
The object I received in the snapshot
will have these properties set on it.
The text property is what I need to replace
pizza words with the emoji.
So what I'll do is get a hold of that text value
in the snapshot data, add some pizzazz to it,
then assign that to a const with the same name.
So how do I add pizzazz?
I'll paste the implementation of that function right here.
What it's doing is using a regular expression
to scan the input string for instances of the full word
"pizza" and replacing each one with the pizza emoji instead.
The modified string is returned.
Now that I have a way to replace pizza words with the emoji,
I can write the modified string back into the database.
That's easy with the ref property of the snapshot
object.
If I hover the mouse over the ref property,
VS Code tells me it's a reference type object
and it has admin access to the database.
I want to say a bit more about this ref property.
There are two things you should know about it.
First, the reference is very similar to other database
references that you might be using
in your client code that also point
to some location in the database.
Here in Cloud Functions, the reference
points to the location matched by the pattern
given to the ref method in the function definition.
You can use it to read and write the database at that location,
or build more references to other locations.
Second, the reference has admin privileges,
which means that it has full control over your database.
Specifically, it means that none of the security
rules on your database will affect
the way that reading or writing is done using this reference.
So keep those things in mind as you write your code.
OK, now that I've added some pizzazz to the message text,
I want to write it back to the database,
and I can do that with the snapshot reference by calling
its update method and passing it an object with the children
I want to update.
But there's one thing missing here--
update is an asynchronous method,
and we can see that when I hover the mouse over the method.
Update returns a promise, so my function
needs to wait on it to complete, and I
can do that by simply returning the promise as the last thing
to do here.
Now I'll switch to my terminal and run
Firebase deploy only functions from my project directory.
After the function is deployed, I'll
run my dialog script again, then switch to the Firebase console
to see it work in real time.
And you can see the pizza words getting
replaced with the emojis by the function.
Such pizzazz.
My chat room has more character.
But I also want to allow people in the room
to edit their messages after they're sent.
OnCreate triggers don't fire when
existing data is changed in the database-- only
when new data is added.
So that means if someone edits their message,
it won't receive this special treatment.
If you want to see how to do that,
be sure to subscribe right here to the Firebase channel
on YouTube, and you'll get notified
when the next video is ready.
Until then, check out the links to the documentation and code
samples in the description below.
And I'll see you next time.
[MUSIC PLAYING]