ด้วย Cloud Functions คุณสามารถจัดการเหตุการณ์ใน Firebase Realtime Database ได้โดยไม่จำเป็นต้องอัปเดตโค้ดไคลเอ็นต์ Cloud Functions ช่วยให้คุณเรียกใช้การดำเนินการฐานข้อมูลเรียลไทม์ด้วยสิทธิ์ผู้ดูแลระบบเต็มรูปแบบ และรับรองว่าการเปลี่ยนแปลงฐานข้อมูลเรียลไทม์แต่ละรายการจะได้รับการประมวลผลแยกกัน คุณสามารถทำการเปลี่ยนแปลง Firebase Realtime Database ผ่าน DataSnapshot
หรือผ่าน Admin SDK
ในวงจรการใช้งานทั่วไป ฟังก์ชัน Firebase Realtime Database จะดำเนินการดังต่อไปนี้:
- รอการเปลี่ยนแปลงตำแหน่งฐานข้อมูลเรียลไทม์เฉพาะ
- ทริกเกอร์เมื่อมีเหตุการณ์เกิดขึ้นและดำเนินงานของมัน (ดู ฉันจะทำอะไรกับฟังก์ชันคลาวด์ได้บ้าง สำหรับตัวอย่างกรณีการใช้งาน)
- รับออบเจ็กต์ข้อมูลที่มีสแน็ปช็อตของข้อมูลที่จัดเก็บไว้ในเอกสารที่ระบุ
ทริกเกอร์ฟังก์ชันฐานข้อมูลเรียลไทม์
สร้างฟังก์ชั่นใหม่สำหรับเหตุการณ์ฐานข้อมูลเรียลไทม์ด้วย functions.database
หากต้องการควบคุมเวลาที่ฟังก์ชันทริกเกอร์ ให้ระบุหนึ่งในตัวจัดการเหตุการณ์ และระบุเส้นทางฐานข้อมูลเรียลไทม์ที่จะรับฟังเหตุการณ์
ตั้งค่าตัวจัดการเหตุการณ์
ฟังก์ชันช่วยให้คุณจัดการเหตุการณ์ฐานข้อมูลเรียลไทม์ได้ในระดับความจำเพาะสองระดับ คุณสามารถฟังเฉพาะเหตุการณ์การสร้าง อัปเดต หรือการลบเท่านั้น หรือคุณสามารถฟังการเปลี่ยนแปลงใดๆ ในเส้นทางได้ Cloud Functions รองรับตัวจัดการเหตุการณ์เหล่านี้สำหรับฐานข้อมูลเรียลไทม์:
-
onWrite()
ซึ่งจะทริกเกอร์เมื่อมีการสร้าง อัปเดต หรือลบข้อมูลใน Realtime Database -
onCreate()
ซึ่งจะทริกเกอร์เมื่อมีการสร้างข้อมูลใหม่ใน Realtime Database -
onUpdate()
ซึ่งจะทริกเกอร์เมื่อมีการอัพเดตข้อมูลใน Realtime Database -
onDelete()
ซึ่งจะทริกเกอร์เมื่อมีการลบข้อมูลออกจาก Realtime Database
ระบุอินสแตนซ์และเส้นทาง
หากต้องการควบคุมเวลาและตำแหน่งที่ฟังก์ชันของคุณควรทริกเกอร์ ให้เรียก 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"
จัดการข้อมูลเหตุการณ์
เมื่อจัดการเหตุการณ์ 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');
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);
});