Realtime Database triggers (1st gen)

With Cloud Functions , you can handle events in the Firebase Realtime Database with no need to update client code. Cloud Functions lets you run Realtime Database operations with full administrative privileges, and ensures that each change to Realtime Database is processed individually. You can make Firebase Realtime Database changes via the DataSnapshot or via the Admin SDK .

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

  1. Waits for changes to a particular Realtime Database location.
  2. Triggers when an event occurs and performs its tasks (see What can I do with Cloud Functions ? for examples of use cases).
  3. Receives a data object that contains a snapshot of the data stored in the specified document.

Trigger a Realtime Database function

Create new functions for Realtime Database events with functions.database . To control when the function triggers, specify one of the event handlers, and specify the Realtime Database path where it will listen for events.

Set the event handler

Functions let you handle Realtime Database events at two levels of specificity; you can listen for specifically for only creation, update, or deletion events, or you can listen for any change of any kind to a path. Cloud Functions supports these event handlers for Realtime Database :

  • onWrite() , which triggers when data is created, updated, or deleted in Realtime Database .
  • onCreate() , which triggers when new data is created in Realtime Database .
  • onUpdate() , which triggers when data is updated in Realtime Database .
  • onDelete() , which triggers when data is deleted from Realtime Database .

Specify the instance and path

To control when and where your function should trigger, call ref(path) to specify a path, and optionally specify a Realtime Database instance with instance('INSTANCE_NAME') . If you do not specify an instance, the function deploys to the default Realtime Database instance for the Firebase project For example:

  • Default Realtime Database instance: functions.database.ref('/foo/bar')
  • Instance named "my-app-db-2": functions.database.instance('my-app-db-2').ref('/foo/bar')

These methods 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.

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 EventContext.params object of your function. In this example, the value is available as context.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, the data object returned is a DataSnapshot . For onWrite or onUpdate events, the first parameter is a Change object that contains two snapshots that represent the data state before and after the triggering event. For onCreate and onDelete 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, 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 
 exports 
 . 
 makeUppercase 
  
 = 
  
 functions 
 . 
 database 
 . 
 ref 
 ( 
 '/messages/{pushId}/original' 
 ) 
  
 . 
 onCreate 
 (( 
 snapshot 
 , 
  
 context 
 ) 
  
 = 
>  
 { 
  
 // 
  
 Grab 
  
 the 
  
 current 
  
 value 
  
 of 
  
 what 
  
 was 
  
 written 
  
 to 
  
 the 
  
 Realtime 
  
 Database 
 . 
  
 const 
  
 original 
  
 = 
  
 snapshot 
 . 
 val 
 (); 
  
 functions 
 . 
 logger 
 . 
 log 
 ( 
 'Uppercasing' 
 , 
  
 context 
 . 
 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 
  
 snapshot 
 . 
 ref 
 . 
 parent 
 . 
 child 
 ( 
 'uppercase' 
 ) 
 . 
 set 
 ( 
 uppercase 
 ); 
  
 }); 
  

Accessing user authentication information

From EventContext.auth and EventContext.authType , you can access the user information, including permissions, for the user that triggered a function. This can be useful for enforcing security rules, allowing your function to complete different operations based on the user's level of permissions:

  const 
  
 functions 
  
 = 
  
 require 
 ( 
 'firebase-functions/v1' 
 ); 
 const 
  
 admin 
  
 = 
  
 require 
 ( 
 'firebase-admin' 
 ); 
 exports 
 . 
 simpleDbFunction 
  
 = 
  
 functions 
 . 
 database 
 . 
 ref 
 ( 
 '/path' 
 ) 
  
 . 
 onCreate 
 (( 
 snap 
 , 
  
 context 
 ) 
  
 = 
>  
 { 
  
 if 
  
 ( 
 context 
 . 
 authType 
  
 === 
  
 'ADMIN' 
 ) 
  
 { 
  
 // 
  
 do 
  
 something 
  
 } 
  
 else 
  
 if 
  
 ( 
 context 
 . 
 authType 
  
 === 
  
 'USER' 
 ) 
  
 { 
  
 console 
 . 
 log 
 ( 
 snap 
 . 
 val 
 (), 
  
 'written by' 
 , 
  
 context 
 . 
 auth 
 . 
 uid 
 ); 
  
 } 
  
 }); 
 

Also, you can leverage user authentication information to "impersonate" a user and perform write operations on the user's behalf. Make sure to delete the app instance as shown below in order to prevent concurrency issues:

  exports 
 . 
 impersonateMakeUpperCase 
  
 = 
  
 functions 
 . 
 database 
 . 
 ref 
 ( 
 '/messages/{pushId}/original' 
 ) 
  
 . 
 onCreate 
 (( 
 snap 
 , 
  
 context 
 ) 
  
 = 
>  
 { 
  
 const 
  
 appOptions 
  
 = 
  
 JSON 
 . 
 parse 
 ( 
 process 
 . 
 env 
 . 
 FIREBASE_CONFIG 
 ); 
  
 appOptions 
 . 
 databaseAuthVariableOverride 
  
 = 
  
 context 
 . 
 auth 
 ; 
  
 const 
  
 app 
  
 = 
  
 admin 
 . 
 initializeApp 
 ( 
 appOptions 
 , 
  
 'app' 
 ); 
  
 const 
  
 uppercase 
  
 = 
  
 snap 
 . 
 val 
 () 
 . 
 toUpperCase 
 (); 
  
 const 
  
 ref 
  
 = 
  
 snap 
 . 
 ref 
 . 
 parent 
 . 
 child 
 ( 
 'uppercase' 
 ); 
  
 const 
  
 deleteApp 
  
 = 
  
 () 
  
 = 
>  
 app 
 . 
 delete 
 () 
 . 
 catch 
 (() 
  
 = 
>  
 null 
 ); 
  
 return 
  
 app 
 . 
 database 
 () 
 . 
 ref 
 ( 
 ref 
 ) 
 . 
 set 
 ( 
 uppercase 
 ) 
 . 
 then 
 ( 
 res 
  
 = 
>  
 { 
  
 // 
  
 Deleting 
  
 the 
  
 app 
  
 is 
  
 necessary 
  
 for 
  
 preventing 
  
 concurrency 
  
 leaks 
  
 return 
  
 deleteApp 
 () 
 . 
 then 
 (() 
  
 = 
>  
 res 
 ); 
  
 }) 
 . 
 catch 
 ( 
 err 
  
 = 
>  
 { 
  
 return 
  
 deleteApp 
 () 
 . 
 then 
 (() 
  
 = 
>  
 Promise 
 . 
 reject 
 ( 
 err 
 )); 
  
 }); 
  
 }); 
 

Reading the previous value

The Change object has a before property 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 
  
 = 
  
 functions 
 . 
 database 
 . 
 ref 
 ( 
 '/messages/{pushId}/original' 
 ) 
  
 . 
 onWrite 
 (( 
 change 
 , 
  
 context 
 ) 
  
 = 
>  
 { 
  
 // 
  
 Only 
  
 edit 
  
 data 
  
 when 
  
 it 
  
 is 
  
 first 
  
 created 
 . 
  
 if 
  
 ( 
 change 
 . 
 before 
 . 
 exists 
 ()) 
  
 { 
  
 return 
  
 null 
 ; 
  
 } 
  
 // 
  
 Exit 
  
 when 
  
 the 
  
 data 
  
 is 
  
 deleted 
 . 
  
 if 
  
 ( 
 ! 
 change 
 . 
 after 
 . 
 exists 
 ()) 
  
 { 
  
 return 
  
 null 
 ; 
  
 } 
  
 // 
  
 Grab 
  
 the 
  
 current 
  
 value 
  
 of 
  
 what 
  
 was 
  
 written 
  
 to 
  
 the 
  
 Realtime 
  
 Database 
 . 
  
 const 
  
 original 
  
 = 
  
 change 
 . 
 after 
 . 
 val 
 (); 
  
 console 
 . 
 log 
 ( 
 'Uppercasing' 
 , 
  
 context 
 . 
 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 
  
 change 
 . 
 after 
 . 
 ref 
 . 
 parent 
 . 
 child 
 ( 
 'uppercase' 
 ) 
 . 
 set 
 ( 
 uppercase 
 ); 
  
 }); 
 
Create a Mobile Website
View Site in Mobile | Classic
Share by: