---
title: "Cloud Functions Realtime Database Triggers Are Now More Efficient"
description: "Cloud Functions Realtime Database Triggers Are Now More Efficient"
published: "2017-07-10"
last_updated: "2017-07-10"
authors:
  - "Doug Stevenson (Developer Advocate)"
categories:
  - "Cloud Functions"
  - "Realtime Database"
  - "Cloud Next"
canonical_url: "https://firebase.blog/posts/2017/07/cloud-functions-realtime-database"
---

# Cloud Functions Realtime Database Triggers Are Now More Efficient

*Published July 10, 2017 by Doug Stevenson (Developer Advocate)*

---
One of my favorite parts about working with the Cloud Functions for Firebase team is helping developers move logic from their mobile apps to a fully managed backend hosted by Firebase. With just a few lines of JavaScript code, they're able to unify logic that automatically takes action on changes to the contents of their Realtime Database. It's really fun to see what people build!

When [Cloud Functions](https://firebase.google.com/docs/functions/) was first announced at Cloud Next 2017, there was just one trigger available for all types of changes to the database. This trigger is specified using the `onWrite()` callback, and it was the code author's responsibility to figure out what sort of change occurred. For example, imagine your app has a chat room, and you want to use Firebase Cloud Messaging to send a notification to users in that room when a new message arrives. To implement that, you might write some code that looks like this:

```js
exports.sendNotification = functions.database
        .ref("/messages/{messageId}").onWrite(event => {
    const dsnap = event.data  // a DeltaSnapshot that describes the write
    if (dsnap.exists() && !dsnap.previous.exists()) {
        // This is a new message, not a change or a delete.
        // Send notifications with FCM...
    }
})
```

Note that you have to check the [DeltaSnapshot](https://firebase.google.com/docs/reference/functions/functions.database.DeltaSnapshot) from the event object to see if new data now exists at the location of the write, and also if there was no prior data at that location. Why is this necessary? Because `onWrite()` will trigger on _all_ changes to data under the matched location, including new messages, updated messages, and deleted messages. However, this function is only interested in _new_ messages, so it has to make sure the write is not an update or a delete. If there was an update or delete, this function would still get triggered and return immediately. This extra execution costs money, and we know you'd rather not be billed for a function that doesn't do useful work.

Today, it gets better
=====================

The good news is that these extra checks are no longer necessary with Cloud Functions database triggers! Starting with the firebase-functions module version 0.5.9, there are now three new types of database triggers you can write: `onCreate()`, `onUpdate()`, and `onDelete()`. These triggers are aware of the type of change that was made, and only run in response to the type of change you desire. So, now you can write the above function like this:

```js
exports.sendNotification = functions.database
        .ref("/messages/{messageId}").onCreate(event => {
    // Send notifications with FCM...
})
```

Here, you don't have to worry about this function getting triggered again for any subsequent updates to the data at the same location. Not only is this easier to read, it costs you less to operate.

Note that `onWrite()` hasn't gone away. You can still keep using it with your functions for as long as you like.

Avoid infinite loops with `onWrite()` and `onUpdate()`
======================================================

If you've previously used `onWrite()` to add or change data at a location, you're aware that the changes you made to that data during `onWrite()` would trigger a second invocation of `onWrite()` (because _all_ writes count as writes, am I right?).

Consider this function that updates the lastUpdated property of a message after it's written:

```js
exports.lastUpdate = functions.database
        .ref("/messages/{messageId}").onWrite(event => {
    const msg = event.data.val()
    msg.lastUpdated = new Date().getTime()
    return event.data.adminRef.set(msg)
})
```

This seems OK at first, but there's something very important missing. Since this function writes back to the same location where it was triggered, it will effectively trigger _another_ call to `onWrite()`. You can see here that this will cause an infinite loop of writes, so it needs some way to bail out of the second invocation.

It turns out that `onUpdate()` function implementations share this concern. One solution in this specific case could be to check the existing value of lastUpdated and bail out if it's not more than 30 seconds older than the current date. So, if we want to rewrite this function using `onUpdate()`, it could look like this:

```js
exports.lastUpdate = functions.database
        .ref("/messages/{messageId}").onUpdate(event => {
    const msg = event.data.val()
    const now = new Date().getTime()
    if (msg.lastUpdated > now - (30*1000)) {
        return
    }
    msg.lastUpdated = now
    return event.data.adminRef.set(msg)
})
```

This function now defends against infinite loops with a little extra logic.

To start using these new triggers, be sure to update your firebase-functions module to 0.5.9. And for more information about these updates, check out the documentation [right here](https://firebase.google.com/docs/functions/database-events).
