Share
Embed
Firebase overview by Web Developer
Firebase is a great tool that helps you rapidly build a product starting from the MVP to test your idea and ending with global scale projects.
In this presentation, I gave a quick overview of tools that are useful for web developer in a current or future projects.
Presented at the local event brought by commit_ community.
Oleh Zasadnyy
December 14, 2017
Transcript
137 slides
-
<script src=“https://www.gstatic.com/firebasejs/4.8.0/firebase.js”> </script> <script> // Initialize Firebase // TODO: Replace
with your project's customized code snippet const config = { apiKey: "<API_KEY>", authDomain: "<PROJECT_ID>.firebaseapp.com", databaseURL: "https://<DATABASE_NAME>.firebaseio.com", storageBucket: "<BUCKET>.appspot.com", messagingSenderId: "<SENDER_ID>", }; firebase.initializeApp(config); </script> // initialize a Firebase app -
const uiConfig = { signInSuccessUrl: '<url-to-redirect-to-on-success>', signInOptions: [ // Choose
providers you want. firebase.auth.GoogleAuthProvider.PROVIDER_ID, ], }; // Initialize the FirebaseUI Widget using Firebase. const ui = new firebaseui.auth.AuthUI(firebase.auth()); // The start method will wait until the DOM is loaded. ui.start('#firebaseui-auth-container', uiConfig); // set up a sign-in screen const uiConfig = { signInSuccessUrl: '<url-to-redirect-to-on-success>', signInOptions: [ // Choose providers you want. firebase.auth.GoogleAuthProvider.PROVIDER_ID, ], }; // Initialize the FirebaseUI Widget using Firebase. const ui = new firebaseui.auth.AuthUI(firebase.auth()); // The start method will wait until the DOM is loaded. ui.start('#firebaseui-auth-container', uiConfig); -
const uiConfig = { signInSuccessUrl: '<url-to-redirect-to-on-success>', signInOptions: [ // Choose
providers you want. firebase.auth.GoogleAuthProvider.PROVIDER_ID, ], }; // Initialize the FirebaseUI Widget using Firebase. const ui = new firebaseui.auth.AuthUI(firebase.auth()); // The start method will wait until the DOM is loaded. ui.start('#firebaseui-auth-container', uiConfig); // set up a sign-in screen const uiConfig = { signInSuccessUrl: '<url-to-redirect-to-on-success>', signInOptions: [ // Choose providers you want. firebase.auth.GoogleAuthProvider.PROVIDER_ID, ], }; // Initialize the FirebaseUI Widget using Firebase. const ui = new firebaseui.auth.AuthUI(firebase.auth()); // The start method will wait until the DOM is loaded. ui.start('#firebaseui-auth-container', uiConfig); -
initApp = () => { firebase.auth() .onAuthStateChanged((user) => {...}); };
window.addEventListener('load', function () { initApp() }); // track the Auth state initApp = () => { firebase.auth() .onAuthStateChanged((user) => {...}); }; window.addEventListener('load', function () { initApp() }); -
initApp = () => { firebase.auth() .onAuthStateChanged((user) => {...}); };
window.addEventListener('load', function () { initApp() }); // track the Auth state initApp = () => { firebase.auth() .onAuthStateChanged((user) => {...}); }; window.addEventListener('load', function () { initApp() }); -
const postsRef = firebase.database().ref('posts'); // Retrieve lists of items or
listen for additions to a list. postsRef.on('child_added', (snapshot) => {...}); // Listen for changes to the items in a list. postsRef.on('child_changed', (snapshot) => {...}); // Listen for items being removed from a list. postsRef.on('child_removed', (snapshot) => {...}); // read data const postsRef = firebase.database().ref('posts'); // Retrieve lists of items or listen for additions to a list. postsRef.on('child_added', (snapshot) => {...}); // Listen for changes to the items in a list. postsRef.on('child_changed', (snapshot) => {...}); // Listen for items being removed from a list. postsRef.on('child_removed', (snapshot) => {...}); -
const postsRef = firebase.database().ref('posts'); // Retrieve lists of items or
listen for additions to a list. postsRef.on('child_added', (snapshot) => {...}); // Listen for changes to the items in a list. postsRef.on('child_changed', (snapshot) => {...}); // Listen for items being removed from a list. postsRef.on('child_removed', (snapshot) => {...}); // read data const postsRef = firebase.database().ref('posts'); // Retrieve lists of items or listen for additions to a list. postsRef.on('child_added', (snapshot) => {...}); // Listen for changes to the items in a list. postsRef.on('child_changed', (snapshot) => {...}); // Listen for items being removed from a list. postsRef.on('child_removed', (snapshot) => {...}); -
const postsRef = firebase.database().ref('posts'); // Return the entire list of
data. postsRef.on('value', (snapshot) => {...}); // Return the entire list of data ONCE. postsRef.once('value').then((snapshot) => {...}); // read data const postsRef = firebase.database().ref('posts'); // Return the entire list of data. postsRef.on('value', (snapshot) => {...}); // Return the entire list of data ONCE. postsRef.once('value').then((snapshot) => {...}); -
const postsRef = firebase.database().ref('posts'); // Return the entire list of
data. postsRef.on('value', (snapshot) => {...}); // Return the entire list of data ONCE. postsRef.once('value').then((snapshot) => {...}); // read data const postsRef = firebase.database().ref('posts'); // Return the entire list of data. postsRef.on('value', (snapshot) => {...}); // Return the entire list of data ONCE. postsRef.once('value').then((snapshot) => {...}); -
const postsRef = firebase.database().ref('posts'); // Order results by the value
of a specified child key. postsRef.orderByChild('<CHILD_KEY>').on(...); // Order results by child keys. postsRef.orderByKey().on(...); // Order results by child values. postsRef.orderByValue().on(...); *You can only use one order-by method at a time. // soA data const postsRef = firebase.database().ref('posts'); // Order results by the value of a specified child key. postsRef.orderByChild('<CHILD_KEY>').on(...); // Order results by child keys. postsRef.orderByKey().on(...); // Order results by child values. postsRef.orderByValue().on(...); *You can only use one order-by method at a time. -
const postsRef = firebase.database().ref('posts'); // Order results by the value
of a specified child key. postsRef.orderByChild('<CHILD_KEY>').on(...); // Order results by child keys. postsRef.orderByKey().on(...); // Order results by child values. postsRef.orderByValue().on(...); *You can only use one order-by method at a time. // soA data const postsRef = firebase.database().ref('posts'); // Order results by the value of a specified child key. postsRef.orderByChild('<CHILD_KEY>').on(...); // Order results by child keys. postsRef.orderByKey().on(...); // Order results by child values. postsRef.orderByValue().on(...); *You can only use one order-by method at a time. -
const postsRef = firebase.database().ref('posts'); const order = postsRef.orderByChild('<CHILD_KEY>'); // Items
to return from the beginning of the list. order.limitToFirst(NUMBER).on(..); // Items to return from the end of the list. order.limitToLast(NUMBER).on(..); // Return items greater than or equal to the specified key or value. order.startAt(VALUE).on(..); // Return items less than or equal to the specified key or value. order.endAt(VALUE).on(..); // Return items equal to the specified key or value. order.equalTo(VALUE).on(..); // Blter data const postsRef = firebase.database().ref('posts'); const order = postsRef.orderByChild('<CHILD_KEY>'); // Items to return from the beginning of the list. order.limitToFirst(NUMBER).on(..); // Items to return from the end of the list. order.limitToLast(NUMBER).on(..); // Return items greater than or equal to the specified key or value. order.startAt(VALUE).on(..); // Return items less than or equal to the specified key or value. order.endAt(VALUE).on(..); // Return items equal to the specified key or value. order.equalTo(VALUE).on(..); -
const postsRef = firebase.database().ref('posts'); const order = postsRef.orderByChild('<CHILD_KEY>'); // Items
to return from the beginning of the list. order.limitToFirst(NUMBER).on(..); // Items to return from the end of the list. order.limitToLast(NUMBER).on(..); // Return items greater than or equal to the specified key or value. order.startAt(VALUE).on(..); // Return items less than or equal to the specified key or value. order.endAt(VALUE).on(..); // Return items equal to the specified key or value. order.equalTo(VALUE).on(..); // Blter data const postsRef = firebase.database().ref('posts'); const order = postsRef.orderByChild('<CHILD_KEY>'); // Items to return from the beginning of the list. order.limitToFirst(NUMBER).on(..); // Items to return from the end of the list. order.limitToLast(NUMBER).on(..); // Return items greater than or equal to the specified key or value. order.startAt(VALUE).on(..); // Return items less than or equal to the specified key or value. order.endAt(VALUE).on(..); // Return items equal to the specified key or value. order.equalTo(VALUE).on(..); -
const postsRef = firebase.database().ref('posts'); // Generates a new child location
using a unique key. // Path will be something like '/posts/-IKo28nwJLH0Nc5XeFmj' postsRef.push({...}, () => {}); // Overwrites any data at this location and all child locations. postsRef.child('<POST_ID>').set({...}) .then(() => {}); // Updates only the referenced properties at the current location. postsRef.child('<POST_ID>').update({...}) .then(() => {}); // save data const postsRef = firebase.database().ref('posts'); // Generates a new child location using a unique key. // Path will be something like '/posts/-IKo28nwJLH0Nc5XeFmj' postsRef.push({...}, () => {}); // Overwrites any data at this location and all child locations. postsRef.child('<POST_ID>').set({...}) .then(() => {}); // Updates only the referenced properties at the current location. postsRef.child('<POST_ID>').update({...}) .then(() => {}); -
const postsRef = firebase.database().ref('posts'); // Generates a new child location
using a unique key. // Path will be something like '/posts/-IKo28nwJLH0Nc5XeFmj' postsRef.push({...}, () => {}); // Overwrites any data at this location and all child locations. postsRef.child('<POST_ID>').set({...}) .then(() => {}); // Updates only the referenced properties at the current location. postsRef.child('<POST_ID>').update({...}) .then(() => {}); // save data const postsRef = firebase.database().ref('posts'); // Generates a new child location using a unique key. // Path will be something like '/posts/-IKo28nwJLH0Nc5XeFmj' postsRef.push({...}, () => {}); // Overwrites any data at this location and all child locations. postsRef.child('<POST_ID>').set({...}) .then(() => {}); // Updates only the referenced properties at the current location. postsRef.child('<POST_ID>').update({...}) .then(() => {}); -
{ "rules": { “posts": { ".read": true, ".write": false }
} } // security rules -
{ "rules": { "posts": { ".read": true, ".write": "auth !=
null" } } } // write access if user is authenticated -
now The current time in milliseconds since Linux epoch. root
The root path as it exists before the attempted operation. newData The data as it would exist after the attempted operation. data The data as it existed before the attempted operation. $variables Used to represent ids and dynamic child keys. auth Represents an authenticated user's token payload. -
{ "rules": { "users": { "$uid": { ".write": "$uid ===
auth.uid" } } } } // write access if is user own data -
{ "rules": { "posts": { "body": { ".validate": "newData.isString() &&
newData.val().length < 140" } } } } // validate data on save -
{ "chats": { "one": { "title": "Historical Tech Pioneers", "messages":
{ "m1": { "sender": "ghopper", "message": “Hello firebase.” }, "m2": { ... }, // a very long list of messages } }, "two": { ... } } } -
// Chats contains only meta info about each conversation //
stored under the chats's unique ID "chats": { "one": { "title": "Historical Tech Pioneers", "lastMessage": "ghopper: Hello firebase.”, "timestamp": 1459361875666 }, "two": { ... }, "three": { ... } } -
// Conversation members are easily accessible // and stored by
chat conversation ID "members": { // we'll talk about indices like this below "one": { "ghopper": true, "alovelace": true, "eclarke": true }, "two": { ... }, "three": { ... } } -
// Messages are separate from data we may want to
iterate quickly // but still easily paginated and queried, and organized by chat // conversation ID "messages": { "one": { "m1": { "name": "eclarke", "message": "The relay seems to be malfunctioning.", "timestamp": 1459361875337 }, "m2": { ... }, "m3": { ... } }, "two": { ... }, "three": { ... } } -
Nested data in documents Subcollections Advantages: Easy to set up
and streamlines your data structure. Limitations: Can't run queries on nested lists. With larger or growing lists, the document also grows. Advantages: The size of the parent document doesn't change if list grows. Full query capabilities. Limitations: Can’t easily delete subcollections. PeTorm compound queries across subcollections. -
const postRef = db.collection('posts').doc('<POST_ID>'); postRef.get() .then((doc) => { if (doc.exists)
{ console.log("Document data:", doc.data()); } else { console.log("No such document!"); } }); // read data from document -
const postsRef = db.collection('posts'); postsRef.get() .then((querySnapshot) => { querySnapshot.forEach((doc) =>
{ console.log(doc.id, ' => ', doc.data()); }); }); // read data from collection -
const postsRef = db.collection('posts'); postsRef.where('author', '==', 'Oleh') postsRef.where('startsCount', '>', 100)
postsRef.where('timePosted', '>=', new Date(’01-12-2017')) // Blter data const postsRef = db.collection('posts'); postsRef.where('author', '==', 'Oleh') postsRef.where('startsCount', '>', 100) postsRef.where('timePosted', '>=', new Date(’01-12-2017')) -
const postsRef = db.collection('posts'); postsRef.where('author', '==', 'Oleh') postsRef.where('startsCount', '>', 100)
postsRef.where('timePosted', '>=', new Date(’01-12-2017')) // Blter data const postsRef = db.collection('posts'); postsRef.where('author', '==', 'Oleh') postsRef.where('startsCount', '>', 100) postsRef.where('timePosted', '>=', new Date(’01-12-2017')) -
const postsRef = db.collection('posts'); postsRef.orderBy('timePosted').limit(3) postsRef.orderBy('timePosted', 'desc').limit(3) postsRef.orderBy('timePosted').orderBy('starsCount', 'desc') postsRef.where('starsCount',
‘>=', 7).orderBy(‘starsCount').limit(3) // order data const postsRef = db.collection('posts'); postsRef.orderBy('timePosted').limit(3) postsRef.orderBy('timePosted', 'desc').limit(3) postsRef.orderBy('timePosted').orderBy('starsCount', 'desc') postsRef.where('starsCount', ‘>=', 7).orderBy(‘starsCount').limit(3) -
const postsRef = db.collection('posts'); postsRef.orderBy('timePosted').limit(3) postsRef.orderBy('timePosted', 'desc').limit(3) postsRef.orderBy('timePosted').orderBy('starsCount', 'desc') postsRef.where('starsCount',
‘>=', 7).orderBy(‘starsCount').limit(3) // order data const postsRef = db.collection('posts'); postsRef.orderBy('timePosted').limit(3) postsRef.orderBy('timePosted', 'desc').limit(3) postsRef.orderBy('timePosted').orderBy('starsCount', 'desc') postsRef.where('starsCount', ‘>=', 7).orderBy(‘starsCount').limit(3) -
const postsRef = db.collection('posts'); // Add a new document with
a generated id. postsRef.add({...}) .then(() => {}); // Overwrites any data at this location and all child locations. postsRef.doc('<POST_ID>').set({...}) .then(() => {}); // Updates only the referenced properties at the current location. postsRef.doc('<POST_ID>').update({...}) .then(() => {}); // save data const postsRef = db.collection('posts'); // Add a new document with a generated id. postsRef.add({...}) .then(() => {}); // Overwrites any data at this location and all child locations. postsRef.doc('<POST_ID>').set({...}) .then(() => {}); // Updates only the referenced properties at the current location. postsRef.doc('<POST_ID>').update({...}) .then(() => {}); -
const postsRef = db.collection('posts'); // Add a new document with
a generated id. postsRef.add({...}) .then(() => {}); // Overwrites any data at this location and all child locations. postsRef.doc('<POST_ID>').set({...}) .then(() => {}); // Updates only the referenced properties at the current location. postsRef.doc('<POST_ID>').update({...}) .then(() => {}); // save data const postsRef = db.collection('posts'); // Add a new document with a generated id. postsRef.add({...}) .then(() => {}); // Overwrites any data at this location and all child locations. postsRef.doc('<POST_ID>').set({...}) .then(() => {}); // Updates only the referenced properties at the current location. postsRef.doc('<POST_ID>').update({...}) .then(() => {}); -
service cloud.firestore { match /databases/{database}/documents { // Rules match specific
paths match /myCollection/myDocument { allow read, write: if <condition>; } // Rules can also specify a wildcard match /myCollection/{anyDocument} { allow write: if <other_condition>; } } } // restrict access -
service cloud.firestore { match /databases/{database}/documents { match /posts { allow
read; allow write: if request.auth != null; } } } // write access if user is authenticated -
service cloud.firestore { match /databases/{database}/documents { match /users/{userId} { allow
read; allow write: if request.auth.uid == userId; } } } // write access if is user own data -
service cloud.firestore { match /databases/{database}/documents { match /posts/{post} { allow
write: if request.resource.data.body is string && request.resource.data.body.length < 140; } } } // validate data on save -
service cloud.firestore { match /databases/{database}/documents { match /posts/{post} { function
canSavePost() { return request.resource.data.body is string && request.resource.data.body.length < 140; } allow write: if canSavePost() ; } } } // validate data on save service cloud.firestore { match /databases/{database}/documents { match /posts/{post} { function canSavePost() { return request.resource.data.body is string && request.resource.data.body.length < 140; } allow write: if canSavePost() ; } } } -
service cloud.firestore { match /databases/{database}/documents { match /posts/{post} { function
canSavePost() { return request.resource.data.body is string && request.resource.data.body.length < 140; } allow write: if canSavePost() ; } } } // validate data on save service cloud.firestore { match /databases/{database}/documents { match /posts/{post} { function canSavePost() { return request.resource.data.body is string && request.resource.data.body.length < 140; } allow write: if canSavePost() ; } } } -
Realtime DB Firestore Data is harder to organize at scale.
Queries are deep by default: They always return the entire subtree. Write data as an individual operation. Easier to organize at scale (subcollections in documents). Query subcollections within a document instead of an entire collection. Batch operations and complete them atomically. -
Realtime DB Firestore Read and write rules cascade. Offline support
for mobile clients on iOS and Android only. Stability you'd expect from a battle-tested, tried-and-true product. Rules don't cascade unless you use a wildcard. Offline support for iOS, Android, and web clients. Stability in a beta product is not always the same as that of a fully launched product. -
If you're comfortable with a product in beta, use Cloud
Firestore for your new projects. - Firebase team -
"hosting": { // Add the "rewrites" section within "hosting" "rewrites":
[ { "source": "**", "destination": "/index.html" } ] } // Brebase.json -
// Create a root reference const storageRef = firebase.storage().ref(); //
Create a reference to 'images/mountains.jpg' const mountainImagesRef = storageRef.child('images/mountains.jpg'); mountainImagesRef.put(file) .then((snapshot) => {...}); // upload Ble -
const uploadTask = storageRef.child('mountains.jpg').put(file); // Pause the upload uploadTask.pause(); //
Resume the upload uploadTask.resume(); // Cancel the upload uploadTask.cancel(); // control upload -
const uploadTask = storageRef.child('mountains.jpg').put(file); // 1. 'state_changed' observer, called any
time the state changes // 2. Error observer, called on failure // 3. Completion observer, called on successful completion uploadTask.on('state_changed', (snap) => { const progress = (snap.bytesTransferred / snap.totalBytes) * 100; }, (error) => { // Handle unsuccessful uploads }, () => { // Handle successful uploads on complete const downloadURL = uploadTask.snapshot.downloadURL; }); // listen to changes const uploadTask = storageRef.child('mountains.jpg').put(file); // 1. 'state_changed' observer, called any time the state changes // 2. Error observer, called on failure // 3. Completion observer, called on successful completion uploadTask.on('state_changed', (snap) => { const progress = (snap.bytesTransferred / snap.totalBytes) * 100; }, (error) => { // Handle unsuccessful uploads }, () => { // Handle successful uploads on complete const downloadURL = uploadTask.snapshot.downloadURL; }); -
const uploadTask = storageRef.child('mountains.jpg').put(file); // 1. 'state_changed' observer, called any
time the state changes // 2. Error observer, called on failure // 3. Completion observer, called on successful completion uploadTask.on('state_changed', (snap) => { const progress = (snap.bytesTransferred / snap.totalBytes) * 100; }, (error) => { // Handle unsuccessful uploads }, () => { // Handle successful uploads on complete const downloadURL = uploadTask.snapshot.downloadURL; }); // listen to changes const uploadTask = storageRef.child('mountains.jpg').put(file); // 1. 'state_changed' observer, called any time the state changes // 2. Error observer, called on failure // 3. Completion observer, called on successful completion uploadTask.on('state_changed', (snap) => { const progress = (snap.bytesTransferred / snap.totalBytes) * 100; }, (error) => { // Handle unsuccessful uploads }, () => { // Handle successful uploads on complete const downloadURL = uploadTask.snapshot.downloadURL; }); -
// The Cloud Functions for Firebase SDK. const functions =
require('firebase-functions'); // The Firebase Admin SDK to access the Firebase Realtime Database. const admin = require('firebase-admin'); admin.initializeApp(functions.config().firebase); // index.js -
functions.database.ref('/messages/{pushId}/original') .onWrite(event => { // Grab the value of what
was written to the Realtime Database. const original = event.data.val(); console.log('Uppercasing', event.params.pushId, original); const uppercase = original.toUpperCase(); // Setting an "uppercase" sibling in the Realtime Database. return event.data.ref.parent.child('uppercase').set(uppercase); }); // uppercase message functions.database.ref('/messages/{pushId}/original') .onWrite(event => { // Grab the value of what was written to the Realtime Database. const original = event.data.val(); console.log('Uppercasing', event.params.pushId, original); const uppercase = original.toUpperCase(); // Setting an "uppercase" sibling in the Realtime Database. return event.data.ref.parent.child('uppercase').set(uppercase); }); -
functions.database.ref('/messages/{pushId}/original') .onWrite(event => { // Grab the value of what
was written to the Realtime Database. const original = event.data.val(); console.log('Uppercasing', event.params.pushId, original); const uppercase = original.toUpperCase(); // Setting an "uppercase" sibling in the Realtime Database. return event.data.ref.parent.child('uppercase').set(uppercase); }); // uppercase message functions.database.ref('/messages/{pushId}/original') .onWrite(event => { // Grab the value of what was written to the Realtime Database. const original = event.data.val(); console.log('Uppercasing', event.params.pushId, original); const uppercase = original.toUpperCase(); // Setting an "uppercase" sibling in the Realtime Database. return event.data.ref.parent.child('uppercase').set(uppercase); }); -
functions.database.ref('<PATH>') .onWrite(event => {...}) .onCreate(event => {...}) .onUpdate(event => {...})
.onDelete(event => {...}) // real-time db triggers -
functions.firestore.document('<PATH>') .onWrite(event => {...}) .onCreate(event => {...}) .onUpdate(event => {...})
.onDelete(event => {...}) // Brestore triggers -
// Retrieve Firebase Messaging object. const messaging = firebase.messaging(); messaging.requestPermission()
.then(() => { console.log('Notification permission granted.'); // ... }); // client index.js -
messaging.onMessage((payload) => { console.log("Message received. ", payload); // ... });
// handle incoming messages -
messaging.setBackgroundMessageHandler((payload) => { console.log('Received background message ', payload); // Customize
notification here const notificationTitle = 'Background Message Title'; const notificationOptions = { body: 'Background Message body.', icon: '/firebase-logo.png' }; return self.registration.showNotification(notificationTitle, notificationOptions); }); // Brebase-messaging-sw.js messaging.setBackgroundMessageHandler((payload) => { console.log('Received background message ', payload); // Customize notification here const notificationTitle = 'Background Message Title'; const notificationOptions = { body: 'Background Message body.', icon: '/firebase-logo.png' }; return self.registration.showNotification(notificationTitle, notificationOptions); }); -
messaging.setBackgroundMessageHandler((payload) => { console.log('Received background message ', payload); // Customize
notification here const notificationTitle = 'Background Message Title'; const notificationOptions = { body: 'Background Message body.', icon: '/firebase-logo.png' }; return self.registration.showNotification(notificationTitle, notificationOptions); }); // Brebase-messaging-sw.js messaging.setBackgroundMessageHandler((payload) => { console.log('Received background message ', payload); // Customize notification here const notificationTitle = 'Background Message Title'; const notificationOptions = { body: 'Background Message body.', icon: '/firebase-logo.png' }; return self.registration.showNotification(notificationTitle, notificationOptions); }); -
/functions-project-12345 /users /Uid-12345 displayName: "Bob Dole" /notificationTokens 1234567890: true photoURL:
"https://lh3.googleusercontent.com/..." -
/functions-project-12345 /followers /followedUid-12345 followerUid-67890: true /users /Uid-12345 displayName: "Bob Dole"
/notificationTokens 1234567890: true photoURL: "https://lh3.googleusercontent.com/..." -
functions.database.ref('/followers/{followedUid}/{followerUid}') .onWrite(event => { const followerUid = event.params.followerUid; const followedUid
= event.params.followedUid; // Get the list of device notification tokens. const deviceTokensPromise = admin.database() .ref(`/users/${followedUid}/notificationTokens`).once('value'); // Get the follower profile. const followerProfilePromise = admin.auth().getUser(followerUid); // ... return admin.messaging().sendToDevice(tokens, payload); }); // Brebase function index.js functions.database.ref('/followers/{followedUid}/{followerUid}') .onWrite(event => { const followerUid = event.params.followerUid; const followedUid = event.params.followedUid; // Get the list of device notification tokens. const deviceTokensPromise = admin.database() .ref(`/users/${followedUid}/notificationTokens`).once('value'); // Get the follower profile. const followerProfilePromise = admin.auth().getUser(followerUid); // ... return admin.messaging().sendToDevice(tokens, payload); }); -
functions.database.ref('/followers/{followedUid}/{followerUid}') .onWrite(event => { const followerUid = event.params.followerUid; const followedUid
= event.params.followedUid; // Get the list of device notification tokens. const deviceTokensPromise = admin.database() .ref(`/users/${followedUid}/notificationTokens`).once('value'); // Get the follower profile. const followerProfilePromise = admin.auth().getUser(followerUid); // ... return admin.messaging().sendToDevice(tokens, payload); }); // Brebase function index.js functions.database.ref('/followers/{followedUid}/{followerUid}') .onWrite(event => { const followerUid = event.params.followerUid; const followedUid = event.params.followedUid; // Get the list of device notification tokens. const deviceTokensPromise = admin.database() .ref(`/users/${followedUid}/notificationTokens`).once('value'); // Get the follower profile. const followerProfilePromise = admin.auth().getUser(followerUid); // ... return admin.messaging().sendToDevice(tokens, payload); });