Firebase is back at Google I/O on May 10! Register now

Realtime Database triggers

Stay organized with collections Save and categorize content based on your preferences.

In a typical lifecycle, a Firebase Realtime Database function does the following:

  1. Waits for changes to a particular Realtime Database path.
  2. Triggers when an event occurs and performs its tasks.
  3. Receives a data object that contains a snapshot of the data stored at that path.

You can trigger a function in response to the writing, creating, updating, or deleting of database nodes in Firebase Realtime Database.

Trigger a function on Firebase Realtime Database changes

Use the firebase-functions/v2/database subpackage to create a function that handles Firebase Realtime Database events. To control when the function triggers, specify one of the event handlers, and specify the Realtime Database path where it will listen for events.

Setting the function location

Distance between the location of a Realtime Database instance and the location of the function can create significant network latency. Also, a mismatch between regions can result in deployment failure. To avoid these situations, specify the function location so that it matches the database instance location.

Handling Realtime Database events

Functions let you handle Realtime Database events at two levels of specificity; you can listen for specifically for only write, creation, update, or deletion events, or you can listen for any change of any kind to a reference.

These handlers for responding to Realtime Database events are available:

  • onValueWritten() Only triggered when data is written in Realtime Database.
  • onValueCreated() Only triggered when data is created in Realtime Database.
  • onValueUpdated() Only triggered when data is updated in Realtime Database.
  • onValueDeleted() Only triggered when data is deleted in Realtime Database.

Specify instance and path

To control when and where your function should trigger, configure your function with a path and optionally a Realtime Database instance. If you do not specify an instance, the function deploys to the all Realtime Database instances in the function region. You may also specify a Realtime Database instance pattern to deploy to a selective subset of instances in the same region.

For example, using onValueWritten() for illustration:

# All Realtime Database instances in default function region us-central1 at path "/user/{uid}"
# There must be at least one Realtime Database present in us-central1.
const onwrittenfunctiondefault = onValueWritten("/user/{uid}", (event) => {
  // …
});

# Instance named "my-app-db-2", at path "/user/{uid}".
# The "my-app-db-2" instance must exist in this region.
const onwrittenfunctioninstance = onValueWritten(
  {
    ref: "/user/{uid}",
    instance: "my-app-db-2"
    // This example assumes us-central1, but to set location:
    // region: "europe-west1"
  },
  (event) => {
    // …
  }
);

# Instance with "my-app-db-" prefix, at path "/user/{uid}", where uid ends with @gmail.com.
# There must be at least one Realtime Database with "my-app-db-*" prefix in this region.
const onwrittenfunctioninstance = onValueWritten(
  {
    ref: "/user/{uid=*@gmail.com}",
    instance: "my-app-db-*"
    // This example assumes us-central1, but to set location:
    // region: "europe-west1"
  },
  (event) => {
    // …
  }
);

These parameters direct your function to handle writes at a certain path within the Realtime Database instance.

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 events 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.

Wildcarding and capturing

You can use {key}, {key=*}, {key=prefix*}, {key=*suffix} for capturing. *, prefix*, *suffix for single-segment wildcarding. Note: ** represents multi-segment wildcarding, which RTDB does not support. See Understand path patterns.

Path wildcarding. You can specify a path component as a wildcard:

  • Using asterisk, *. For example, foo/* matches any children one level of the node hierarchy below foo/.
  • Using a segment containing exactly asterisk, *. For example, foo/app*-us matches any child segments below foo/ with app prefix and -us suffix.

Paths with wildcards can match multiple events from, for example, a single write. An insert of

{
  "foo": {
    "hello": "world",
    "firebase": "functions"
  }
}

matches the path "/foo/*" twice: once with "hello": "world" and again with "firebase": "functions".

Path capturing. You can capture path matches into named variables to be used in your function code (e.g. /user/{uid}, /user/{uid=*-us}).

The values of the capture variables are available within the database.DatabaseEvent.params object of your function.

Instance wildcarding. You can also specify an instance component using wildcarding. A instance wildcard can have prefix, suffix or both (e.g. my-app-*-prod).

Wildcard and capture reference

With Cloud Functions (2nd gen) and Realtime Database, a pattern can be used when specifying ref and instance. Each trigger interface will have the following options for scoping a function:

Specifying ref Specifying instance Behavior
Single (/foo/bar) Not specifying Scopes handler to all instances in the function region.
Single (/foo/bar) Single (‘my-new-db') Scopes handler to the specific instance in the function region.
Single (/foo/bar) Pattern (‘inst-prefix*') Scopes handler to all instances that match the pattern in the function region.
Pattern (/foo/{bar}) Not specifying Scopes handler to all instances in the function region.
Pattern (/foo/{bar}) Single (‘my-new-db') Scopes handler to the specific instance in the function region.
Pattern (/foo/{bar}) Pattern (‘inst-prefix*') Scopes handler to all instances that match the pattern in the function region.

Handle event data

When handling a Realtime Database event, the data object returned is a DataSnapshot.

For onValueWritten or onValueUpdated events, the first parameter is a Change object that contains two snapshots that represent the data state before and after the triggering event.

For onValueCreated and onValueDeleted events, the data object returned is a snapshot of the data created or deleted.

In this example, the function retrieves the snapshot for the specified path foo/bar as snap, converts the string at that location to uppercase, and writes that modified string to the database:

// Listens for new messages added to /messages/:pushId/original and creates an
// uppercase version of the message to /messages/:pushId/uppercase
export makeuppercase = onValueCreated("foo/bar", (event) => {
      // Grab the current value of what was written to the Realtime Database.
      const original = event.data.val();
      functions.logger.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 Change object has a beforeproperty that lets you inspect what was saved to Realtime Database before the event. The before property returns a DataSnapshot 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 DataSnapshot or reading the after property. This property on any Change is another DataSnapshot representing the state of the data after the event happened.

For example, the before property can be used to make sure the function only uppercases text when it is first created:

    exports makeuppercase = onValueWritten("/messages/{pushId}/original", (event) => {
          // Only edit data when it is first created.
          if (event.data.before.exists()) {
            return null;
          }
          // Exit when the data is deleted.
          if (!event.data.after.exists()) {
            return null;
          }
          // Grab the current value of what was written to the Realtime Database.
          const original = event.data.after.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.after.ref.parent.child('uppercase').set(uppercase);
        });