Extending Cloud Firestore with Cloud Functions

With Cloud Functions, you can deploy Node.js code to handle events triggered by changes in your Cloud Firestore database. This allows you to easily add server-side functionality into your app without running your own servers.

For examples of use cases, see What Can I Do with Cloud Functions? or the Functions Samples GitHub repository.

Trigger a Cloud Firestore function

The Cloud Functions for Firebase SDK exports a functions.firestore object that allows you to create handlers tied to specific events.

Cloud Firestore supports create, update, delete, and write events:

Event Type Trigger
onCreate Triggered when a document is written to for the first time.
onUpdate Triggered when a document already exists and has any value changed.
onDelete Triggered when a document with data is deleted.
onWrite Triggered when onCreate, onUpdate or onDelete is triggered.

If you don't have a project enabled for Cloud Functions for Firebase yet, then read Get Started: Write and Deploy Your First Functions to configure and set up your Cloud Functions for Firebase project.

Trigger a function when a specific document changes

If you want to trigger an event for any change to a specific document then you can use the following function.

Node.js

// Listen for any change on document `marie` in collection `users`
exports.myFunctionName = functions.firestore
    .document('users/marie').onWrite((change, context) => {
      // ... Your code here
    });

Trigger a function when a new document is created

You can trigger a function to fire any time a new document is created in a collection by using an onCreate() handler with a wildcard. This example function calls createUser every time a new user profile is added:

Node.js

exports.createUser = functions.firestore
    .document('users/{userId}')
    .onCreate((snap, context) => {
      // Get an object representing the document
      // e.g. {'name': 'Marie', 'age': 66}
      const newValue = snap.data();

      // access a particular field as you would any JS property
      const name = newValue.name;

      // perform desired operations ...
    });

Trigger a function when a document is updated

You can also trigger a function to fire when a document is updated using the onUpdate() function with a wildcard. This example function calls updateUser if a user changes their profile:

Node.js

exports.updateUser = functions.firestore
    .document('users/{userId}')
    .onUpdate((change, context) => {
      // Get an object representing the document
      // e.g. {'name': 'Marie', 'age': 66}
      const newValue = change.after.data();

      // ...or the previous value before this update
      const previousValue = change.before.data();

      // access a particular field as you would any JS property
      const name = newValue.name;

      // perform desired operations ...
    });

Trigger a function when a document is deleted

You can also trigger a function when a document is deleted using the onDelete() function with a wildcard. This example function calls deleteUser when a user deletes their user profile:

Node.js

exports.deleteUser = functions.firestore
    .document('users/{userID}')
    .onDelete((snap, context) => {
      // Get an object representing the document prior to deletion
      // e.g. {'name': 'Marie', 'age': 66}
      const deletedValue = snap.data();

      // perform desired operations ...
    });

Trigger a function for any change to a document

If you don't care about the type of event being fired, you can listen for all changes in a Cloud Firestore document using the onWrite() function with a wildcard This example function calls modifyUser if a user is created, updated, or deleted:

Node.js

exports.modifyUser = functions.firestore
    .document('users/{userID}')
    .onWrite((change, context) => {
      // Get an object with the current document value.
      // If the document does not exist, it has been deleted.
      const document = change.after.exists ? change.after.data() : null;

      // Get an object with the previous document value (for update or delete)
      const oldDocument = change.before.data();

      // perform desired operations ...
    });

Handle Event Data

Reading Data

When a function is triggered, you might want to get data from a document that was updated, or get the data prior to update. You can get the prior data by using change.before.data(), which contains the document snapshot before the update. Similarly, change.after.data() contains the document snapshot state after the update.

Node.js

exports.updateUser = functions.firestore
    .document('users/{userId}')
    .onUpdate((change, context) => {
      // Get an object representing the current document
      const newValue = change.after.data();

      // ...or the previous value before this update
      const previousValue = change.before.data();
    });

You can access properties as you would in any other object. Alternatively, you can use the get function to access specific fields:

Node.js

// Fetch data using standard accessors
const age = snap.data().age;
const name = snap.data()['name'];

// Fetch data using built in accessor
const experience = snap.get('experience');

Writing Data

Each function invocation is associated with a specific document in your Cloud Firestore database. You can access that document as a DocumentReference in the ref property of the snapshot returned to your function.

This DocumentReference comes from the Cloud Firestore Node.js SDK and includes methods like update(), set(), and remove() so you can easily modify the document that triggered the function.

Node.js

// Listen for updates to any `user` document.
exports.countNameChanges = functions.firestore
    .document('users/{userId}')
    .onUpdate((change, context) => {
      // Retrieve the current and previous value
      const data = change.after.data();
      const previousData = change.before.data();

      // We'll only update if the name has changed.
      // This is crucial to prevent infinite loops.
      if (data.name == previousData.name) return null;

      // Retrieve the current count of name changes
      let count = data.name_change_count;
      if (!count) {
        count = 0;
      }

      // Then return a promise of a set operation to update the count
      return change.after.ref.set({
        name_change_count: count + 1
      }, {merge: true});
    });

Using wildcards and parameters

If you do not know the specific document you want to attach an event trigger to, then you can use a {wildcard} in place of the document ID.

Node.js

// Listen for changes in all documents in the 'users' collection
exports.useWildcard = functions.firestore
    .document('users/{userId}')
    .onWrite((change, context) => {
      // If we set `/users/marie` to {name: "marie"} then
      // context.params.userId == "marie"
      // ... and ...
      // change.after.data() == {name: "Marie"}
    });

In this example, when any field on any document in users is changed, it matches a wildcard called userId.

If a document in users has subcollections, and a field in one of those subcollections' documents is changed, the userId wildcard is not triggered.

Wildcard matches are extracted from the document path and stored into event.params. You may define as many wildcards as you like to substitute explicit collection or document IDs.

Node.js

// Listen for changes in all documents in the 'users' collection and all subcollections
exports.useMultipleWildcards = functions.firestore
    .document('users/{userId}/{messageCollectionId}/{messageId}')
    .onWrite((change, context) => {
      // If we set `/users/marie/incoming_messages/134` to {body: "Hello"} then
      // context.params.userId == "marie";
      // context.params.messageCollectionId == "incoming_messages";
      // context.params.messageId == "134";
      // ... and ...
      // change.after.data() == {body: "Hello"}
    });

Limitations and guarantees

While developing your applications, keep in mind Cloud Firestore is currently in beta which may result in unexpected behavior.

A few known limitations include:

  • It may take up to 10 seconds for a function to be triggered after a change to Cloud Firestore data
  • As with all background functions, event ordering is not guaranteed. In addition, a single event may result in multiple Cloud Functions invocations, so for the highest quality ensure your functions are written to be idempotent.
Was this page helpful? Let us know how we did:

Send feedback about...

Cloud Firestore