Con Cloud Functions, puedes controlar eventos en Cloud Firestore sin necesidad de actualizar el código del cliente. Puedes hacer cambios en Cloud Firestore a través de la interfaz de instantáneas del documento o el SDK de Admin.
En un ciclo de vida típico, una función de Cloud Firestore hace lo siguiente:
- Espera a que ocurran cambios en un documento en particular.
- Se activa cuando ocurre un evento y realiza sus tareas.
- Recibe un objeto de datos que contiene una instantánea de los datos almacenados en el documento especificado. En el caso de los eventos de escritura o actualización, el objeto de datos contiene dos instantáneas que representan el estado de los datos antes y después del evento que provocó la activación.
La distancia entre la ubicación de la instancia de Firestore y la ubicación de la función puede crear una latencia de red considerable. Para optimizar el rendimiento, considera especificar la ubicación de la función cuando corresponda.
Activadores de funciones de Cloud Firestore
El SDK de Cloud Functions for Firebase exporta un objeto functions.firestore
que te permite crear controladores vinculados a eventos específicos de Cloud Firestore.
Tipo de evento | Activador |
---|---|
onCreate |
Se activa cuando se escribe en un documento por primera vez. |
onUpdate |
Se activa cuando un documento ya existe y se cambia uno de sus valores. |
onDelete |
Se activa cuando se borra un documento con datos. |
onWrite |
Se activa con onCreate , onUpdate o onDelete . |
Si aún no tienes un proyecto habilitado para Cloud Functions for Firebase, lee Primeros pasos: Escribe y, luego, implementa tus primeras funciones para configurar tu proyecto de Cloud Functions for Firebase.
Escribe funciones activadas por Cloud Firestore
Define un activador de función
Para definir un activador de Cloud Firestore, especifica la ruta de acceso de un documento y un tipo de evento:
Node.js
const functions = require('firebase-functions');
exports.myFunction = functions.firestore
.document('my-collection/{docId}')
.onWrite((change, context) => { /* ... */ });
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
// Listen for any change on document `marie` in collection `users` exports.myFunctionName = functions.firestore .document('users/marie').onWrite((change, context) => { // ... Your code here });
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
// Listen for changes in all documents in the 'users' collection exports.useWildcard = functions.firestore .document('users/{userId}') .onWrite((change, context) => { // If we set `/users/marie` to {name: "Marie"} then // context.params.userId == "marie" // ... and ... // change.after.data() == {name: "Marie"} });
En este ejemplo, cuando se cambia algún campo en cualquier documento de users
, coincide
con un comodín llamado userId
.
Si un documento de users
tiene subcolecciones y se modifica un campo de uno de sus documentos, no se activa el comodín userId
.
Las coincidencias de comodines se extraen de la ruta del documento y se almacenan en context.params
.
Puedes definir tantos comodines como desees para sustituir los IDs explícitos de colección o de documento, por ejemplo:
Node.js
// Listen for changes in all documents in the 'users' collection and all subcollections exports.useMultipleWildcards = functions.firestore .document('users/{userId}/{messageCollectionId}/{messageId}') .onWrite((change, context) => { // If we set `/users/marie/incoming_messages/134` to {body: "Hello"} then // context.params.userId == "marie"; // context.params.messageCollectionId == "incoming_messages"; // context.params.messageId == "134"; // ... and ... // change.after.data() == {body: "Hello"} });
Activadores de eventos
Activa una función cuando se cree un documento nuevo
Puedes usar un controlador onCreate()
con un comodín para que se active una función cada vez que se cree un documento nuevo en una colección.
Esta función de ejemplo llama a createUser
cada vez que se agrega un nuevo perfil de usuario:
Node.js
exports.createUser = functions.firestore .document('users/{userId}') .onCreate((snap, context) => { // Get an object representing the document // e.g. {'name': 'Marie', 'age': 66} const newValue = snap.data(); // access a particular field as you would any JS property const name = newValue.name; // perform desired operations ... });
Activa una función cuando se actualice un documento
También puedes usar la función onUpdate()
con un comodín para que se active una función cuando se actualice un documento. Esta función de ejemplo llama a updateUser
cada vez que un usuario cambia su perfil:
Node.js
exports.updateUser = functions.firestore .document('users/{userId}') .onUpdate((change, context) => { // Get an object representing the document // e.g. {'name': 'Marie', 'age': 66} const newValue = change.after.data(); // ...or the previous value before this update const previousValue = change.before.data(); // access a particular field as you would any JS property const name = newValue.name; // perform desired operations ... });
Activa una función cuando se borre un documento
También puedes usar la función onDelete()
con un comodín para que se active una función cuando se borre un documento. Esta función de ejemplo llama a deleteUser
cuando un usuario borra su perfil:
Node.js
exports.deleteUser = functions.firestore .document('users/{userID}') .onDelete((snap, context) => { // Get an object representing the document prior to deletion // e.g. {'name': 'Marie', 'age': 66} const deletedValue = snap.data(); // perform desired operations ... });
Activa una función para todos los cambios en un documento
Si no te importa el tipo de evento que tiene lugar, puedes usar la función onWrite()
con un comodín para detectar cualquier cambio que afecte a un documento de Cloud Firestore. Esta función de ejemplo llama a modifyUser
si se crea, actualiza o borra un usuario:
Node.js
exports.modifyUser = functions.firestore .document('users/{userID}') .onWrite((change, context) => { // Get an object with the current document value. // If the document does not exist, it has been deleted. const document = change.after.exists ? change.after.data() : null; // Get an object with the previous document value (for update or delete) const oldDocument = change.before.data(); // perform desired 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 change.before.data()
, que contiene la instantánea del documento antes de la actualización.
De manera similar, change.after.data()
contiene el estado de la instantánea del documento después de la actualización.
Node.js
exports.updateUser2 = functions.firestore .document('users/{userId}') .onUpdate((change, context) => { // Get an object representing the current document const newValue = change.after.data(); // ...or the previous value before this update const previousValue = change.before.data(); });
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 = snap.data().age; const name = snap.data()['name']; // Fetch data using built in accessor const experience = snap.get('experience');
Escribe datos
Cada invocación de función está asociada a un documento específico de tu
base de datos de Cloud Firestore. Puedes acceder a ese documento como una
DocumentReference
en la propiedad ref
de la instantánea que se mostró a tu función.
Esta DocumentReference
proviene del SDK de Cloud Firestore para Node.js que incluye métodos (como update()
, set()
y remove()
) que te permiten modificar fácilmente el documento que activó la función.
Node.js
// Listen for updates to any `user` document. exports.countNameChanges = functions.firestore .document('users/{userId}') .onUpdate((change, context) => { // Retrieve the current and previous value const data = change.after.data(); const previousData = change.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 change.after.ref.set({ name_change_count: count + 1 }, {merge: true}); });
Datos fuera del evento activador
Cloud Functions se ejecutan en un entorno de confianza, lo que significa que autorizada como una cuenta de servicio en tu proyecto. Puedes realizar operaciones de lectura y escritura con el SDK de Firebase Admin:
Node.js
const admin = require('firebase-admin');
admin.initializeApp();
const db = admin.firestore();
exports.writeToFirestore = functions.firestore
.document('some/doc')
.onWrite((change, context) => {
db.doc('some/otherdoc').set({ ... });
});
Limitaciones
Ten en cuenta las siguientes limitaciones para los activadores de Cloud Firestore de Cloud Functions:
- Cloud Functions (1ª gen.) es un requisito de una base de datos “(predeterminada)” existente en modo nativo de Firestore. No admite bases de datos con nombre de Cloud Firestore ni modo Datastore. Usa Cloud Functions (2ª gen.) para configurar eventos en esos casos.
- 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.
- Cloud Firestore en modo Datastore requiere Cloud Functions (2ª gen.). Cloud Functions (1ª gen.) no es compatible con el modo Datastore.
- 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.
- Si un evento coincidente excede el tamaño máximo de la solicitud, es posible que el evento no se entregue a Cloud Functions (1st gen).
- Los eventos que no se entregaron debido al tamaño de la solicitud se registran en los registros de la plataforma y se consideran en el uso de registros del proyecto.
- Puedes encontrar estos registros en el Explorador de registros con el mensaje “El evento no se puede entregar a Cloud Function debido a que el tamaño supera el límite de 1ª gen... de gravedad
error
”. Puedes encontrar el nombre de la función en el campofunctionName
. Si el camporeceiveTimestamp
todavía está dentro de una hora a partir de ahora, puedes inferir el contenido real del evento si lees el documento en cuestión con una instantánea antes y después de la marca de tiempo. - Para evitar esa cadencia, puedes hacer lo siguiente:
- Migra y actualiza a Cloud Functions (2ª gen.)
- Reducir el tamaño del documento
- Borra las Cloud Functions en cuestión.
- Puedes desactivar el registro con exclusiones, pero ten en cuenta que los eventos problemáticos aún no se entregarán.