Amplía Cloud Firestore con compatibilidad con MongoDB con Cloud Functions (2ª gen.)

Con Cloud Functions, puedes implementar código para controlar eventos que se activan con los cambios en tu base de datos de Cloud Firestore con compatibilidad con MongoDB. Esto te permite agregar funciones del servidor a tu app sin ejecutar tus propios servidores.

Cloud Functions (2ª gen.)

Con la tecnología de Cloud Run y Eventarc, Cloud Functions for Firebase (2ª gen.) te brinda una infraestructura más potente, control avanzado sobre el rendimiento y la escalabilidad, y más control del entorno de ejecución de las funciones. Si quieres obtener más información sobre la 2ª gen., consulta Cloud Functions para Firebase (2ª gen.).

Activadores de funciones de Cloud Firestore con compatibilidad con MongoDB

El SDK de Cloud Functions for Firebase exporta los siguientes activadores de eventos de Cloud Firestore con compatibilidad con MongoDB para que puedas crear controladores vinculados a eventos específicos de Cloud Firestore con compatibilidad con MongoDB:

Node.js

Tipo de evento Activador
onDocumentCreated Se activa cuando se escribe en un documento por primera vez.
onDocumentUpdated Se activa cuando un documento ya existe y se cambia uno de sus valores.
onDocumentDeleted Se activa cuando se borra un documento.
onDocumentWritten Se activa con onDocumentCreated, onDocumentUpdated o onDocumentDeleted.
onDocumentCreatedWithAuthContext onDocumentCreated con información de autenticación adicional
onDocumentWrittenWithAuthContext onDocumentWritten con información de autenticación adicional
onDocumentDeletedWithAuthContext onDocumentDeleted con información de autenticación adicional
onDocumentUpdatedWithAuthContext onDocumentUpdated con información de autenticación adicional

Python

Tipo de evento Activador
on_document_created Se activa cuando se escribe en un documento por primera vez.
on_document_updated Se activa cuando un documento ya existe y se cambia uno de sus valores.
on_document_deleted Se activa cuando se borra un documento.
on_document_written Se activa con on_document_created, on_document_updated o on_document_deleted.
on_document_created_with_auth_context on_document_created con información de autenticación adicional
on_document_updated_with_auth_context on_document_updated con información de autenticación adicional
on_document_deleted_with_auth_context on_document_deleted con información de autenticación adicional
on_document_written_with_auth_context on_document_written con información de autenticación adicional

Los eventos de Cloud Firestore con compatibilidad con MongoDB se activan solo cuando ocurren cambios en los documentos. La actualización de un documento de Cloud Firestore con compatibilidad con MongoDB en la que no se modifican los datos (una escritura no-op) no genera un evento de actualización ni de escritura. No es posible agregar eventos a campos específicos.

Si aún no tienes un proyecto habilitado para Cloud Functions for Firebase, consulta Comenzar a usar Cloud Functions for Firebase (2ª gen.) para configurar tu proyecto de Cloud Functions for Firebase.

Escribe funciones activadas por Cloud Firestore con compatibilidad con MongoDB

Define un activador de función

Para definir un activador de Cloud Firestore con compatibilidad con MongoDB, especifica una ruta de acceso del documento y un tipo de evento:

Node.js

const {
  onDocumentWritten,
  onDocumentCreated,
  onDocumentUpdated,
  onDocumentDeleted,
  Change,
  FirestoreEvent
} = require('firebase-functions/v2/firestore');

exports.myfunction = onDocumentWritten("my-collection/{docId}", (event) => {
   /* ... */ 
});

Python

from firebase_functions.firestore_fn import (
  on_document_created,
  on_document_deleted,
  on_document_updated,
  on_document_written,
  Event,
  Change,
  DocumentSnapshot,
)

@on_document_created(document="users/{userId}")
def myfunction(event: Event[DocumentSnapshot]) -> None:

Las rutas de acceso de los documentos pueden hacer referencia a un documento específico o un patrón comodín.

Especifica un solo documento

Si deseas activar un evento para cualquier cambio que se realice a un documento específico, puedes usar la siguiente función.

Node.js

const {
  onDocumentWritten,
  Change,
  FirestoreEvent
} = require('firebase-functions/v2/firestore');

exports.myfunction = onDocumentWritten("users/marie", (event) => {
  // Your code here
});

Python

from firebase_functions.firestore_fn import (
  on_document_written,
  Event,
  Change,
  DocumentSnapshot,
)

@on_document_written(document="users/marie")
def myfunction(event: Event[Change[DocumentSnapshot]]) -> None:

Especifica un grupo de documentos mediante comodines

Si deseas conectar un activador a un grupo de documentos, como cualquier documento de una colección determinada, utiliza un {wildcard} en lugar del ID del documento:

Node.js

const {
  onDocumentWritten,
  Change,
  FirestoreEvent
} = require('firebase-functions/v2/firestore');

exports.myfunction = onDocumentWritten("users/{userId}", (event) => {
  // If we set `/users/marie` to {name: "Marie"} then
  // event.params.userId == "marie"
  // ... and ...
  // event.data.after.data() == {name: "Marie"}
});

Python

from firebase_functions.firestore_fn import (
  on_document_written,
  Event,
  Change,
  DocumentSnapshot,
)

@on_document_written(document="users/{userId}")
def myfunction(event: Event[Change[DocumentSnapshot]]) -> None:
  # If we set `/users/marie` to {name: "Marie"} then
  event.params["userId"] == "marie"  # True
  # ... and ...
  event.data.after.to_dict() == {"name": "Marie"}  # True

En este ejemplo, cuando se cambia algún campo en cualquier documento de users, coincide con un comodín llamado userId.

Las coincidencias de comodines se extraen de la ruta del documento y se almacenan en event.params. Los activadores siempre deben apuntar a un documento, incluso si usas un comodín.

Activadores de eventos

Activa una función cuando se cree un documento nuevo

Puedes activar una función para que se active cada vez que se cree un documento nuevo en una colección. Esta función de ejemplo se activa cada vez que se agrega un nuevo perfil de usuario:

Node.js

const {
  onDocumentCreated,
  Change,
  FirestoreEvent
} = require('firebase-functions/v2/firestore');

exports.createuser = onDocumentCreated("users/{userId}", (event) => {
    // Get an object representing the document
    // e.g. {'name': 'Marie', 'age': 66}
    const snapshot = event.data;
    if (!snapshot) {
        console.log("No data associated with the event");
        return;
    }
    const data = snapshot.data();

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

    // perform more operations ...
});

Para obtener información de autenticación adicional, usa onDocumentCreatedWithAuthContext.

Python

from firebase_functions.firestore_fn import (
  on_document_created,
  Event,
  DocumentSnapshot,
)

@on_document_created(document="users/{userId}")
def myfunction(event: Event[DocumentSnapshot]) -> None:
  # Get a dictionary representing the document
  # e.g. {'name': 'Marie', 'age': 66}
  new_value = event.data.to_dict()

  # Access a particular field as you would any dictionary
  name = new_value["name"]

  # Perform more operations ...

Activa una función cuando se actualice un documento

También puedes activar una función para que se active cuando se actualice un documento. Esta función de ejemplo se activa cada vez que un usuario cambia su perfil:

Node.js

const {
  onDocumentUpdated,
  Change,
  FirestoreEvent
} = require('firebase-functions/v2/firestore');

exports.updateuser = onDocumentUpdated("users/{userId}", (event) => {
    // Get an object representing the document
    // e.g. {'name': 'Marie', 'age': 66}
    const newValue = event.data.after.data();

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

    // perform more operations ...
});

Para obtener información de autenticación adicional, usa onDocumentUpdatedWithAuthContext.

Python

from firebase_functions.firestore_fn import (
  on_document_updated,
  Event,
  Change,
  DocumentSnapshot,
)

@on_document_updated(document="users/{userId}")
def myfunction(event: Event[Change[DocumentSnapshot]]) -> None:
  # Get a dictionary representing the document
  # e.g. {'name': 'Marie', 'age': 66}
  new_value = event.data.after.to_dict()

  # Access a particular field as you would any dictionary
  name = new_value["name"]

  # Perform more operations ...

Activa una función cuando se borre un documento

Además, puedes activar una función cuando se borre un documento. Esta función de ejemplo se activa cuando un usuario borra su perfil:

Node.js

const {
  onDocumentDeleted,
  Change,
  FirestoreEvent
} = require('firebase-functions/v2/firestore');

exports.deleteuser = onDocumentDeleted("users/{userId}", (event) => {
    // Get an object representing the document
    // e.g. {'name': 'Marie', 'age': 66}
    const snap =  event.data;
    const data =  snap.data();

    // perform more operations ...
});

Para obtener información de autenticación adicional, usa onDocumentDeletedWithAuthContext.

Python

from firebase_functions.firestore_fn import (
  on_document_deleted,
  Event,
  DocumentSnapshot,
)

@on_document_deleted(document="users/{userId}")
def myfunction(event: Event[DocumentSnapshot|None]) -> None:
  # Perform more operations ...

Activa una función para todos los cambios en un documento

Si no te interesa el tipo de evento que se activa, puedes detectar todos los cambios en un documento de Cloud Firestore con compatibilidad con MongoDB con el activador de eventos “documento escrito”. Esta función de ejemplo se activa si se crea, actualiza o borra un usuario:

Node.js

const {
  onDocumentWritten,
  Change,
  FirestoreEvent
} = require('firebase-functions/v2/firestore');

exports.modifyuser = onDocumentWritten("users/{userId}", (event) => {
    // Get an object with the current document values.
    // If the document does not exist, it was deleted
    const document =  event.data.after.data();

    // Get an object with the previous document values
    const previousValues =  event.data.before.data();

    // perform more operations ...
});

Para obtener información de autenticación adicional, usa onDocumentWrittenWithAuthContext.

Python

from firebase_functions.firestore_fn import (
  on_document_written,
  Event,
  Change,
  DocumentSnapshot,
)

@on_document_written(document="users/{userId}")
def myfunction(event: Event[Change[DocumentSnapshot | None]]) -> None:
  # Get an object with the current document values.
  # If the document does not exist, it was deleted.
  document = (event.data.after.to_dict()
              if event.data.after is not None else None)

  # Get an object with the previous document values.
  # If the document does not exist, it was newly created.
  previous_values = (event.data.before.to_dict()
                     if event.data.before is not None else None)

  # Perform more operations ...

Lectura y escritura de datos

Cuando se activa una función, esta proporciona una instantánea de los datos relacionados con el evento. Puedes usar esta instantánea para leer el documento que activó el evento o escribir en él, o bien usar el SDK de Firebase Admin para acceder a otras partes de tu base de datos.

Datos de eventos

Lee datos

Cuando se activa una función, es posible que desees obtener datos de un documento antes o después de que se lo actualice. Para obtener los datos previos, puedes usar event.data.before, que contiene la instantánea del documento antes de la actualización. De manera similar, event.data.after contiene el estado de la instantánea del documento después de la actualización.

Node.js

exports.updateuser2 = onDocumentUpdated("users/{userId}", (event) => {
    // Get an object with the current document values.
    // If the document does not exist, it was deleted
    const newValues =  event.data.after.data();

    // Get an object with the previous document values
    const previousValues =  event.data.before.data();
});

Python

@on_document_updated(document="users/{userId}")
def myfunction(event: Event[Change[DocumentSnapshot]]) -> None:
  # Get an object with the current document values.
  new_value = event.data.after.to_dict()

  # Get an object with the previous document values.
  prev_value = event.data.before.to_dict()

Puedes acceder a las propiedades del mismo modo que con cualquier otro objeto. También puedes utilizar la función get para acceder a campos específicos:

Node.js

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

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

Python

# Get the value of a single document field.
age = event.data.after.get("age")

# Convert the document to a dictionary.
age = event.data.after.to_dict()["age"]

Escribe datos

Cada invocación de función está asociada a un documento específico de tu base de datos de Cloud Firestore con compatibilidad con MongoDB. Puedes acceder a ese documento en la instantánea que se muestra a tu función.

La referencia del documento incluye métodos como update(), set() y remove() para que puedas modificar el documento que activó la función.

Node.js

const {onDocumentUpdated} = require('firebase-functions/v2/firestore');

exports.countnamechanges = onDocumentUpdated('users/{userId}', (event) => {
  // Retrieve the current and previous value
  const data = event.data.after.data();
  const previousData = event.data.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 event.data.after.ref.set({
    name_change_count: count + 1
  }, {merge: true});

});

Python

@on_document_updated(document="users/{userId}")
def myfunction(event: Event[Change[DocumentSnapshot]]) -> None:
  # Get the current and previous document values.
  new_value = event.data.after
  prev_value = event.data.before

  # We'll only update if the name has changed.
  # This is crucial to prevent infinite loops.
  if new_value.get("name") == prev_value.get("name"):
      return

  # Retrieve the current count of name changes
  count = new_value.to_dict().get("name_change_count", 0)

  # Update the count
  new_value.reference.update({"name_change_count": count + 1})

Accede a la información de autenticación del usuario

Si usas uno de los siguientes tipos de eventos, puedes acceder a la información de autenticación del usuario sobre el principal que activó el evento. Esta información se suma a la que se muestra en el evento base.

Node.js

  • onDocumentCreatedWithAuthContext
  • onDocumentWrittenWithAuthContext
  • onDocumentDeletedWithAuthContext
  • onDocumentUpdatedWithAuthContext

Python

  • on_document_created_with_auth_context
  • on_document_updated_with_auth_context
  • on_document_deleted_with_auth_context
  • on_document_written_with_auth_context

Para obtener información sobre los datos disponibles en el contexto de autenticación, consulta Contexto de autenticación. En el siguiente ejemplo, se muestra cómo recuperar información de autenticación:

Node.js

const {onDocumentWrittenWithAuthContext} = require('firebase-functions/v2/firestore');

exports.syncUser = onDocumentWrittenWithAuthContext("users/{userId}", (event) => {
    const snapshot = event.data.after;
    if (!snapshot) {
        console.log("No data associated with the event");
        return;
    }
    const data = snapshot.data();

    // retrieve auth context from event
    const { authType, authId } = event;

    let verified = false;
    if (authType === "system") {
      // system-generated users are automatically verified
      verified = true;
    } else if (authType === "unknown" || authType === "unauthenticated") {
      // admin users from a specific domain are verified
      if (authId.endsWith("@example.com")) {
        verified = true;
      }
    }

    return data.after.ref.set({
        created_by: authId,
        verified,
    }, {merge: true}); 
}); 

Python

@on_document_updated_with_auth_context(document="users/{userId}")
def myfunction(event: Event[Change[DocumentSnapshot]]) -> None:

  # Get the current and previous document values.
  new_value = event.data.after
  prev_value = event.data.before

  # Get the auth context from the event
  user_auth_type = event.auth_type
  user_auth_id = event.auth_id

Datos fuera del evento activador

Cloud Functions se ejecuta en entornos de confianza, lo que significa que cuenta con autorización como una cuenta de servicio en tu proyecto. Puedes realizar operaciones de lectura y escritura con el SDK de Firebase Admin:

Node.js

const { initializeApp } = require('firebase-admin/app');
const { getFirestore, Timestamp, FieldValue } = require('firebase-admin/firestore');

initializeApp();
const db = getFirestore();

exports.writetofirestore = onDocumentWritten("some/doc", (event) => {
    db.doc('some/otherdoc').set({ ... });
  });

  exports.writetofirestore = onDocumentWritten('users/{userId}', (event) => {
    db.doc('some/otherdoc').set({
      // Update otherdoc
    });
  });

Python

from firebase_admin import firestore, initialize_app
import google.cloud.firestore

initialize_app()

@on_document_written(document="some/doc")
def myfunction(event: Event[Change[DocumentSnapshot | None]]) -> None:
  firestore_client: google.cloud.firestore.Client = firestore.client()
  firestore_client.document("another/doc").set({
      # ...
  })

Limitaciones

  • No se garantiza el ordenamiento. Los cambios rápidos pueden activar invocaciones de funciones en un orden inesperado.
  • Los eventos se entregan al menos una vez, pero un solo evento puede dar lugar a varias invocaciones de funciones. Evita depender de la mecánica de entrega de eventos exactamente una vez y escribe funciones idempotentes.
  • Un activador se asocia con una sola base de datos. No puedes crear un activador que coincida con varias bases de datos.
  • Cuando se borra una base de datos, no se borra automáticamente ningún activador de la base de datos. El activador deja de entregar eventos, pero sigue existiendo hasta que lo borras.