React to value change by updating a value

Updates a value whenever it is changed in Firestore.

Documentation pages that include this code sample

To view the code sample used in context, see the following documentation:

Code sample


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) =>

    // Register the startup class to provide the Firestore dependency.
    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");

            string newValue = currentValue.ToUpperInvariant();
            if (newValue == currentValue)
                _logger.LogInformation("Value is already upper-cased; no replacement necessary");

            // 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.");
            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);


// Package upper contains a Firestore Cloud Function.
package upper

import (

	firebase ""

// FirestoreEvent is the payload of a Firestore event.
type FirestoreEvent struct {
	OldValue   FirestoreValue `json:"oldValue"`
	Value      FirestoreValue `json:"value"`
	UpdateMask struct {
		FieldPaths []string `json:"fieldPaths"`
	} `json:"updateMask"`

// FirestoreValue holds Firestore fields.
type FirestoreValue struct {
	CreateTime time.Time `json:"createTime"`
	// Fields is the data for this value. The type depends on the format of your
	// database. Log an interface{} value and inspect the result to see a JSON
	// representation of your database fields.
	Fields     MyData    `json:"fields"`
	Name       string    `json:"name"`
	UpdateTime time.Time `json:"updateTime"`

// MyData represents a value from Firestore. The type definition depends on the
// format of your database.
type MyData struct {
	Original struct {
		StringValue string `json:"stringValue"`
	} `json:"original"`

// GOOGLE_CLOUD_PROJECT is automatically set by the Cloud Functions runtime.
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)

// 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 FirestoreEvent) error {
	fullPath := strings.Split(e.Value.Name, "/documents/")[1]
	pathParts := strings.Split(fullPath, "/")
	collection := pathParts[0]
	doc := strings.Join(pathParts[1:], "/")

	curValue := e.Value.Fields.Original.StringValue
	newValue := strings.ToUpper(curValue)
	if curValue == newValue {
		log.Printf("%q is already upper case: skipping", curValue)
		return nil
	log.Printf("Replacing value: %q -> %q", curValue, newValue)

	data := map[string]string{"original": newValue}
	_, err := client.Collection(collection).Doc(doc).Set(ctx, data)
	if err != nil {
		return fmt.Errorf("Set: %v", err)
	return nil


import java.util.HashMap;
import java.util.Locale;
import java.util.Map;
import java.util.concurrent.ExecutionException;
import java.util.logging.Level;
import java.util.logging.Logger;

public class FirebaseFirestoreReactive implements RawBackgroundFunction {

  // Use GSON ( to parse JSON content.
  private static final Gson gson = new Gson();

  private static final Logger logger = Logger.getLogger(FirebaseFirestoreReactive.class.getName());
  private static final Firestore FIRESTORE = FirestoreOptions.getDefaultInstance().getService();

  private final Firestore firestore;

  public FirebaseFirestoreReactive() {

  FirebaseFirestoreReactive(Firestore firestore) {
    this.firestore = firestore;

  public void accept(String json, Context context) {
    // Get the recently-written value
    JsonObject body = gson.fromJson(json, JsonObject.class);
    JsonObject tempJson = body.getAsJsonObject("value");

    // Verify that value.fields.original.stringValue exists
    String currentValue = null;
    if (tempJson != null) {
      tempJson = tempJson.getAsJsonObject("fields");
    if (tempJson != null) {
      tempJson = tempJson.getAsJsonObject("original");
    if (tempJson != null && tempJson.has("stringValue")) {
      currentValue = tempJson.get("stringValue").getAsString();
    if (currentValue == null) {
      throw new IllegalArgumentException("Malformed JSON: " + json);

    // Convert recently-written value to ALL CAPS
    String newValue = currentValue.toUpperCase(Locale.getDefault());

    // Update Firestore DB with ALL CAPS value
    Map<String, String> newFields = Map.of("original", newValue);

    String affectedDoc = context.resource().split("/documents/")[1].replace("\"", "");

    if (!currentValue.equals(newValue)) {
      // The stored value needs to be updated
      // Write the upper-cased value to Firestore"Replacing value: %s --> %s", currentValue, newValue));
      try {
        FIRESTORE.document(affectedDoc).set(newFields, SetOptions.merge()).get();
      } catch (ExecutionException | InterruptedException e) {
        logger.log(Level.SEVERE, "Error updating Firestore document: " + e.getMessage(), e);
    } else {
      // The stored value is already upper-case, and doesn't need updating.
      // (Don't perform a "second" write, since that could trigger an infinite loop.)"Value is already upper-case."));


const Firestore = require('@google-cloud/firestore');

const firestore = new Firestore({
  projectId: process.env.GOOGLE_CLOUD_PROJECT,

// Converts strings added to /messages/{pushId}/original to uppercase
exports.makeUpperCase = event => {
  const resource =;
  const affectedDoc = firestore.doc(resource.split('/documents/')[1]);

  const curValue = event.value.fields.original.stringValue;
  const newValue = curValue.toUpperCase();

  if (curValue !== newValue) {
    console.log(`Replacing value: ${curValue} --> ${newValue}`);

    return affectedDoc.set({
      original: newValue,
  } else {
    // Value is already upper-case
    // Don't perform a(nother) write to avoid infinite loops
    console.log('Value is already upper-case.');


use Google\Cloud\Firestore\FirestoreClient;
use Google\CloudFunctions\CloudEvent;

function firebaseReactive(CloudEvent $cloudevent)
    $log = fopen(getenv('LOGGER_OUTPUT') ?: 'php://stderr', 'wb');
    $data = $cloudevent->getData();

    $resource = $data['value']['name'];

    $db = new FirestoreClient();

    $docPath = explode('/documents/', $resource)[1];

    $affectedDoc = $db->document($docPath);

    $curValue = $data['value']['fields']['original']['stringValue'];
    $newValue = strtoupper($curValue);

    if ($curValue !== $newValue) {
        fwrite($log, 'Replacing value: ' . $curValue . ' --> ' . $newValue . PHP_EOL);

        $affectedDoc->set(['original' => $newValue]);
    } else {
        // Value is already upper-case
        // Don't perform another write (it might cause an infinite loop)
        fwrite($log, 'Value is already upper-case.' . PHP_EOL);


from import firestore
client = firestore.Client()

# Converts strings added to /messages/{pushId}/original to uppercase
def make_upper_case(data, context):
    path_parts = context.resource.split('/documents/')[1].split('/')
    collection_path = path_parts[0]
    document_path = '/'.join(path_parts[1:])

    affected_doc = client.collection(collection_path).document(document_path)

    cur_value = data["value"]["fields"]["original"]["stringValue"]
    new_value = cur_value.upper()

    if cur_value != new_value:
        print(f'Replacing value: {cur_value} --> {new_value}')
            u'original': new_value
        # Value is already upper-case
        # Don't perform a second write (which can trigger an infinite loop)
        print('Value is already upper-case.')


require "functions_framework"

FunctionsFramework.on_startup do
  # Lazily construct a Firestore client when needed, and reuse it on
  # subsequent calls.
  set_global :firestore_client do
    require "google/cloud/firestore" project_id: ENV["GOOGLE_CLOUD_PROJECT"]

# Converts strings added to /messages/{pushId}/original to uppercase
FunctionsFramework.cloud_event "make_upper_case" do |event|
  # Event-triggered Ruby functions receive a CloudEvents::Event::V1 object.
  # See
  # The Firebase event payload can be obtained from the event data.
  cur_value =["value"]["fields"]["original"]["stringValue"]

  # Compute new value and determine whether it needs to be modified.
  # If the value is already upper-case, don't perform another write,
  # to avoid infinite loops.
  new_value = cur_value.upcase
  if cur_value == new_value "Value is already upper-case"

  # Use the Firestore client library to update the value.
  # The document name can be obtained from the event subject. "Replacing value: #{cur_value} --> #{new_value}"
  doc_name = event.subject.split("documents/").last
  affected_doc = global(:firestore_client).doc doc_name
  new_doc_data = { original: new_value }
  affected_doc.set new_doc_data

What's next

To search and filter code samples for other Google Cloud products, see the Google Cloud sample browser