Activadores de Firebase Realtime Database

Con Cloud Run Functions, puedes controlar eventos en Firebase Realtime Database en el mismo proyecto de Google Cloud en el que se encuentra la función. Cloud Run Functions te permite ejecutar operaciones de base de datos con privilegios de administrador completos y garantiza que cada cambio en la base de datos se procese de forma individual. Puedes hacer cambios en Firebase Realtime Database mediante el SDK de Firebase Admin.

.

En un ciclo de vida típico, una función de Firebase Realtime Database hace lo siguiente:

  1. Espera cambios en una ubicación de base de datos específica.

  2. Se activa cuando ocurre un evento y realiza sus tareas.

  3. Recibe un objeto de datos que contiene una instantánea de los datos almacenados en el documento especificado.

Tipos de eventos

Las funciones te permiten controlar los eventos de la base de datos en dos niveles de especificidad: puedes detectar específicamente solo eventos de creación, actualización o eliminación, o puedes detectar cambios de cualquier tipo en una ruta de acceso. Cloud Run Functions admite los siguientes tipos de eventos para Realtime Database:

Tipo de evento Activador
providers/google.firebase.database/eventTypes/ref.write Se activa en cualquier evento de mutación: cuando se crean, actualizan o borran datos en Realtime Database.
providers/google.firebase.database/eventTypes/ref.create (predeterminada) Se activa cuando se crean datos nuevos en Realtime Database.
providers/google.firebase.database/eventTypes/ref.update Se activa cuando se actualizan los datos en Realtime Database.
providers/google.firebase.database/eventTypes/ref.delete Se activa cuando se borran datos de Realtime Database.

Especifica la ruta de la base de datos y la instancia

Para controlar cuándo y dónde se debe activar tu función, tienes que especificar una ruta de acceso y, opcionalmente, una instancia de base de datos.

Ruta

Las especificaciones de la ruta de acceso coinciden con todas las escrituras que afectan una ruta, incluidas aquellas que ocurren en cualquier lugar debajo de ella. Si estableces la ruta de acceso para tu función como /foo/bar, se muestran coincidencias de eventos en estas dos ubicaciones:

 /foo/bar
 /foo/bar/baz/really/deep/path

En ambos casos, Firebase interpreta que el evento ocurre en /foo/bar, y los datos del evento incluyen los datos antiguos y nuevos en /foo/bar. Si los datos del evento son grandes, puedes usar varias funciones en rutas de acceso más profundas en lugar de una sola función cerca de la raíz de la base de datos. Para obtener el mejor rendimiento, solicita datos únicamente en el nivel más profundo posible.

Para especificar un componente de ruta de acceso como comodín, puedes ponerlo entre llaves. foo/{bar} coincide con cualquier elemento secundario de /foo. Los valores de estos componentes de ruta de acceso comodín están disponibles dentro del objeto event.params de la función. En este ejemplo, el valor está disponible como event.params.bar.

Las rutas de acceso con comodines pueden coincidir con varios eventos de una misma escritura. La inserción de

{
  "foo": {
    "hello": "world",
    "firebase": "functions"
  }
}

coincide con la ruta de acceso /foo/{bar} dos veces: una vez con "hello": "world" y otra vez con "firebase": "functions".

Instancia

Cuando se usa la consola de Google Cloud, se debe especificar la instancia de base de datos.

Cuando se usa Google Cloud CLI, la instancia debe especificarse como parte de la string --trigger-resource.

Por ejemplo, lo siguiente usaría lo que se indica a continuación en tu string de --trigger-resource:

--trigger-resource projects/_/instances/DATABASE_INSTANCE/refs/PATH

Estructura de eventos

Cuando se controla un evento de Realtime Database, el objeto data contiene dos propiedades que se proporcionan en formato de objeto JSON:

  • data: es una instantánea de los datos tomada antes del evento que activó la función.

  • delta: es una instantánea de los datos tomados después del evento que activó la función.

.

Muestra de código

Node.js

/**
 * Background Function triggered by a change to a Firebase RTDB reference.
 *
 * @param {!Object} event The Cloud Functions event.
 * @param {!Object} context The Cloud Functions event context.
 */
exports.helloRTDB = (event, context) => {
  const triggerResource = context.resource;

  console.log(`Function triggered by change to: ${triggerResource}`);
  console.log(`Admin?: ${!!context.auth.admin}`);
  console.log('Delta:');
  console.log(JSON.stringify(event.delta, null, 2));
};

Python

import json

def hello_rtdb(data, context):
    """Triggered by a change to a Firebase RTDB reference.
    Args:
        data (dict): The event payload.
        context (google.cloud.functions.Context): Metadata for the event.
    """
    trigger_resource = context.resource

    print("Function triggered by change to: %s" % trigger_resource)
    print("Admin?: %s" % data.get("admin", False))
    print("Delta:")
    print(json.dumps(data["delta"]))

Go


// Package p contains a Cloud Function triggered by a Firebase Realtime Database
// event.
package p

import (
	"context"
	"fmt"
	"log"

	"cloud.google.com/go/functions/metadata"
)

// RTDBEvent is the payload of a RTDB event.
type RTDBEvent struct {
	Data  interface{} `json:"data"`
	Delta interface{} `json:"delta"`
}

// HelloRTDB handles changes to a Firebase RTDB.
func HelloRTDB(ctx context.Context, e RTDBEvent) error {
	meta, err := metadata.FromContext(ctx)
	if err != nil {
		return fmt.Errorf("metadata.FromContext: %w", err)
	}
	log.Printf("Function triggered by change to: %v", meta.Resource)
	log.Printf("%+v", e)
	return nil
}

Java

import com.google.cloud.functions.Context;
import com.google.cloud.functions.RawBackgroundFunction;
import com.google.gson.Gson;
import com.google.gson.JsonObject;
import java.util.logging.Logger;

public class FirebaseRtdb implements RawBackgroundFunction {
  private static final Logger logger = Logger.getLogger(FirebaseRtdb.class.getName());

  // Use GSON (https://github.com/google/gson) to parse JSON content.
  private static final Gson gson = new Gson();

  @Override
  public void accept(String json, Context context) {
    logger.info("Function triggered by change to: " + context.resource());

    JsonObject body = gson.fromJson(json, JsonObject.class);

    boolean isAdmin = false;
    if (body != null && body.has("auth")) {
      JsonObject authObj = body.getAsJsonObject("auth");
      isAdmin = authObj.has("admin") && authObj.get("admin").getAsBoolean();
    }

    logger.info("Admin?: " + isAdmin);

    if (body != null && body.has("delta")) {
      logger.info("Delta:");
      logger.info(body.get("delta").toString());
    }
  }
}

C#

using CloudNative.CloudEvents;
using Google.Cloud.Functions.Framework;
using Google.Events.Protobuf.Firebase.Database.V1;
using Microsoft.Extensions.Logging;
using System.Threading;
using System.Threading.Tasks;

namespace FirebaseRtdb;

public class Function : ICloudEventFunction<ReferenceEventData>
{
    private readonly ILogger _logger;

    public Function(ILogger<Function> logger) =>
        _logger = logger;

    public Task HandleAsync(CloudEvent cloudEvent, ReferenceEventData data, CancellationToken cancellationToken)
    {
        _logger.LogInformation("Function triggered by change to {subject}", cloudEvent.Subject);
        _logger.LogInformation("Delta: {delta}", data.Delta);

        // 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;
    }
}

Ruby

require "functions_framework"

# Triggered by a change to a Firebase RTDB document.
FunctionsFramework.cloud_event "hello_rtdb" do |event|
  # Event-triggered Ruby functions receive a CloudEvents::Event::V1 object.
  # See https://cloudevents.github.io/sdk-ruby/latest/CloudEvents/Event/V1.html
  # The Firebase event payload can be obtained from the `data` field.
  payload = event.data

  logger.info "Function triggered by change to: #{event.source}"
  logger.info "Admin?: #{payload.fetch 'admin', false}"
  logger.info "Delta: #{payload['delta']}"
end

PHP


use Google\CloudFunctions\CloudEvent;

function firebaseRTDB(CloudEvent $cloudevent)
{
    $log = fopen(getenv('LOGGER_OUTPUT') ?: 'php://stderr', 'wb');

    fwrite($log, 'Event: ' . $cloudevent->getId() . PHP_EOL);

    $data = $cloudevent->getData();
    $resource = $data['resource'] ?? '<null>';

    fwrite($log, 'Function triggered by change to: ' . $resource . PHP_EOL);

    $isAdmin = isset($data['auth']['admin']) && $data['auth']['admin'] == true;

    fwrite($log, 'Admin?: ' . var_export($isAdmin, true) . PHP_EOL);
    fwrite($log, 'Delta: ' . json_encode($data['delta'] ?? '') . PHP_EOL);
}

Implementa la función

Con el siguiente comando de gcloud, se implementa una función que se activará mediante eventos create en la ruta /messages/{pushId}/original:

gcloud functions deploy FUNCTION_NAME \
  --no-gen2 \
  --entry-point ENTRY_POINT \
  --trigger-event providers/google.firebase.database/eventTypes/ref.create \
  --trigger-resource projects/_/instances/DATABASE_INSTANCE/refs/messages/{pushId}/original \
  --runtime RUNTIME
Argumento Descripción
FUNCTION_NAME El nombre registrado de la función de Cloud Run Functions que estás implementando. Puede ser el nombre de una función en tu código fuente o una string arbitraria. Si FUNCTION_NAME es una string arbitraria, debes incluir la marca --entry-point.
--entry-point ENTRY_POINT El nombre de una función o clase en tu código fuente. Opcional, a menos que no hayas usado FUNCTION_NAME para especificar la función en tu código fuente que se ejecutará durante la implementación. En ese caso, debes usar --entry-point para proporcionar el nombre de la función ejecutable.
--trigger-event NAME El nombre del tipo de evento que la función desea recibir. En este caso, será uno de los siguientes: escribir, crear, actualizar o borrar.
--trigger-resource NAME Es la ruta de acceso de la base de datos completamente calificada a la que escuchará la función. Esto debe ajustarse al siguiente formato: projects/_/instances/DATABASE_INSTANCE/refs/PATH.
--runtime RUNTIME Es el nombre del entorno de ejecución que usas. Para obtener una lista completa, consulta la gcloudreferencia.