С помощью облачных функций вы можете обрабатывать события в базе данных Firebase Realtime без необходимости обновления клиентского кода. Облачные функции позволяют выполнять операции с базой данных в реальном времени с полными правами администратора и гарантируют, что каждое изменение в базе данных в реальном времени обрабатывается отдельно. Вы можете вносить изменения в базу данных Firebase Realtime с помощью DataSnapshot
или с помощью Admin SDK .
В типичном жизненном цикле функция базы данных Firebase Realtime выполняет следующие действия:
- Ожидает изменений в определенном местоположении базы данных реального времени.
- Запускается, когда происходит событие, и выполняет свои задачи (примеры использования см. в разделе Что можно делать с облачными функциями? ).
- Получает объект данных, содержащий моментальный снимок данных, хранящихся в указанном документе.
Активировать функцию базы данных в реальном времени
Создайте новые функции для событий базы данных реального времени с помощью functions.database
. Чтобы управлять запуском функции, укажите один из обработчиков событий и укажите путь к базе данных реального времени, где он будет прослушивать события.
Установить обработчик события
Функции позволяют обрабатывать события базы данных реального времени на двух уровнях специфичности; вы можете прослушивать только события создания, обновления или удаления, или вы можете прослушивать любое изменение пути любого рода. Cloud Functions поддерживает следующие обработчики событий для базы данных реального времени:
-
onWrite()
, который срабатывает при создании, обновлении или удалении данных в базе данных реального времени. -
onCreate()
, который срабатывает при создании новых данных в базе данных реального времени. -
onUpdate()
, который срабатывает при обновлении данных в базе данных реального времени. -
onDelete()
, который срабатывает при удалении данных из базы данных реального времени.
Укажите экземпляр и путь
Чтобы контролировать, когда и где должна срабатывать ваша функция, вызовите ref(path)
для указания пути и, при необходимости, укажите экземпляр базы данных реального времени с помощью instance('INSTANCE_NAME')
. Если вы не укажете экземпляр, функция будет развернута в экземпляре базы данных реального времени по умолчанию для проекта Firebase. Например:
- Экземпляр базы данных реального времени по умолчанию:
functions.database.ref('/foo/bar')
- Экземпляр с именем «my-app-db-2»:
functions.database.instance('my-app-db-2').ref('/foo/bar')
Эти методы направляют вашу функцию для обработки записей по определенному пути в экземпляре базы данных реального времени. Спецификации пути соответствуют всем записям, которые касаются пути, включая записи, которые происходят в любом месте ниже этого пути. Если вы установите путь для своей функции как /foo/bar
, он будет соответствовать событиям в обоих этих местах:
/foo/bar
/foo/bar/baz/really/deep/path
В любом случае Firebase интерпретирует событие как событие /foo/bar
, а данные события включают старые и новые данные в /foo/bar
. Если данные события могут быть большими, рассмотрите возможность использования нескольких функций на более глубоких путях вместо одной функции рядом с корнем вашей базы данных. Для наилучшей производительности запрашивайте данные только на самом глубоком возможном уровне.
Вы можете указать компонент пути как подстановочный знак, заключив его в фигурные скобки; ref('foo/{bar}')
соответствует любому потомку /foo
. Значения этих компонентов пути с подстановочными знаками доступны в объекте EventContext.params
вашей функции. В этом примере значение доступно как context.params.bar
.
Пути с подстановочными знаками могут соответствовать нескольким событиям из одной записи. Вставка из
{
"foo": {
"hello": "world",
"firebase": "functions"
}
}
соответствует пути "/foo/{bar}"
дважды: один раз с "hello": "world"
и снова с "firebase": "functions"
.
Обрабатывать данные о событиях
При обработке события базы данных реального времени возвращаемый объект данных представляет собой DataSnapshot
. Для событий onWrite
или onUpdate
первым параметром является объект Change
, содержащий два моментальных снимка, представляющих состояние данных до и после инициирующего события. Для событий onCreate
и onDelete
возвращаемый объект данных представляет собой снимок созданных или удаленных данных.
В этом примере функция извлекает снимок для указанного пути, преобразует строку в этом месте в верхний регистр и записывает измененную строку в базу данных:
// Listens for new messages added to /messages/:pushId/original and creates an // uppercase version of the message to /messages/:pushId/uppercase exports.makeUppercase = functions.database.ref('/messages/{pushId}/original') .onCreate((snapshot, context) => { // Grab the current value of what was written to the Realtime Database. const original = snapshot.val(); functions.logger.log('Uppercasing', context.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 snapshot.ref.parent.child('uppercase').set(uppercase); });
Доступ к информации об аутентификации пользователя
Из EventContext.auth
и EventContext.authType
вы можете получить доступ к информации о пользователе, включая разрешения, для пользователя, который активировал функцию. Это может быть полезно для обеспечения соблюдения правил безопасности, позволяя вашей функции выполнять различные операции в зависимости от уровня разрешений пользователя:
const functions = require('firebase-functions');
const admin = require('firebase-admin');
exports.simpleDbFunction = functions.database.ref('/path')
.onCreate((snap, context) => {
if (context.authType === 'ADMIN') {
// do something
} else if (context.authType === 'USER') {
console.log(snap.val(), 'written by', context.auth.uid);
}
});
Кроме того, вы можете использовать информацию об аутентификации пользователя, чтобы «олицетворять» пользователя и выполнять операции записи от имени пользователя. Обязательно удалите экземпляр приложения, как показано ниже, чтобы предотвратить проблемы параллелизма:
exports.impersonateMakeUpperCase = functions.database.ref('/messages/{pushId}/original')
.onCreate((snap, context) => {
const appOptions = JSON.parse(process.env.FIREBASE_CONFIG);
appOptions.databaseAuthVariableOverride = context.auth;
const app = admin.initializeApp(appOptions, 'app');
const uppercase = snap.val().toUpperCase();
const ref = snap.ref.parent.child('uppercase');
const deleteApp = () => app.delete().catch(() => null);
return app.database().ref(ref).set(uppercase).then(res => {
// Deleting the app is necessary for preventing concurrency leaks
return deleteApp().then(() => res);
}).catch(err => {
return deleteApp().then(() => Promise.reject(err));
});
});
Чтение предыдущего значения
Объект Change
имеет свойство before
, которое позволяет проверить, что было сохранено в базе данных реального времени до события. Свойство before
возвращает DataSnapshot
, в котором все методы (например, val()
и exists()
) ссылаются на предыдущее значение. Вы можете снова прочитать новое значение, используя исходный DataSnapshot
или прочитав свойство after
. Это свойство любого Change
представляет собой еще один DataSnapshot
, представляющий состояние данных после того, как произошло событие.
Например, свойство before
можно использовать для того, чтобы при первом создании функция выводила текст только в верхний регистр:
exports.makeUppercase = functions.database.ref('/messages/{pushId}/original')
.onWrite((change, context) => {
// Only edit data when it is first created.
if (change.before.exists()) {
return null;
}
// Exit when the data is deleted.
if (!change.after.exists()) {
return null;
}
// Grab the current value of what was written to the Realtime Database.
const original = change.after.val();
console.log('Uppercasing', context.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 change.after.ref.parent.child('uppercase').set(uppercase);
});