Trigger functions with Firestore documents

This guide shows examples of functions that are triggered when you make changes to a document inside of a specified collection.

Before you begin

Before you run the sample code in this guide, you'll need to do the following:

Examples

The following examples demonstrate how to write functions that respond to a Firestore trigger.

Example 1: Hello Firestore function

The following sample prints the fields of a triggering Firestore event:

Node.js

  /** 
 * Cloud Event Function triggered by a change to a Firestore document. 
 */ 
 const 
  
 functions 
  
 = 
  
 require 
 ( 
 '@google-cloud/functions-framework' 
 ); 
 const 
  
 protobuf 
  
 = 
  
 require 
 ( 
 'protobufjs' 
 ); 
 functions 
 . 
 cloudEvent 
 ( 
 'helloFirestore' 
 , 
  
 async 
  
 cloudEvent 
  
 = 
>  
 { 
  
 console 
 . 
 log 
 ( 
 `Function triggered by event on: 
 ${ 
 cloudEvent 
 . 
 source 
 } 
 ` 
 ); 
  
 console 
 . 
 log 
 ( 
 `Event type: 
 ${ 
 cloudEvent 
 . 
 type 
 } 
 ` 
 ); 
  
 console 
 . 
 log 
 ( 
 'Loading protos...' 
 ); 
  
 const 
  
 root 
  
 = 
  
 await 
  
 protobuf 
 . 
 load 
 ( 
 'data.proto' 
 ); 
  
 const 
  
 DocumentEventData 
  
 = 
  
 root 
 . 
 lookupType 
 ( 
  
 'google.events.cloud.firestore.v1.DocumentEventData' 
  
 ); 
  
 console 
 . 
 log 
 ( 
 'Decoding data...' 
 ); 
  
 const 
  
 firestoreReceived 
  
 = 
  
 DocumentEventData 
 . 
 decode 
 ( 
 cloudEvent 
 . 
 data 
 ); 
  
 console 
 . 
 log 
 ( 
 '\nOld value:' 
 ); 
  
 console 
 . 
 log 
 ( 
 JSON 
 . 
 stringify 
 ( 
 firestoreReceived 
 . 
 oldValue 
 , 
  
 null 
 , 
  
 2 
 )); 
  
 console 
 . 
 log 
 ( 
 '\nNew value:' 
 ); 
  
 console 
 . 
 log 
 ( 
 JSON 
 . 
 stringify 
 ( 
 firestoreReceived 
 . 
 value 
 , 
  
 null 
 , 
  
 2 
 )); 
 }); 
 

Python

  from 
  
 cloudevents.http 
  
 import 
 CloudEvent 
 import 
  
 functions_framework 
 from 
  
 google.events.cloud 
  
 import 
 firestore 
 @functions_framework 
 . 
 cloud_event 
 def 
  
 hello_firestore 
 ( 
 cloud_event 
 : 
 CloudEvent 
 ) 
 - 
> None 
 : 
  
 """Triggers by a change to a Firestore document. 
 Args: 
 cloud_event: cloud event with information on the firestore event trigger 
 """ 
 firestore_payload 
 = 
 firestore 
 . 
 DocumentEventData 
 () 
 firestore_payload 
 . 
 _pb 
 . 
 ParseFromString 
 ( 
 cloud_event 
 . 
 data 
 ) 
 print 
 ( 
 f 
 "Function triggered by change to: 
 { 
 cloud_event 
 [ 
 'source' 
 ] 
 } 
 " 
 ) 
 print 
 ( 
 " 
 \n 
 Old value:" 
 ) 
 print 
 ( 
 firestore_payload 
 . 
 old_value 
 ) 
 print 
 ( 
 " 
 \n 
 New value:" 
 ) 
 print 
 ( 
 firestore_payload 
 . 
 value 
 ) 
 

Go

  // Package hellofirestore contains a Cloud Event Function triggered by a Cloud Firestore event. 
 package 
  
 hellofirestore 
 import 
  
 ( 
  
 "context" 
  
 "fmt" 
  
 "github.com/GoogleCloudPlatform/functions-framework-go/functions" 
  
 "github.com/cloudevents/sdk-go/v2/event" 
  
 "github.com/googleapis/google-cloudevents-go/cloud/firestoredata" 
  
 "google.golang.org/protobuf/proto" 
 ) 
 func 
  
 init 
 () 
  
 { 
  
 functions 
 . 
 CloudEvent 
 ( 
 "helloFirestore" 
 , 
  
 HelloFirestore 
 ) 
 } 
 // HelloFirestore is triggered by a change to a Firestore document. 
 func 
  
 HelloFirestore 
 ( 
 ctx 
  
 context 
 . 
 Context 
 , 
  
 event 
  
 event 
 . 
 Event 
 ) 
  
 error 
  
 { 
  
 var 
  
 data 
  
 firestoredata 
 . 
 DocumentEventData 
  
 // If you omit `DiscardUnknown`, protojson.Unmarshal returns an error 
  
 // when encountering a new or unknown field. 
  
 options 
  
 := 
  
 proto 
 . 
 UnmarshalOptions 
 { 
  
 DiscardUnknown 
 : 
  
 true 
 , 
  
 } 
  
 err 
  
 := 
  
 options 
 . 
 Unmarshal 
 ( 
 event 
 . 
 Data 
 (), 
  
& data 
 ) 
  
 if 
  
 err 
  
 != 
  
 nil 
  
 { 
  
 return 
  
 fmt 
 . 
 Errorf 
 ( 
 "proto.Unmarshal: %w" 
 , 
  
 err 
 ) 
  
 } 
  
 fmt 
 . 
 Printf 
 ( 
 "Function triggered by change to: %v\n" 
 , 
  
 event 
 . 
 Source 
 ()) 
  
 fmt 
 . 
 Printf 
 ( 
 "Old value: %+v\n" 
 , 
  
 data 
 . 
 GetOldValue 
 ()) 
  
 fmt 
 . 
 Printf 
 ( 
 "New value: %+v\n" 
 , 
  
 data 
 . 
 GetValue 
 ()) 
  
 return 
  
 nil 
 } 
 

Java

  import 
  
 com.google.cloud.functions.CloudEventsFunction 
 ; 
 import 
  
 com.google.events.cloud.firestore.v1. DocumentEventData 
 
 ; 
 import 
  
 com.google.protobuf. InvalidProtocolBufferException 
 
 ; 
 import 
  
 io.cloudevents.CloudEvent 
 ; 
 import 
  
 java.util.logging.Logger 
 ; 
 public 
  
 class 
 FirebaseFirestore 
  
 implements 
  
 CloudEventsFunction 
  
 { 
  
 private 
  
 static 
  
 final 
  
 Logger 
  
 logger 
  
 = 
  
 Logger 
 . 
 getLogger 
 ( 
 FirebaseFirestore 
 . 
 class 
 . 
 getName 
 ()); 
  
 @Override 
  
 public 
  
 void 
  
 accept 
 ( 
 CloudEvent 
  
 event 
 ) 
  
 throws 
  
  InvalidProtocolBufferException 
 
  
 { 
  
  DocumentEventData 
 
  
 firestoreEventData 
  
 = 
  
  DocumentEventData 
 
  
 . 
 parseFrom 
 ( 
 event 
 . 
 getData 
 (). 
 toBytes 
 ()); 
  
 logger 
 . 
 info 
 ( 
 "Function triggered by event on: " 
  
 + 
  
 event 
 . 
 getSource 
 ()); 
  
 logger 
 . 
 info 
 ( 
 "Event type: " 
  
 + 
  
 event 
 . 
 getType 
 ()); 
  
 logger 
 . 
 info 
 ( 
 "Old value:" 
 ); 
  
 logger 
 . 
 info 
 ( 
 firestoreEventData 
 . 
  getOldValue 
 
 (). 
 toString 
 ()); 
  
 logger 
 . 
 info 
 ( 
 "New value:" 
 ); 
  
 logger 
 . 
 info 
 ( 
 firestoreEventData 
 . 
  getValue 
 
 (). 
 toString 
 ()); 
  
 } 
 } 
 

C#

  using 
  
 CloudNative.CloudEvents 
 ; 
 using 
  
 Google.Cloud.Functions.Framework 
 ; 
 using 
  
 Google.Events.Protobuf.Cloud.Firestore.V1 
 ; 
 using 
  
 Microsoft.Extensions.Logging 
 ; 
 using 
  
 System.Collections.Generic 
 ; 
 using 
  
 System.Linq 
 ; 
 using 
  
 System.Threading 
 ; 
 using 
  
 System.Threading.Tasks 
 ; 
 namespace 
  
 FirebaseFirestore 
 ; 
 public 
  
 class 
  
 Function 
  
 : 
  
 ICloudEventFunction<DocumentEventData> 
 { 
  
 private 
  
 readonly 
  
 ILogger 
  
 _logger 
 ; 
  
 public 
  
 Function 
 ( 
 ILogger<Function> 
  
 logger 
 ) 
  
 = 
>  
 _logger 
  
 = 
  
 logger 
 ; 
  
 public 
  
 Task 
  
 HandleAsync 
 ( 
 CloudEvent 
  
 cloudEvent 
 , 
  
 DocumentEventData 
  
 data 
 , 
  
 CancellationToken 
  
 cancellationToken 
 ) 
  
 { 
  
 _logger 
 . 
 LogInformation 
 ( 
 "Function triggered by event on {subject}" 
 , 
  
 cloudEvent 
 . 
 Subject 
 ); 
  
 _logger 
 . 
 LogInformation 
 ( 
 "Event type: {type}" 
 , 
  
 cloudEvent 
 . 
 Type 
 ); 
  
 MaybeLogDocument 
 ( 
 "Old value" 
 , 
  
 data 
 . 
 OldValue 
 ); 
  
 MaybeLogDocument 
 ( 
 "New value" 
 , 
  
 data 
 . 
 Value 
 ); 
  
 // In this example, we don't need to perform any asynchronous operations, so the 
  
 // method doesn't need to be declared async. 
  
 return 
  
 Task 
 . 
 CompletedTask 
 ; 
  
 } 
  
 /// <summary> 
  
 /// Logs the names and values of the fields in a document in a very simplistic way. 
  
 /// </summary> 
  
 private 
  
 void 
  
 MaybeLogDocument 
 ( 
 string 
  
 message 
 , 
  
 Document 
  
 document 
 ) 
  
 { 
  
 if 
  
 ( 
 document 
  
 is 
  
 null 
 ) 
  
 { 
  
 return 
 ; 
  
 } 
  
 // ConvertFields converts the Firestore representation into a .NET-friendly 
  
 // representation. 
  
 IReadOnlyDictionary<string 
 , 
  
 object 
>  
 fields 
  
 = 
  
 document 
 . 
 ConvertFields 
 (); 
  
 var 
  
 fieldNamesAndTypes 
  
 = 
  
 fields 
  
 . 
 OrderBy 
 ( 
 pair 
  
 = 
>  
 pair 
 . 
 Key 
 ) 
  
 . 
 Select 
 ( 
 pair 
  
 = 
>  
 $"{pair.Key}: {pair.Value}" 
 ); 
  
 _logger 
 . 
 LogInformation 
 ( 
 message 
  
 + 
  
 ": {fields}" 
 , 
  
 string 
 . 
 Join 
 ( 
 ", " 
 , 
  
 fieldNamesAndTypes 
 )); 
  
 } 
 } 
 

Deploy the Hello Firestore function

If you haven't already done so, set up your Firestore database .

Click the tab for instructions using the tool of your choice.

Console

When you use the Google Cloud console to create a function, you can also add a trigger to your function. Follow these steps to create a trigger for your function:

  1. In the Google Cloud console, go to Cloud Run:

    Go to Cloud Run

  2. Click Write a function, and enter the function details. For more information about configuring functions during deployment, see Deploy functions .

  3. In the Triggersection, click Add trigger.

  4. Select Firestore trigger .

  5. In the Eventarc triggerpane, modify the trigger details as follows:

    1. Enter a name for the trigger in the Trigger namefield, or use the default name.

    2. Select a Trigger typefrom the list:

      • Google Sourcesto specify triggers for Pub/Sub, Cloud Storage, Firestore, and other Google event providers.

      • Third-partyto integrate with non-Google providers that offer an Eventarc source. For more information, see Third-party events in Eventarc .

    3. Select Firestore from the Event providerlist, to select a product that provides the type of event for triggering your function. For the list of event providers, see Event providers and destinations .

    4. Select type=google.cloud.firestore.document.v1.written from the Event typelist. Your trigger configuration varies depending on the supported event type. For more information, see Event types .

    5. In the Filters section, select a database, operation and attribute values, or use the default selections.

    6. If the Regionfield is enabled, select a location for the Eventarc trigger. In general, the location of an Eventarc trigger should match the location of the Google Cloud resource that you want to monitor for events. In most scenarios, you should also deploy your function in the same region. See Understand Eventarc locations for more details about Eventarc trigger locations.

    7. In the Service accountfield, select a service account. Eventarc triggers are linked to service accounts to use as an identity when invoking your function. Your Eventarc trigger's service account must have the permission to invoke your function. By default, Cloud Run uses the Compute Engine default service account .

    8. Optionally, specify the Service URL pathto send the incoming request to. This is the relative path on the destination service to which the events for the trigger should be sent. For example: / , /route , route , and route/subroute .

  6. Once you've completed the required fields, click Save trigger.

gcloud

When you create a function using the gcloud CLI, you must first deploy your function, and then create a trigger. Follow these steps to create a trigger for your function:

  1. Run the following command in the directory that contains the sample code to deploy your function:

     gcloud run deploy FUNCTION 
    \
            --source . \
            --function FUNCTION_ENTRYPOINT 
    \
            --base-image BASE_IMAGE_ID 
    \
            --region REGION 
     
    

    Replace:

    • FUNCTION with the name of the function you are deploying. You can omit this parameter entirely, but you will be prompted for the name if you omit it.

    • FUNCTION_ENTRYPOINT with the entry point to your function in your source code. This is the code Cloud Run executes when your function runs. The value of this flag must be a function name or fully-qualified class name that exists in your source code.

    • BASE_IMAGE_ID with the base image environment for your function. For more details about base images and the packages included in each image, see Runtimes base images .

    • REGION with the Google Cloud region where you want to deploy your function. For example, europe-west1 .

  2. Run the following command to create a trigger that filters events:

     gcloud eventarc triggers create TRIGGER_NAME 
    \
        --location= EVENTARC_TRIGGER_LOCATION 
    \
        --destination-run-service= FUNCTION 
    \
        --destination-run-region= REGION 
    \
        --event-filters=type=google.cloud.firestore.document.v1.written \
        --event-filters=database='(default)' \
        --event-data-content-type=application/protobuf \
        --event-filters-path-pattern=document='users/{username}' \
        --service-account= PROJECT_NUMBER 
    -compute@developer.gserviceaccount.com 
    

    Replace:

    • TRIGGER_NAME with the name for your trigger.

    • EVENTARC_TRIGGER_LOCATION with the location for the Eventarc trigger. In general, the location of an Eventarc trigger should match the location of the Google Cloud resource that you want to monitor for events. In most scenarios, you should also deploy your function in the same region. For more information, see Eventarc locations .

    • FUNCTION with the name of the function you are deploying.

    • REGION with the Cloud Run region of the function.

    • PROJECT_NUMBER with your Google Cloud project number. Eventarc triggers are linked to service accounts to use as an identity when invoking your function. Your Eventarc trigger's service account must have the permission to invoke your function. By default, Cloud Run uses the Default compute service account.

    Each event-filters flag specifies a type of event, with the function triggering only when an event meets all of the criteria specified in its event-filters flags. Each trigger must have an event-filters flag specifying a supported event type , such as a new document written to Firestore or a file uploaded to Cloud Storage. You can't change the event filter type after creation. To change the event filter type, you must create a new trigger and delete the old one. Optionally, you can repeat the --event-filters flag with a supported filter in the form ATTRIBUTE=VALUE to add more filters.

Terraform

To create an Eventarc trigger for a Cloud Run function, see Create a trigger using Terraform .

Use the other fields as is:

  • --event-filters=type=google.cloud.firestore.document.v1.written specifies that the function is triggered when a document is created, updated or deleted, per the google.cloud.firestore.document.v1.written event type .
  • --event-filters=database='(default)' specifies the Firebase database. For the default database name, use (default) .
  • --event-filters-path-pattern=document='users/{username}' provides the path pattern of the documents that should be monitored for relevant changes. This path pattern states that all documents in the users collection should be monitored. For more information, see Understand path patterns .

Test the Hello Firestore function

To test the Hello Firestore function, set up a collection called users in your Firestore database :

  1. In the Google Cloud console, go to the Firestore databases page:

    Go to Firestore

  2. Click Start a collection.

  3. Specify users as the collection ID.

  4. To start adding the collection's first document, under Add its first documentaccept the auto-generated Document ID.

  5. Add at least one field for the document, specifying a name and value. For example, in Field name, enter username , and in Field value, enter rowan .

  6. When you're done, click Save.

    This action creates a new document, thereby triggering your function.

  7. To confirm that your function was triggered, click the linked name of the function in the Google Cloud console Cloud Run Overview page to open the Service detailspage.

  8. Select the Logstab and look for this string:

Function triggered by change to: //firestore.googleapis.com/projects/ your-project-id 
/databases/(default)'

Example 2: Convert to Uppercase function

The following example retrieves the value added by the user, converts the string at that location to uppercase, and replaces the value with the uppercase string:

Node.js

Use protobufjs to decode the event data. Include the google.events.cloud.firestore.v1 data.proto in your source.

  const 
  
 functions 
  
 = 
  
 require 
 ( 
 '@google-cloud/functions-framework' 
 ); 
 const 
  
 Firestore 
  
 = 
  
 require 
 ( 
 ' @google-cloud/firestore 
' 
 ); 
 const 
  
 protobuf 
  
 = 
  
 require 
 ( 
 'protobufjs' 
 ); 
 const 
  
 firestore 
  
 = 
  
 new 
  
  Firestore 
 
 ({ 
  
 projectId 
 : 
  
 process 
 . 
 env 
 . 
 GOOGLE_CLOUD_PROJECT 
 , 
 }); 
 // Converts strings added to /messages/{pushId}/original to uppercase 
 functions 
 . 
 cloudEvent 
 ( 
 'makeUpperCase' 
 , 
  
 async 
  
 cloudEvent 
  
 = 
>  
 { 
  
 console 
 . 
 log 
 ( 
 'Loading protos...' 
 ); 
  
 const 
  
 root 
  
 = 
  
 await 
  
 protobuf 
 . 
 load 
 ( 
 'data.proto' 
 ); 
  
 const 
  
 DocumentEventData 
  
 = 
  
 root 
 . 
 lookupType 
 ( 
  
 'google.events.cloud.firestore.v1.DocumentEventData' 
  
 ); 
  
 console 
 . 
 log 
 ( 
 'Decoding data...' 
 ); 
  
 const 
  
 firestoreReceived 
  
 = 
  
 DocumentEventData 
 . 
 decode 
 ( 
 cloudEvent 
 . 
 data 
 ); 
  
 const 
  
 resource 
  
 = 
  
 firestoreReceived 
 . 
 value 
 . 
 name 
 ; 
  
 const 
  
 affectedDoc 
  
 = 
  
 firestore 
 . 
 doc 
 ( 
 resource 
 . 
 split 
 ( 
 '/documents/' 
 )[ 
 1 
 ]); 
  
 const 
  
 curValue 
  
 = 
  
 firestoreReceived 
 . 
 value 
 . 
 fields 
 . 
 original 
 . 
 stringValue 
 ; 
  
 const 
  
 newValue 
  
 = 
  
 curValue 
 . 
 toUpperCase 
 (); 
  
 if 
  
 ( 
 curValue 
  
 === 
  
 newValue 
 ) 
  
 { 
  
 // Value is already upper-case 
  
 // Don't perform a(nother) write to avoid infinite loops 
  
 console 
 . 
 log 
 ( 
 'Value is already upper-case.' 
 ); 
  
 return 
 ; 
  
 } 
  
 console 
 . 
 log 
 ( 
 `Replacing value: 
 ${ 
 curValue 
 } 
 --> 
 ${ 
 newValue 
 } 
 ` 
 ); 
  
 affectedDoc 
 . 
 set 
 ({ 
  
 original 
 : 
  
 newValue 
 , 
  
 }); 
 }); 
 

Python

  from 
  
 cloudevents.http 
  
 import 
 CloudEvent 
 import 
  
 functions_framework 
 from 
  
 google.cloud 
  
 import 
 firestore 
 from 
  
 google.events.cloud 
  
 import 
 firestore 
 as 
 firestoredata 
 client 
 = 
 firestore 
 . 
  Client 
 
 () 
 # Converts strings added to /messages/{pushId}/original to uppercase 
 @functions_framework 
 . 
 cloud_event 
 def 
  
 make_upper_case 
 ( 
 cloud_event 
 : 
 CloudEvent 
 ) 
 - 
> None 
 : 
 firestore_payload 
 = 
 firestoredata 
 . 
 DocumentEventData 
 () 
 firestore_payload 
 . 
 _pb 
 . 
 ParseFromString 
 ( 
 cloud_event 
 . 
 data 
 ) 
 path_parts 
 = 
 firestore_payload 
 . 
 value 
 . 
 name 
 . 
 split 
 ( 
 "/" 
 ) 
 separator_idx 
 = 
 path_parts 
 . 
 index 
 ( 
 "documents" 
 ) 
 collection_path 
 = 
 path_parts 
 [ 
 separator_idx 
 + 
 1 
 ] 
 document_path 
 = 
 "/" 
 . 
 join 
 ( 
 path_parts 
 [( 
 separator_idx 
 + 
 2 
 ) 
 :]) 
 print 
 ( 
 f 
 "Collection path: 
 { 
 collection_path 
 } 
 " 
 ) 
 print 
 ( 
 f 
 "Document path: 
 { 
 document_path 
 } 
 " 
 ) 
 affected_doc 
 = 
 client 
 . 
  collection 
 
 ( 
 collection_path 
 ) 
 . 
 document 
 ( 
 document_path 
 ) 
 cur_value 
 = 
 firestore_payload 
 . 
 value 
 . 
 fields 
 [ 
 "original" 
 ] 
 . 
 string_value 
 new_value 
 = 
 cur_value 
 . 
 upper 
 () 
 if 
 cur_value 
 != 
 new_value 
 : 
 print 
 ( 
 f 
 "Replacing value: 
 { 
 cur_value 
 } 
 --> 
 { 
 new_value 
 } 
 " 
 ) 
 affected_doc 
 . 
 set 
 ({ 
 "original" 
 : 
 new_value 
 }) 
 else 
 : 
 # Value is already upper-case 
 # Don't perform a second write (which can trigger an infinite loop) 
 print 
 ( 
 "Value is already upper-case." 
 ) 
 

Go

  // Package upper contains a Firestore Cloud Function. 
 package 
  
 upper 
 import 
  
 ( 
  
 "context" 
  
 "errors" 
  
 "fmt" 
  
 "log" 
  
 "os" 
  
 "strings" 
  
 "cloud.google.com/go/firestore" 
  
 firebase 
  
 "firebase.google.com/go/v4" 
  
 "github.com/GoogleCloudPlatform/functions-framework-go/functions" 
  
 "github.com/cloudevents/sdk-go/v2/event" 
  
 "github.com/googleapis/google-cloudevents-go/cloud/firestoredata" 
  
 "google.golang.org/protobuf/proto" 
 ) 
 // set the GOOGLE_CLOUD_PROJECT environment variable when deploying. 
 var 
  
 projectID 
  
 = 
  
 os 
 . 
 Getenv 
 ( 
 "GOOGLE_CLOUD_PROJECT" 
 ) 
 // client is a Firestore client, reused between function invocations. 
 var 
  
 client 
  
 * 
 firestore 
 . 
 Client 
 func 
  
 init 
 () 
  
 { 
  
 // Use the application default credentials. 
  
 conf 
  
 := 
  
& firebase 
 . 
 Config 
 { 
 ProjectID 
 : 
  
 projectID 
 } 
  
 // Use context.Background() because the app/client should persist across 
  
 // invocations. 
  
 ctx 
  
 := 
  
 context 
 . 
 Background 
 () 
  
 app 
 , 
  
 err 
  
 := 
  
 firebase 
 . 
 NewApp 
 ( 
 ctx 
 , 
  
 conf 
 ) 
  
 if 
  
 err 
  
 != 
  
 nil 
  
 { 
  
 log 
 . 
 Fatalf 
 ( 
 "firebase.NewApp: %v" 
 , 
  
 err 
 ) 
  
 } 
  
 client 
 , 
  
 err 
  
 = 
  
 app 
 . 
 Firestore 
 ( 
 ctx 
 ) 
  
 if 
  
 err 
  
 != 
  
 nil 
  
 { 
  
 log 
 . 
 Fatalf 
 ( 
 "app.Firestore: %v" 
 , 
  
 err 
 ) 
  
 } 
  
 // Register cloud event function 
  
 functions 
 . 
 CloudEvent 
 ( 
 "MakeUpperCase" 
 , 
  
 MakeUpperCase 
 ) 
 } 
 // MakeUpperCase is triggered by a change to a Firestore document. It updates 
 // the `original` value of the document to upper case. 
 func 
  
 MakeUpperCase 
 ( 
 ctx 
  
 context 
 . 
 Context 
 , 
  
 e 
  
 event 
 . 
 Event 
 ) 
  
 error 
  
 { 
  
 var 
  
 data 
  
 firestoredata 
 . 
 DocumentEventData 
  
 // If you omit `DiscardUnknown`, protojson.Unmarshal returns an error 
  
 // when encountering a new or unknown field. 
  
 options 
  
 := 
  
 proto 
 . 
 UnmarshalOptions 
 { 
  
 DiscardUnknown 
 : 
  
 true 
 , 
  
 } 
  
 err 
  
 := 
  
 options 
 . 
 Unmarshal 
 ( 
 e 
 . 
  Data 
 
 (), 
  
& data 
 ) 
  
 if 
  
 err 
  
 != 
  
 nil 
  
 { 
  
 return 
  
 fmt 
 . 
 Errorf 
 ( 
 "proto.Unmarshal: %w" 
 , 
  
 err 
 ) 
  
 } 
  
 if 
  
 data 
 . 
  GetValue 
 
 () 
  
 == 
  
 nil 
  
 { 
  
 return 
  
 errors 
 . 
 New 
 ( 
 "Invalid message: 'Value' not present" 
 ) 
  
 } 
  
 fullPath 
  
 := 
  
 strings 
 . 
 Split 
 ( 
 data 
 . 
  GetValue 
 
 (). 
 GetName 
 (), 
  
 "/documents/" 
 )[ 
 1 
 ] 
  
 pathParts 
  
 := 
  
 strings 
 . 
 Split 
 ( 
 fullPath 
 , 
  
 "/" 
 ) 
  
 collection 
  
 := 
  
 pathParts 
 [ 
 0 
 ] 
  
 doc 
  
 := 
  
 strings 
 . 
 Join 
 ( 
 pathParts 
 [ 
 1 
 :], 
  
 "/" 
 ) 
  
 var 
  
 originalStringValue 
  
 string 
  
 if 
  
 v 
 , 
  
 ok 
  
 := 
  
 data 
 . 
  GetValue 
 
 (). 
 GetFields 
 ()[ 
 "original" 
 ]; 
  
 ok 
  
 { 
  
 originalStringValue 
  
 = 
  
 v 
 . 
  GetStringValue 
 
 () 
  
 } 
  
 else 
  
 { 
  
 return 
  
 errors 
 . 
 New 
 ( 
 "Document did not contain field \"original\"" 
 ) 
  
 } 
  
 newValue 
  
 := 
  
 strings 
 . 
 ToUpper 
 ( 
 originalStringValue 
 ) 
  
 if 
  
 originalStringValue 
  
 == 
  
 newValue 
  
 { 
  
 log 
 . 
 Printf 
 ( 
 "%q is already upper case: skipping" 
 , 
  
 originalStringValue 
 ) 
  
 return 
  
 nil 
  
 } 
  
 log 
 . 
 Printf 
 ( 
 "Replacing value: %q -> %q" 
 , 
  
 originalStringValue 
 , 
  
 newValue 
 ) 
  
 newDocumentEntry 
  
 := 
  
 map 
 [ 
 string 
 ] 
 string 
 { 
 "original" 
 : 
  
 newValue 
 } 
  
 _ 
 , 
  
 err 
  
 = 
  
 client 
 . 
 Collection 
 ( 
 collection 
 ). 
 Doc 
 ( 
 doc 
 ). 
 Set 
 ( 
 ctx 
 , 
  
 newDocumentEntry 
 ) 
  
 if 
  
 err 
  
 != 
  
 nil 
  
 { 
  
 return 
  
 fmt 
 . 
 Errorf 
 ( 
 "Set: %w" 
 , 
  
 err 
 ) 
  
 } 
  
 return 
  
 nil 
 } 
 

Java

  import 
  
 com.google.cloud.firestore. Firestore 
 
 ; 
 import 
  
 com.google.cloud.firestore. FirestoreOptions 
 
 ; 
 import 
  
 com.google.cloud.firestore. SetOptions 
 
 ; 
 import 
  
 com.google.cloud.functions.CloudEventsFunction 
 ; 
 import 
  
 com.google.events.cloud.firestore.v1. DocumentEventData 
 
 ; 
 import 
  
 com.google.events.cloud.firestore.v1. Value 
 
 ; 
 import 
  
 com.google.protobuf. InvalidProtocolBufferException 
 
 ; 
 import 
  
 io.cloudevents.CloudEvent 
 ; 
 import 
  
 java.util.Map 
 ; 
 import 
  
 java.util.concurrent.ExecutionException 
 ; 
 import 
  
 java.util.logging.Logger 
 ; 
 public 
  
 class 
 FirebaseFirestoreReactive 
  
 implements 
  
 CloudEventsFunction 
  
 { 
  
 private 
  
 static 
  
 final 
  
 Logger 
  
 logger 
  
 = 
  
 Logger 
 . 
 getLogger 
 ( 
 FirebaseFirestoreReactive 
 . 
 class 
 . 
 getName 
 ()); 
  
 private 
  
 final 
  
  Firestore 
 
  
 firestore 
 ; 
  
 private 
  
 static 
  
 final 
  
 String 
  
 FIELD_KEY 
  
 = 
  
 "original" 
 ; 
  
 private 
  
 static 
  
 final 
  
 String 
  
 APPLICATION_PROTOBUF 
  
 = 
  
 "application/protobuf" 
 ; 
  
 public 
  
 FirebaseFirestoreReactive 
 () 
  
 { 
  
 this 
 ( 
  FirestoreOptions 
 
 . 
 getDefaultInstance 
 (). 
 getService 
 ()); 
  
 } 
  
 public 
  
 FirebaseFirestoreReactive 
 ( 
  Firestore 
 
  
 firestore 
 ) 
  
 { 
  
 this 
 . 
 firestore 
  
 = 
  
 firestore 
 ; 
  
 } 
  
 @Override 
  
 public 
  
 void 
  
 accept 
 ( 
 CloudEvent 
  
 event 
 ) 
  
 throws 
  
  InvalidProtocolBufferException 
 
 , 
  
 InterruptedException 
 , 
  
 ExecutionException 
  
 { 
  
 if 
  
 ( 
 event 
 . 
 getData 
 () 
  
 == 
  
 null 
 ) 
  
 { 
  
  logger 
 
 . 
 warning 
 ( 
 "No data found in event!" 
 ); 
  
 return 
 ; 
  
 } 
  
 if 
  
 ( 
 ! 
 event 
 . 
 getDataContentType 
 (). 
 equals 
 ( 
 APPLICATION_PROTOBUF 
 )) 
  
 { 
  
  logger 
 
 . 
 warning 
 ( 
 String 
 . 
 format 
 ( 
 "Found unexpected content type %s, expected %s" 
 , 
  
 event 
 . 
 getDataContentType 
 (), 
  
 APPLICATION_PROTOBUF 
 )); 
  
 return 
 ; 
  
 } 
  
  DocumentEventData 
 
  
 firestoreEventData 
  
 = 
  
  DocumentEventData 
 
  
 . 
 parseFrom 
 ( 
 event 
 . 
 getData 
 (). 
  toBytes 
 
 ()); 
  
 // Get the fields from the post-operation document snapshot 
  
 // https://firebase.google.com/docs/firestore/reference/rest/v1/projects.databases.documents#Document 
  
 Map<String 
 , 
  
 Value 
>  
 fields 
  
 = 
  
 firestoreEventData 
 . 
  getValue 
 
 (). 
 getFieldsMap 
 (); 
  
 if 
  
 ( 
 ! 
 fields 
 . 
 containsKey 
 ( 
 FIELD_KEY 
 )) 
  
 { 
  
  logger 
 
 . 
 warning 
 ( 
 "Document does not contain original field" 
 ); 
  
 return 
 ; 
  
 } 
  
 String 
  
 currValue 
  
 = 
  
 fields 
 . 
 get 
 ( 
 FIELD_KEY 
 ). 
 getStringValue 
 (); 
  
 String 
  
 newValue 
  
 = 
  
 currValue 
 . 
 toUpperCase 
 (); 
  
 if 
  
 ( 
 currValue 
 . 
 equals 
 ( 
 newValue 
 )) 
  
 { 
  
  logger 
 
 . 
 info 
 ( 
 "Value is already upper-case" 
 ); 
  
 return 
 ; 
  
 } 
  
 // Retrieve the document name from the resource path: 
  
 // projects/{project_id}/databases/{database_id}/documents/{document_path} 
  
 String 
  
 affectedDoc 
  
 = 
  
 firestoreEventData 
 . 
  getValue 
 
 () 
  
 . 
 getName 
 () 
  
 . 
 split 
 ( 
 "/documents/" 
 ) 
 [ 
 1 
 ] 
  
 . 
 replace 
 ( 
 "\"" 
 , 
  
 "" 
 ); 
  
  logger 
 
 . 
 info 
 ( 
 String 
 . 
 format 
 ( 
 "Replacing values: %s --> %s" 
 , 
  
 currValue 
 , 
  
 newValue 
 )); 
  
 // Wait for the async call to complete 
  
 this 
 . 
 firestore 
  
 . 
 document 
 ( 
 affectedDoc 
 ) 
  
 . 
 set 
 ( 
 Map 
 . 
 of 
 ( 
 FIELD_KEY 
 , 
  
 newValue 
 ), 
  
  SetOptions 
 
 . 
 merge 
 ()) 
  
 . 
 get 
 (); 
  
 } 
 } 
 

C#

  using 
  
 CloudNative.CloudEvents 
 ; 
 using 
  
  Google.Cloud.Firestore 
 
 ; 
 using 
  
 Google.Cloud.Functions.Framework 
 ; 
 using 
  
 Google.Cloud.Functions.Hosting 
 ; 
 using 
  
 Google.Events.Protobuf.Cloud.Firestore.V1 
 ; 
 using 
  
 Microsoft.AspNetCore.Hosting 
 ; 
 using 
  
  Microsoft.Extensions.DependencyInjection 
 
 ; 
 using 
  
 Microsoft.Extensions.Logging 
 ; 
 using 
  
 System.Collections.Generic 
 ; 
 using 
  
 System.Threading 
 ; 
 using 
  
 System.Threading.Tasks 
 ; 
 namespace 
  
 FirestoreReactive 
 ; 
 public 
  
 class 
  
 Startup 
  
 : 
  
 FunctionsStartup 
 { 
  
 public 
  
 override 
  
 void 
  
 ConfigureServices 
 ( 
 WebHostBuilderContext 
  
 context 
 , 
  
 IServiceCollection 
  
 services 
 ) 
  
 = 
>  
 services 
 . 
 AddSingleton 
 ( 
  FirestoreDb 
 
 . 
  Create 
 
 ()); 
 } 
 // Register the startup class to provide the Firestore dependency. 
 [FunctionsStartup(typeof(Startup))] 
 public 
  
 class 
  
 Function 
  
 : 
  
 ICloudEventFunction<DocumentEventData> 
 { 
  
 private 
  
 readonly 
  
 ILogger 
  
 _logger 
 ; 
  
 private 
  
 readonly 
  
  FirestoreDb 
 
  
 _firestoreDb 
 ; 
  
 public 
  
 Function 
 ( 
 ILogger<Function> 
  
 logger 
 , 
  
 FirestoreDb 
  
 firestoreDb 
 ) 
  
 = 
>  
 ( 
 _logger 
 , 
  
 _firestoreDb 
 ) 
  
 = 
  
 ( 
 logger 
 , 
  
 firestoreDb 
 ); 
  
 public 
  
 async 
  
 Task 
  
 HandleAsync 
 ( 
 CloudEvent 
  
 cloudEvent 
 , 
  
 DocumentEventData 
  
 data 
 , 
  
  CancellationToken 
 
  
 cancellationToken 
 ) 
  
 { 
  
 // Get the recently-written value. This expression will result in a null value 
  
 // if any of the following is true: 
  
 // - The event doesn't contain a "new" document 
  
 // - The value doesn't contain a field called "original" 
  
 // - The "original" field isn't a string 
  
 string 
  
 currentValue 
  
 = 
  
 data 
 . 
  Value 
 
 ?. 
 ConvertFields 
 (). 
 GetValueOrDefault 
 ( 
 "original" 
 ) 
  
 as 
  
 string 
 ; 
  
 if 
  
 ( 
 currentValue 
  
 is 
  
 null 
 ) 
  
 { 
  
 _logger 
 . 
 LogWarning 
 ( 
 $"Event did not contain a suitable document" 
 ); 
  
 return 
 ; 
  
 } 
  
 string 
  
 newValue 
  
 = 
  
 currentValue 
 . 
 ToUpperInvariant 
 (); 
  
 if 
  
 ( 
 newValue 
  
 == 
  
 currentValue 
 ) 
  
 { 
  
 _logger 
 . 
 LogInformation 
 ( 
 "Value is already upper-cased; no replacement necessary" 
 ); 
  
 return 
 ; 
  
 } 
  
 // The CloudEvent subject is "documents/x/y/...". 
  
 // The Firestore SDK FirestoreDb.Document method expects a reference relative to 
  
 // "documents" (so just the "x/y/..." part). This may be simplified over time. 
  
 if 
  
 ( 
 cloudEvent 
 . 
 Subject 
  
 is 
  
 null 
  
 || 
  
 ! 
 cloudEvent 
 . 
 Subject 
 . 
 StartsWith 
 ( 
 "documents/" 
 )) 
  
 { 
  
 _logger 
 . 
 LogWarning 
 ( 
 "CloudEvent subject is not a document reference." 
 ); 
  
 return 
 ; 
  
 } 
  
 string 
  
 documentPath 
  
 = 
  
 cloudEvent 
 . 
 Subject 
 . 
 Substring 
 ( 
 "documents/" 
 . 
  Length 
 
 ); 
  
 _logger 
 . 
 LogInformation 
 ( 
 "Replacing '{current}' with '{new}' in '{path}'" 
 , 
  
 currentValue 
 , 
  
 newValue 
 , 
  
 documentPath 
 ); 
  
 await 
  
 _firestoreDb 
 . 
  Document 
 
 ( 
 documentPath 
 ). 
  UpdateAsync 
 
 ( 
 "original" 
 , 
  
 newValue 
 , 
  
 cancellationToken 
 : 
  
 cancellationToken 
 ); 
  
 } 
 } 
 

Deploy the Convert to Uppercase function

If you haven't already done so, set up your Firestore database .

Click the tab for instructions using the tool of your choice.

Console

When you use the Google Cloud console to create a function, you can also add a trigger to your function. Follow these steps to create a trigger for your function:

  1. In the Google Cloud console, go to Cloud Run:

    Go to Cloud Run

  2. Click Write a function, and enter the function details. For more information about configuring functions during deployment, see Deploy functions .

  3. In the Triggersection, click Add trigger.

  4. Select Firestore trigger .

  5. In the Eventarc triggerpane, modify the trigger details as follows:

    1. Enter a name for the trigger in the Trigger namefield, or use the default name.

    2. Select a Trigger typefrom the list:

      • Google Sourcesto specify triggers for Pub/Sub, Cloud Storage, Firestore, and other Google event providers.

      • Third-partyto integrate with non-Google providers that offer an Eventarc source. For more information, see Third-party events in Eventarc .

    3. Select Firestore from the Event providerlist, to select a product that provides the type of event for triggering your function. For the list of event providers, see Event providers and destinations .

    4. Select type=google.cloud.firestore.document.v1.written from the Event typelist. Your trigger configuration varies depending on the supported event type. For more information, see Event types .

    5. In the Filters section, select a database, operation and attribute values, or use the default selections.

    6. If the Regionfield is enabled, select a location for the Eventarc trigger. In general, the location of an Eventarc trigger should match the location of the Google Cloud resource that you want to monitor for events. In most scenarios, you should also deploy your function in the same region. See Understand Eventarc locations for more details about Eventarc trigger locations.

    7. In the Service accountfield, select a service account. Eventarc triggers are linked to service accounts to use as an identity when invoking your function. Your Eventarc trigger's service account must have the permission to invoke your function. By default, Cloud Run uses the Compute Engine default service account .

    8. Optionally, specify the Service URL pathto send the incoming request to. This is the relative path on the destination service to which the events for the trigger should be sent. For example: / , /route , route , and route/subroute .

  6. Once you've completed the required fields, click Save trigger.

gcloud

When you create a function using the gcloud CLI, you must first deploy your function, and then create a trigger. Follow these steps to create a trigger for your function:

  1. Run the following command in the directory that contains the sample code to deploy your function:

     gcloud run deploy FUNCTION 
    \
            --source . \
            --function FUNCTION_ENTRYPOINT 
    \
            --base-image BASE_IMAGE_ID 
    \
            --region REGION 
     
    

    Replace:

    • FUNCTION with the name of the function you are deploying. You can omit this parameter entirely, but you will be prompted for the name if you omit it.

    • FUNCTION_ENTRYPOINT with the entry point to your function in your source code. This is the code Cloud Run executes when your function runs. The value of this flag must be a function name or fully-qualified class name that exists in your source code.

    • BASE_IMAGE_ID with the base image environment for your function. For more details about base images and the packages included in each image, see Runtimes base images .

    • REGION with the Google Cloud region where you want to deploy your function. For example, europe-west1 .

  2. Run the following command to create a trigger that filters events:

     gcloud eventarc triggers create TRIGGER_NAME 
    \
        --location= EVENTARC_TRIGGER_LOCATION 
    \
        --destination-run-service= FUNCTION 
    \
        --destination-run-region= REGION 
    \
        --event-filters=type=google.cloud.firestore.document.v1.written \
        --event-filters=database='(default)' \
        --event-data-content-type=application/protobuf \
        --event-filters-path-pattern=document='messages/{pushId}' \
        --service-account= PROJECT_NUMBER 
    -compute@developer.gserviceaccount.com 
    

    Replace:

    • TRIGGER_NAME with the name for your trigger.

    • EVENTARC_TRIGGER_LOCATION with the location for the Eventarc trigger. In general, the location of an Eventarc trigger should match the location of the Google Cloud resource that you want to monitor for events. In most scenarios, you should also deploy your function in the same region. For more information, see Eventarc locations .

    • FUNCTION with the name of the function you are deploying.

    • REGION with the Cloud Run region of the function.

    • PROJECT_NUMBER with your Google Cloud project number. Eventarc triggers are linked to service accounts to use as an identity when invoking your function. Your Eventarc trigger's service account must have the permission to invoke your function. By default, Cloud Run uses the Default compute service account.

    Each event-filters flag specifies a type of event, with the function triggering only when an event meets all of the criteria specified in its event-filters flags. Each trigger must have an event-filters flag specifying a supported event type , such as a new document written to Firestore or a file uploaded to Cloud Storage. You can't change the event filter type after creation. To change the event filter type, you must create a new trigger and delete the old one. Optionally, you can repeat the --event-filters flag with a supported filter in the form ATTRIBUTE=VALUE to add more filters.

Terraform

To create an Eventarc trigger for a Cloud Run function, see Create a trigger using Terraform .

Use the other fields as is:

  • --event-filters=type=google.cloud.firestore.document.v1.written specifies that the function is triggered when a document is created, updated or deleted, per the google.cloud.firestore.document.v1.written event type .
  • --event-filters=database='(default)' specifies the Firestore database. For the default database name, use (default) .
  • --event-filters-path-pattern=document='messages/{pushId}' provides the path pattern of the documents that should be monitored for relevant changes. This path pattern states that all documents in the messages collection should be monitored. For more information, see Understand path patterns .

Test the Convert to Uppercase function

To test the Convert to Uppercase function you just deployed, set up a collection called messages in your Firestore database :

  1. In the Google Cloud console, go to the Firestore databases page:

    Go to Firestore

  2. Click Start a collection.

  3. Specify messages as the collection ID.

  4. To start adding the collection's first document, under Add its first documentaccept the auto-generated Document ID.

  5. To trigger your deployed function, add a document where the Field nameis original and the Field valueis minka .

  6. When you save the document, you can see the lowercase word in the value field convert to uppercase.

    If you subsequently edit the field value to contain lowercase letters, that triggers the function again, converting all lowercase letters to uppercase.

Limitations for functions

  • Ordering is not guaranteed. Rapid changes can trigger function invocations in an unexpected order.
  • Events are delivered at least once, but a single event may result in multiple function invocations. Avoid depending on exactly-once mechanics, and write idempotent functions .
  • A trigger is associated with a single database. You cannot create a trigger that matches multiple databases.
  • Deleting a database does not automatically delete any triggers for that database. The trigger stops delivering events but continues to exist until you delete the trigger .
Design a Mobile Site
View Site in Mobile | Classic
Share by: