With Cloud Functions, you can handle events in the Firebase Realtime Database. Processing database events with Cloud Functions is more flexible than processing on the client side for these reasons:
- Each change to the database is processed individually.
- Functions run with administrative privileges, so actions may be performed that bypass the security restrictions of the authenticated user.
- You can change the behavior anytime you want without having to update client code.
The Realtime Database supports the
onWrite()
event, which triggers anytime data is created, destroyed, or changed in a
specified database location.
In a typical lifecycle, a Firebase Realtime Database function does the following:
- Waits for changes to a particular database location for write events.
- Fires when a write event occurs and performs its tasks (see What can I do with Cloud Functions? for examples of use cases).
- Receives an event data object that contains two snapshots of the data stored at the specified path: one with the original data prior to the change, and one with the new data.
Trigger a database function
You create a new function for Realtime Database events
with functions.database.
To control when your function should trigger, call
ref(path). This method
directs your function to handle writes at a certain path within your
database:
functions.database.ref('/foo/bar')
Path specifications match all writes that touch a path, including writes
that happen anywhere below it. If you set the path
for your function as /foo/bar, it matches writes at both of these locations:
/foo/bar
/foo/bar/baz/really/deep/path
In either case, Firebase interprets that the event occurs at /foo/bar,
and the event data includes
the old and new data at /foo/bar. If the event data might be large,
consider using multiple functions at deeper paths instead of a single
function near the root of your database. For the best performance, only request data at the deepest level
possible.
You can specify a path component as a wildcard by surrounding it with curly
brackets; ref('foo/{bar}') matches any child of /foo. The values of these
wildcard path components are available within the
event.params object of
your function. In this example, the value is available as
event.params.bar.
Paths with wildcards can match multiple events from a single write. An insert of
{
"foo": {
"hello": "world",
"firebase": "functions"
}
}
matches the path "/foo/{bar}" twice: once with "hello": "world"
and again with "firebase": "functions".
Handle event data
When handling a Realtime Database event,
event.data is a
DeltaSnapshot.
In this example, the function retrieves event.data for
the specified path, converts the string at that location to uppercase, and
writes that modified string to the location /messages/{pushId}/uppercased:
// Listens for new messages added to /messages/:pushId/original and creates an
// uppercase version of the message to /messages/:pushId/uppercase
exports.makeUppercase = functions.database.ref('/messages/{pushId}/original')
.onWrite(event => {
// Grab the current value of what was written to the Realtime Database.
const original = event.data.val();
console.log('Uppercasing', event.params.pushId, original);
const uppercase = original.toUpperCase();
// You must return a Promise when performing asynchronous tasks inside a Functions such as
// writing to the Firebase Realtime Database.
// Setting an "uppercase" sibling in the Realtime Database returns a Promise.
return event.data.ref.parent.child('uppercase').set(uppercase);
});
Reading the previous value
The DeltaSnapshot has a
previous
property that lets you inspect what was saved to the database before the
event. The previous property returns a new DeltaSnapshot where all
methods (for example,
val() and
exists())
refer to the previous value. You can read the new value again by either using
the original DeltaSnapshot or reading the
current
property on any DeltaSnapshot to read a value as it is after the event.
For example, the previous property could be used to make the makeUppercase()
function an uppercase-only new value:
exports.makeUppercase = functions.database.ref('/messages/{pushId}/original')
.onWrite(event => {
// Only edit data when it is first created.
if (event.data.previous.exists()) {
return;
}
// Exit when the data is deleted.
if (!event.data.exists()) {
return;
}
// Grab the current value of what was written to the Realtime Database.
const original = event.data.val();
console.log('Uppercasing', event.params.pushId, original);
const uppercase = original.toUpperCase();
// You must return a Promise when performing asynchronous tasks inside a Functions such as
// writing to the Firebase Realtime Database.
// Setting an "uppercase" sibling in the Realtime Database returns a Promise.
return event.data.ref.parent.child('uppercase').set(uppercase);
});
Monitoring changed values
Sometimes you don't need the old value; you just need to know whether
data has changed. You can see if data changed at a path using the
changed()
function on DeltaSnapshot. This function only calls
createThumbnail() if a change to a user profile also changed the value of
profilePicture:
exports.thumbnailProfile = functions.database.ref('/profiles/{userID}')
.onWrite(event => {
var eventSnapshot = event.data;
var profilePictureSnapshot = eventSnapshot.child('profilePicture');
if (profilePictureSnapshot.changed()) {
return createThumbnail(profilePictureSnapshot.val())
.then(url => {
return eventSnapshot.ref.update({ profileThumbnail: url });
});
}
});

