Activadores de Firebase Realtime Database

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

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

  1. Espera a que se produzcan cambios en una ubicación de base de datos concreta.

  2. Se activa cuando se produce 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 gestionar eventos de bases de datos en dos niveles de especificidad: puedes escuchar específicamente solo los eventos de creación, actualización o eliminación, o puedes escuchar cualquier cambio de cualquier tipo en una ruta. Cloud Run functions admite los siguientes tipos de eventos de 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 eliminan datos en Realtime Database.
providers/google.firebase.database/eventTypes/ref.create (predeterminado) Se activa cuando se crean datos en la base de datos en tiempo real.
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 eliminan datos de Realtime Database.

Especificar la ruta y la instancia de la base de datos

Para controlar cuándo y dónde debe activarse tu función, debes especificar una ruta y, opcionalmente, una instancia de base de datos.

Ruta

Las especificaciones de ruta coinciden con todas las escrituras que tocan una ruta, incluidas las que se producen en cualquier lugar por debajo de ella. Si defines la ruta de tu función como /foo/bar, coincidirá con los eventos de estas dos ubicaciones:

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

En ambos casos, Firebase interpreta que el evento se produce en /foo/bar y los datos del evento incluyen los datos antiguos y nuevos en /foo/bar. Si los datos de eventos pueden ser grandes, considera la posibilidad de usar varias funciones en rutas más profundas en lugar de una sola función cerca de la raíz de tu base de datos. Para obtener el mejor rendimiento, solicita datos solo en el nivel más profundo posible.

Puede especificar un componente de ruta como comodín rodeándolo con llaves. foo/{bar} coincide con cualquier elemento secundario de /foo. Los valores de estos componentes de ruta comodín están disponibles en el objeto event.params de tu función. En este ejemplo, el valor está disponible como event.params.bar.

Las rutas con comodines pueden coincidir con varios eventos de una sola escritura. Una inserción de:

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

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

Instancia

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

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

Por ejemplo, se usaría lo siguiente en tu cadena --trigger-resource:

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

Estructura de eventos

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

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

  • delta: una instantánea de los datos tomada después del evento que ha activado la función.

Código de ejemplo

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

Desplegar una función

El siguiente comando de gcloud despliega 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 Nombre registrado de la función de Cloud Run que vas a desplegar. Puede ser el nombre de una función de tu código fuente o una cadena arbitraria. Si FUNCTION_NAME es una cadena arbitraria, debes incluir la marca --entry-point.
--entry-point ENTRY_POINT El nombre de una función o una clase en el código fuente. Opcional, a menos que no hayas usado FUNCTION_NAME para especificar la función en el código fuente que se va a ejecutar durante la implementación. En ese caso, debes usar --entry-point para indicar el nombre de la función ejecutable.
--trigger-event NAME Nombre del tipo de evento que quiere recibir la función. En este caso, será una de las siguientes: escribir, crear, actualizar o eliminar.
--trigger-resource NAME Ruta de la base de datos completa a la que se conectará la función. Debe tener el siguiente formato: projects/_/instances/DATABASE_INSTANCE/refs/PATH.
--runtime RUNTIME El nombre del tiempo de ejecución que estás usando. Para ver una lista completa, consulta la referencia de gcloud.