Cloud Functions จะช่วยให้คุณจัดการเหตุการณ์ในฐานข้อมูลเรียลไทม์ของ Firebase ได้โดยไม่ต้องอัปเดตรหัสไคลเอ็นต์
Cloud Functions ช่วยให้คุณเรียกใช้การดำเนินการ Realtime Database ด้วยสิทธิ์การดูแลระบบแบบเต็ม และมั่นใจได้ว่าการเปลี่ยนแปลงแต่ละรายการใน Realtime Database จะได้รับการประมวลผลแยกกัน คุณเปลี่ยนแปลงฐานข้อมูลเรียลไทม์ของ Firebase ได้ผ่านทาง DataSnapshot
หรือผ่าน Admin SDK
ในวงจรทั่วไป ฟังก์ชันฐานข้อมูลเรียลไทม์ของ Firebase จะทำสิ่งต่อไปนี้
- รอให้มีการเปลี่ยนแปลงตำแหน่งของ 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')
(ไม่บังคับ) หากคุณไม่ระบุอินสแตนซ์ ฟังก์ชันจะใช้งานได้กับอินสแตนซ์ 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
ระบบจะจับคู่กับเหตุการณ์ที่ 2 ตำแหน่งต่อไปนี้
/foo/bar
/foo/bar/baz/really/deep/path
ในทั้ง 2 กรณี 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');
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);
});