如何用Cloud Function监听指定节点新增子节点并推送通知?
Firebase实时数据库Cloud Function通知推送问题排查与实现
问题描述
- 需求确认:能否通过Cloud Function监听Firebase实时数据库
Emergencies节点的子节点新增事件,并向Registered Admins下的所有用户推送通知? - 数据库结构:
- 根节点包含
Emergencies(存储紧急求助记录)和Registered Admins(存储管理员用户信息) Registered Admins下每个用户节点包含Notification(存储通知消息)和Token(存储设备推送令牌)
- 根节点包含
- 当前问题:已部署Cloud Function,但无法发送任何通知,请求排查。
用户提供的代码:
const functions = require("firebase-functions"); const admin = require("firebase-admin"); admin.initializeApp(); exports.listen = functions.database.ref("/Emergencies") .onWrite(async (change, context) => { change.after.val(); context.params.pushId; // Get the list of device notification tokens. const getDeviceTokensPromise = admin.database() .ref("/Registered Admins/{uid}/Token").once("value"); // The snapshot to the user's tokens. let tokensSnapshot; // The array containing all the user's tokens. let tokens; const results = await Promise.all([getDeviceTokensPromise]); tokensSnapshot = results[0]; // Check if there are any device tokens. if (!tokensSnapshot.hasChildren()) { return functions.logger.log( 'There are no notification tokens to send to.' ); } functions.logger.log( 'There are', tokensSnapshot.numChildren(), 'tokens to send notifications to.' ); // Notification details. const payload = { notification: { title: "New Emergency Request!", body: "Someone needs help check Emergenie App now!", } }; // Listing all tokens as an array. tokens = Object.keys(tokensSnapshot.val()); // Send notifications to all tokens. const response = await admin.messaging().sendToDevice(tokens, payload); // For each message check if there was an error. const tokensToRemove = []; response.results.forEach((result, index) => { const error = result.error; if (error) { functions.logger.error( 'Failure sending notification to', tokens[index], error ); // Cleanup the tokens who are not registered anymore. if (error.code === 'messaging/invalid-registration-token' || error.code === 'messaging/registration-token-not-registered') { tokensToRemove.push(tokensSnapshot.ref.child(tokens[index]).remove()); } } }); return Promise.all(tokensToRemove); });
问题排查与修复
你的代码存在3个核心问题,导致通知无法正常发送:
1. 监听事件选择错误
你使用onWrite监听/Emergencies根节点,这会在节点**任何写入操作(新增、修改、删除)**时触发,而你只需要监听子节点新增事件。应改用onCreate监听/Emergencies/{pushId},仅在新增紧急求助记录时触发函数,避免无效执行。
2. 令牌读取路径错误
代码中/Registered Admins/{uid}/Token的{uid}是占位符写法,实际会尝试读取名为{uid}的固定节点,而非遍历所有管理员的令牌。正确做法是读取整个Registered Admins节点,再逐个提取每个用户的Token值。
3. 令牌提取逻辑错误
原代码用Object.keys(tokensSnapshot.val())获取的是管理员ID列表,而非令牌值。需要遍历每个管理员节点,提取其Token字段的实际内容。
修正后的完整代码
const functions = require("firebase-functions"); const admin = require("firebase-admin"); admin.initializeApp(); exports.sendEmergencyNotification = functions.database.ref("/Emergencies/{pushId}") .onCreate(async (snapshot, context) => { // 读取所有管理员用户数据 const adminsSnapshot = await admin.database().ref("/Registered Admins").once("value"); if (!adminsSnapshot.exists()) { functions.logger.log("未找到任何管理员用户"); return null; } // 提取所有有效令牌 const tokens = []; adminsSnapshot.forEach(adminNode => { const token = adminNode.child("Token").val(); if (token) { tokens.push(token); } }); if (tokens.length === 0) { functions.logger.log("没有可用的设备推送令牌"); return null; } functions.logger.log("准备向", tokens.length, "个设备发送通知"); // 通知内容配置 const payload = { notification: { title: "新的紧急求助请求!", body: "有人需要帮助,请立即查看Emergenie应用!" } }; // 批量发送通知 const response = await admin.messaging().sendToDevice(tokens, payload); // 清理无效令牌 const cleanupTasks = []; response.results.forEach((result, index) => { const error = result.error; if (error) { functions.logger.error("通知发送失败,令牌:", tokens[index], "错误信息:", error); // 移除已失效的令牌 if (error.code === "messaging/invalid-registration-token" || error.code === "messaging/registration-token-not-registered") { adminsSnapshot.forEach(adminNode => { if (adminNode.child("Token").val() === tokens[index]) { cleanupTasks.push(adminNode.child("Token").remove()); } }); } } }); return Promise.all(cleanupTasks); });
额外注意事项
- 确保Cloud Function拥有
firebase.messaging.send权限,以及实时数据库的读写权限(默认部署的函数已具备基础权限)。 - 测试时可通过Firebase控制台手动向
Emergencies添加子节点,然后查看函数日志(Firebase控制台->函数->日志)排查错误。 - 确认设备令牌有效,且对应设备已正确订阅Firebase消息推送服务。
内容的提问来源于stack exchange,提问作者Timow
相关产品推荐
相关产品推荐

