ด้วย 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 ได้ 2 ระดับความเฉพาะเจาะจง โดยคุณสามารถเลือกฟังเหตุการณ์การสร้าง การอัปเดต หรือการลบโดยเฉพาะ หรือจะเลือกฟังการเปลี่ยนแปลงทุกประเภทในเส้นทางก็ได้ Cloud Functions รองรับตัวแฮนเดิลเหตุการณ์ต่อไปนี้สำหรับ Realtime Database
onWrite()ซึ่งจะทริกเกอร์เมื่อมีการสร้าง อัปเดต หรือลบข้อมูลใน Realtime DatabaseonCreate()ซึ่งจะทริกเกอร์เมื่อมีการสร้างข้อมูลใหม่ใน Realtime DatabaseonUpdate()ซึ่งจะทริกเกอร์เมื่อมีการอัปเดตข้อมูลใน Realtime DatabaseonDelete()ซึ่งจะทริกเกอร์เมื่อมีการลบข้อมูลออกจาก Realtime Database
ระบุอินสแตนซ์และเส้นทาง
หากต้องการควบคุมเวลาและตำแหน่งที่ฟังก์ชันควรทริกเกอร์ ให้เรียกใช้ ref(path)
เพื่อระบุเส้นทาง และระบุอินสแตนซ์ Realtime Database ด้วย instance('INSTANCE_NAME') (ไม่บังคับ)
หากไม่ระบุอินสแตนซ์ ฟังก์ชันจะติดตั้งใช้งานในอินสแตนซ์เริ่มต้นสำหรับโปรเจ็กต์ Firebase เช่นRealtime Database
- อินสแตนซ์เริ่มต้น: 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}" 2 ครั้ง ได้แก่ ครั้งหนึ่งด้วย "hello": "world"
และอีกครั้งด้วย "firebase": "functions"
จัดการข้อมูลเหตุการณ์
เมื่อจัดการเหตุการณ์ Realtime Database ออบเจ็กต์ข้อมูลที่แสดงผลจะเป็น
DataSnapshot
สำหรับเหตุการณ์ onWrite หรือ onUpdate พารามิเตอร์แรกจะเป็นออบเจ็กต์ Change ที่มีสแนปชอต 2 รายการซึ่งแสดงสถานะข้อมูลก่อนและหลังเหตุการณ์ทริกเกอร์ สำหรับเหตุการณ์ 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);
});