Con Cloud Functions, puedes implementar código para administrar eventos que se activan con los cambios en tu base de datos de Cloud Firestore. Esto te permite agregar con facilidad 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.). Para obtener más información sobre la 1ª gen., consulta Amplía Cloud Firestore con Cloud Functions.
Activadores de la función Cloud Firestore
El SDK de Cloud Functions for Firebase exporta los siguientes activadores de eventos de Cloud Firestore que te permiten crear controladores vinculados a eventos específicos de Cloud Firestore:
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 (vista previa)
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 |
Solo se activan los eventos Cloud Firestore cuando hay cambios en los documentos. La actualización de un documento de Cloud Firestore 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
Define un activador de función
Para definir un activador de Cloud Firestore, especifica una ruta de documento y un tipo de evento:
Node.js
import {
onDocumentWritten,
onDocumentCreated,
onDocumentUpdated,
onDocumentDeleted,
Change,
FirestoreEvent
} from "firebase-functions/v2/firestore";
exports.myfunction = onDocumentWritten("my-collection/{docId}", (event) => {
/* ... */
});
Python (vista previa)
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
import {
onDocumentWritten,
Change,
FirestoreEvent
} from "firebase-functions/v2/firestore";
exports.myfunction = onDocumentWritten("users/marie", (event) => {
// Your code here
});
Python (vista previa)
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
import {
onDocumentWritten,
Change,
FirestoreEvent
} from "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 (vista previa)
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
.
Si un documento de users
tiene subcolecciones y se modifica un campo en uno
de los documentos de las subcolecciones, no se activa el comodín userId
.
Las coincidencias de comodines se extraen de la ruta del documento y se almacenan en event.params
.
Puedes definir tantos comodines como desees para sustituir los IDs explícitos de colección o de documento, por ejemplo:
Node.js
import {
onDocumentWritten,
Change,
FirestoreEvent
} from "firebase-functions/v2/firestore";
exports.myfunction = onDocumentWritten("users/{userId}/{messageCollectionId}/{messageId}", (event) => {
// If we set `/users/marie/incoming_messages/134` to {body: "Hello"} then
// event.params.userId == "marie";
// event.params.messageCollectionId == "incoming_messages";
// event.params.messageId == "134";
// ... and ...
// event.data.after.data() == {body: "Hello"}
});
Python (vista previa)
from firebase_functions.firestore_fn import (
on_document_written,
Event,
Change,
DocumentSnapshot,
)
@on_document_written(document="users/{userId}/{messageCollectionId}/{messageId}")
def myfunction(event: Event[Change[DocumentSnapshot]]) -> None:
# If we set `/users/marie/incoming_messages/134` to {body: "Hello"} then
event.params["userId"] == "marie" # True
event.params["messageCollectionId"] == "incoming_messages" # True
event.params["messageId"] == "134" # True
# ... and ...
event.data.after.to_dict() == {"body": "Hello"}
Los activadores siempre deben apuntar a un documento, incluso si usas un comodín.
Por ejemplo, users/{userId}/{messageCollectionId}
no es válido porque {messageCollectionId}
es una colección. Sin embargo, users/{userId}/{messageCollectionId}/{messageId}
sí
es válido porque {messageId}
siempre apunta a un documento.
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
import {
onDocumentCreated,
Change,
FirestoreEvent
} from "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 (vista previa)
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
import {
onDocumentUpdated,
Change,
FirestoreEvent
} from "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 (vista previa)
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
import {
onDocumentDeleted,
Change,
FirestoreEvent
} from "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 (vista previa)
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 el activador de eventos “documento escrito”. Esta función de ejemplo se activa si se crea, actualiza o borra un usuario:
Node.js
import {
onDocumentWritten,
Change,
FirestoreEvent
} from "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 (vista previa)
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 (vista previa)
@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 (vista previa)
# 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 la base de datos de Cloud Firestore. 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
import { onDocumentUpdated } from "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 data.after.ref.set({
name_change_count: count + 1
}, {merge: true});
});
Python (vista previa)
@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 (vista previa)
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
import { onDocumentWrittenWithAuthContext } from "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 (vista previa)
@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 (vista previa)
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
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.