С помощью Cloud Functions вы можете обрабатывать события в Firebase Realtime Database без необходимости обновления клиентского кода. Cloud Functions позволяет выполнять операции Realtime Database с полными административными правами и гарантирует, что каждое изменение в Realtime Database обрабатывается индивидуально. Вы можете вносить изменения Firebase Realtime Database через DataSnapshot или через Admin SDK .
В типичном жизненном цикле функция Firebase Realtime Database выполняет следующие действия:
- Ожидает изменений в определенном местоположении Realtime Database .
- Запускается при возникновении события и выполняет соответствующие задачи (см. раздел «Что можно делать с Cloud Functions ?» для примеров использования).
- Получает объект данных, содержащий снимок данных, хранящихся в указанном документе.
Запуск функции Realtime Database
Создавайте новые функции для событий Realtime Database с помощью functions.database . Чтобы управлять временем срабатывания функции, укажите один из обработчиков событий и путь Realtime Database где она будет прослушивать события.
Установить обработчик событий
Функции позволяют обрабатывать события Realtime Database на двух уровнях детализации: вы можете отслеживать только события создания, обновления или удаления, или же отслеживать любые изменения любого типа в заданном пути. Cloud Functions поддерживает следующие обработчики событий для Realtime Database :
-
onWrite()срабатывает при создании, обновлении или удалении данных в Realtime Database . -
onCreate()срабатывает при создании новых данных в Realtime Database . - Функция
onUpdate()срабатывает при обновлении данных в Realtime Database . - Функция
onDelete()срабатывает при удалении данных из Realtime Database .
Укажите экземпляр и путь.
Чтобы контролировать, когда и где должна запускаться ваша функция, вызовите ref(path) чтобы указать путь, и, при необходимости, укажите экземпляр Realtime Database с помощью instance('INSTANCE_NAME') . Если вы не укажете экземпляр, функция будет развернута в экземпляре Realtime Database по умолчанию для проекта Firebase. Например:
- Экземпляр Realtime Database по умолчанию:
functions.database.ref('/foo/bar') - Экземпляр с именем "my-app-db-2":
functions.database.instance('my-app-db-2').ref('/foo/bar')
Эти методы указывают вашей функции обрабатывать записи по определенному пути внутри экземпляра Realtime Database . Указания пути соответствуют всем записям, затрагивающим этот путь, включая записи, происходящие в любом месте ниже него. Если вы зададите путь для вашей функции как /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" .
Обработка данных событий
При обработке событий Realtime Database возвращаемый объект данных представляет собой 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/v1');
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 , позволяющее просмотреть данные, сохраненные в Realtime Database до события. Свойство 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);
});