С помощью Cloud Functions вы можете обрабатывать события в Firebase Realtime Database без необходимости обновлять клиентский код. Cloud Functions позволяют выполнять операции Realtime Database с полными административными привилегиями и гарантируют, что каждое изменение в Realtime Database обрабатывается индивидуально. Вы можете вносить изменения Firebase Realtime Database с помощью снимка данных или с помощью Admin SDK.
В типичном жизненном цикле функция Firebase Realtime Database выполняет следующие действия:
- Ожидает изменений в определенном пути Realtime Database .
- Запускается при возникновении события и выполняет свои задачи.
- Получает объект данных, содержащий снимок данных, хранящихся по этому пути.
Вы можете вызвать функцию в ответ на запись, создание, обновление или удаление узлов базы данных в Firebase Realtime Database . Чтобы контролировать время срабатывания функции, укажите один из обработчиков событий и укажите путь Realtime Database , где она будет прослушивать события.
Установка местоположения функции
Расстояние между расположением экземпляра Realtime Database и расположением функции может привести к значительной задержке в сети. Кроме того, несоответствие между регионами может привести к сбою развертывания. Чтобы избежать подобных ситуаций, укажите расположение функции так, чтобы оно совпадало с расположением экземпляра базы данных .
Обработка событий Realtime Database
Функции позволяют обрабатывать события Realtime Database на двух уровнях специфичности; вы можете прослушивать только события записи, создания, обновления или удаления или вы можете прослушивать любые изменения в ссылке.
Доступны следующие обработчики для реагирования на события Realtime Database :
-
onValueWritten()
Вызывается, когда данные создаются, обновляются или удаляются в Realtime Database . -
onValueCreated()
срабатывает только при создании данных в Realtime Database . -
onValueUpdated()
срабатывает только при обновлении данных в Realtime Database . -
onValueDeleted()
Срабатывает только при удалении данных в Realtime Database .
-
on_value_written()
Вызывается, когда данные создаются, обновляются или удаляются в Realtime Database . -
on_value_created()
Срабатывает только при создании данных в Realtime Database . -
on_value_updated()
Срабатывает только при обновлении данных в Realtime Database . -
on_value_deleted()
Срабатывает только при удалении данных в Realtime Database .
Импортируйте необходимые модули
В источнике функции вы должны импортировать модули SDK, которые хотите использовать. Для этого примера необходимо импортировать модули HTTP и Realtime Database вместе с модулем Firebase Admin SDK для записи в Realtime Database .
// The Cloud Functions for Firebase SDK to setup triggers and logging.
const {onRequest} = require("firebase-functions/v2/https");
const {onValueCreated} = require("firebase-functions/v2/database");
const {logger} = require("firebase-functions");
// The Firebase Admin SDK to access the Firebase Realtime Database.
const admin = require("firebase-admin");
admin.initializeApp();
# The Cloud Functions for Firebase SDK to create Cloud Functions and set up triggers.
from firebase_functions import db_fn, https_fn
# The Firebase Admin SDK to access the Firebase Realtime Database.
from firebase_admin import initialize_app, db
app = initialize_app()
Укажите экземпляр и путь
Чтобы контролировать, когда и где должна срабатывать ваша функция, настройте для своей функции путь и, при необходимости, экземпляр Realtime Database . Если вы не укажете экземпляр, функция прослушивает все экземпляры Realtime Database в области функции. Вы также можете указать шаблон экземпляра Realtime Database для развертывания в выборочном подмножестве экземпляров в одном регионе.
Например:
// All Realtime Database instances in default function region us-central1 at path "/user/{uid}" // There must be at least one Realtime Database present in us-central1. const onWrittenFunctionDefault = onValueWritten("/user/{uid}", (event) => { // … }); // Instance named "my-app-db-2", at path "/user/{uid}". // The "my-app-db-2" instance must exist in this region. const OnWrittenFunctionInstance = onValueWritten( { ref: "/user/{uid}", instance: "my-app-db-2" // This example assumes us-central1, but to set location: // region: "europe-west1" }, (event) => { // … } ); // Instance with "my-app-db-" prefix, at path "/user/{uid}", where uid ends with @gmail.com. // There must be at least one Realtime Database with "my-app-db-*" prefix in this region. const onWrittenFunctionInstance = onValueWritten( { ref: "/user/{uid=*@gmail.com}", instance: "my-app-db-*" // This example assumes us-central1, but to set location: // region: "europe-west1" }, (event) => { // … } );
# All Realtime Database instances in default function region us-central1 at path "/user/{uid}"
# There must be at least one Realtime Database present in us-central1.
@db_fn.on_value_written(r"/user/{uid}")
def onwrittenfunctiondefault(event: db_fn.Event[db_fn.Change]):
# ...
pass
# Instance named "my-app-db-2", at path "/user/{uid}".
# The "my-app-db-2" instance must exist in this region.
@db_fn.on_value_written(
reference=r"/user/{uid}",
instance="my-app-db-2",
# This example assumes us-central1, but to set location:
# region="europe-west1",
)
def on_written_function_instance(event: db_fn.Event[db_fn.Change]):
# ...
pass
# Instance with "my-app-db-" prefix, at path "/user/{uid}", where uid ends with @gmail.com.
# There must be at least one Realtime Database with "my-app-db-*" prefix in this region.
@db_fn.on_value_written(
reference=r"/user/{uid=*@gmail.com}",
instance="my-app-db-*",
# This example assumes us-central1, but to set location:
# region="europe-west1",
)
def on_written_function_instance(event: db_fn.Event[db_fn.Change]):
# ...
pass
Эти параметры предписывают вашей функции обрабатывать записи по определенному пути в экземпляре Realtime Database .
Спецификации пути соответствуют всем операциям записи, касающимся пути, включая записи, происходящие где-либо ниже него. Если вы установите путь для своей функции как /foo/bar
, он будет соответствовать событиям в обоих этих местах:
/foo/bar
/foo/bar/baz/really/deep/path
В любом случае Firebase интерпретирует, что событие происходит в /foo/bar
, а данные события включают старые и новые данные в /foo/bar
. Если данные о событиях могут быть большими, рассмотрите возможность использования нескольких функций на более глубоких путях вместо одной функции в корне вашей базы данных. Для достижения наилучшей производительности запрашивайте данные только на самом глубоком уровне.
Подстановочные знаки и захват
Вы можете использовать {key}
, {key=*}
, {key=prefix*}
, {key=*suffix}
для захвата. *
, prefix*
, *suffix
для односегментного подстановочного знака. Примечание. **
представляет собой многосегментный подстановочный знак, который Realtime Database не поддерживает. См. раздел Понимание шаблонов путей .
Подстановочные знаки пути. Вы можете указать компонент пути в качестве подстановочного знака:
- Используя звездочку,
*
. Например,foo/*
соответствует любому дочернему узлу на один уровень иерархии узлов нижеfoo/
. - Используя сегмент, содержащий ровно звездочку,
*
. Например,foo/app*-us
соответствует любым дочерним сегментам нижеfoo/
с префиксомapp
и суффиксом-us
.
Пути с подстановочными знаками могут соответствовать нескольким событиям, например, одной записи. Вставка из
{
"foo": {
"hello": "world",
"firebase": "functions"
}
}
дважды соответствует пути "/foo/*"
: один раз с "hello": "world"
и снова с "firebase": "functions"
.
Захват пути. Вы можете записать совпадения путей в именованные переменные, которые будут использоваться в коде функции (например /user/{uid}
, /user/{uid=*-us}
).
Значения переменных захвата доступны в объекте data.DatabaseEvent.params вашей функции.
Подстановочные знаки экземпляра. Вы также можете указать компонент экземпляра, используя подстановочные знаки. Подстановочный знак экземпляра может иметь префикс, суффикс или и то, и другое (например, my-app-*-prod
).
Подстановочный знак и ссылка на захват
При использовании Cloud Functions (2-го поколения) и Realtime Database при указании ref
и instance
можно использовать шаблон. Каждый интерфейс триггера будет иметь следующие параметры для определения функции:
Указание ref | Указание instance | Поведение |
---|---|---|
Одиночный ( /foo/bar ) | Не указывая | Охватывает обработчик всеми экземплярами в области функции. |
Одиночный ( /foo/bar ) | Сингл ( 'my-new-db' ) | Привязывает обработчик к конкретному экземпляру в области функции. |
Одиночный ( /foo/bar ) | Шаблон ( 'inst-prefix*' ) | Охватывает обработчик всеми экземплярами, которые соответствуют шаблону в области функции. |
Шаблон ( /foo/{bar} ) | Не указывая | Охватывает обработчик всеми экземплярами в области функции. |
Шаблон ( /foo/{bar} ) | Сингл ( 'my-new-db' ) | Привязывает обработчик к конкретному экземпляру в области функции. |
Шаблон ( /foo/{bar} ) | Шаблон ( 'inst-prefix*' ) | Охватывает обработчик всеми экземплярами, которые соответствуют шаблону в области функции. |
Обработка данных о событиях
Когда срабатывает событие Realtime Database , оно передает объект Event
в вашу функцию-обработчик. У этого объекта есть свойство data
, которое для событий создания и удаления содержит снимок созданных или удаленных данных.
В этом примере функция извлекает данные по указанному пути, преобразует строку в этом месте в верхний регистр и записывает эту измененную строку в базу данных:
// Listens for new messages added to /messages/:pushId/original and creates an
// uppercase version of the message to /messages/:pushId/uppercase
// for all databases in 'us-central1'
exports.makeuppercase = onValueCreated(
"/messages/{pushId}/original",
(event) => {
// Grab the current value of what was written to the Realtime Database.
const original = event.data.val();
logger.log("Uppercasing", event.params.pushId, original);
const uppercase = original.toUpperCase();
// You must return a Promise when performing
// asynchronous tasks inside a function, such as
// writing to the Firebase Realtime Database.
// Setting an "uppercase" sibling in the
// Realtime Database returns a Promise.
return event.data.ref.parent.child("uppercase").set(uppercase);
},
);
@db_fn.on_value_created(reference="/messages/{pushId}/original")
def makeuppercase(event: db_fn.Event[Any]) -> None:
"""Listens for new messages added to /messages/{pushId}/original and
creates an uppercase version of the message to /messages/{pushId}/uppercase
"""
# Grab the value that was written to the Realtime Database.
original = event.data
if not isinstance(original, str):
print(f"Not a string: {event.reference}")
return
# Use the Admin SDK to set an "uppercase" sibling.
print(f"Uppercasing {event.params['pushId']}: {original}")
upper = original.upper()
parent = db.reference(event.reference).parent
if parent is None:
print("Message can't be root node.")
return
parent.child("uppercase").set(upper)
Чтение предыдущего значения
Для событий write
или update
свойство data
представляет собой объект Change
, содержащий два моментальных снимка, представляющих состояние данных до и после инициирующего события. Объект Change
имеет свойство before
, которое позволяет вам проверять, что было сохранено в Realtime Database до события, и свойство after
, которое представляет состояние данных после того, как событие произошло.
Например, свойство before
можно использовать, чтобы гарантировать, что функция будет отображать текст только в верхнем регистре при его первом создании:
exports makeUppercase = onValueWritten("/messages/{pushId}/original", (event) => { // Only edit data when it is first created. if (event.data.before.exists()) { return null; } // Exit when the data is deleted. if (!event.data.after.exists()) { return null; } // Grab the current value of what was written to the Realtime Database. const original = event.data.after.val(); console.log('Uppercasing', event.params.pushId, original); const uppercase = original.toUpperCase(); // You must return a Promise when performing asynchronous tasks inside a Functions such as // writing to the Firebase Realtime Database. // Setting an "uppercase" sibling in the Realtime Database returns a Promise. return event.data.after.ref.parent.child('uppercase').set(uppercase); });
@db_fn.on_value_written(reference="/messages/{pushId}/original")
def makeuppercase2(event: db_fn.Event[db_fn.Change]) -> None:
"""Listens for new messages added to /messages/{pushId}/original and
creates an uppercase version of the message to /messages/{pushId}/uppercase
"""
# Only edit data when it is first created.
if event.data.before is not None:
return
# Exit when the data is deleted.
if event.data.after is None:
return
# Grab the value that was written to the Realtime Database.
original = event.data.after
if not hasattr(original, "upper"):
print(f"Not a string: {event.reference}")
return
# Use the Admin SDK to set an "uppercase" sibling.
print(f"Uppercasing {event.params['pushId']}: {original}")
upper = original.upper()
parent = db.reference(event.reference).parent
if parent is None:
print("Message can't be root node.")
return
parent.child("uppercase").set(upper)