Node.js+Express下FCM推送后存储通知到MySQL失效问题
问题场景
- 基于Node.js + Express开发服务端,对接Firebase Cloud Messaging向App用户推送通知
- 业务逻辑要求:推送发送完成后,将通知ID、标题、内容存入MySQL的notifications表
- 故障表现:插入逻辑不执行,回调参数
rows为undefined,数据库无对应新增数据 - 原有问题代码:
const message_notification = { notification: { title: "notification_title", body: "notification_body" } }; const notification_options = { priority: "high", timeToLive: 60 * 60 * 24 }; admin.messaging().sendToDevice(device_token, message_notification, notification_options) .then( response => { //doesn't work mySqlConnection.query(`insert into notifications (notification_id, body, title) values (?,?,?)`, [response.multicastId, message_notification.notification.body, message_notification.notification.title], (err,rows)=> { try { console.log(rows); } catch(err) { console.log(err) } }); })
故障根因
- MySQL回调逻辑写反:
mysql驱动的查询回调中,错误参数err永远是第一个参数,只要SQL执行失败,err会携带具体错误详情,此时rows必然为undefined。你的代码既没有优先判断err,还用try/catch包裹同步的console.log逻辑,根本捕获不到SQL执行的异步错误,完全看不到真实报错原因。常见的SQL执行失败原因包括:- 数据库连接断开、连接权限不足
- notifications表不存在、字段名/字段类型不匹配
- notification_id字段主键冲突
- SQL语法错误
- Promise链异常被吞:你没有给
admin.messaging().sendToDevice的Promise加.catch()分支,推送阶段的错误(比如device_token无效、Firebase SDK初始化失败、网络超时、参数不合法)会被完全静默,没有任何日志输出。 - 缺少推送结果校验:
sendToDevice支持批量推送,即使请求整体成功返回response,也可能存在部分/全部token推送失败的情况,直接拿multicastId写库可能产生无效数据。
修复后可运行代码
const message_notification = { notification: { title: "notification_title", body: "notification_body" } }; const notification_options = { priority: "high", timeToLive: 60 * 60 * 24 }; admin.messaging().sendToDevice(device_token, message_notification, notification_options) .then(response => { // 先校验推送结果,记录失败的token if (response.failureCount > 0) { const failedList = []; response.responses.forEach((item, index) => { if (!item.success) { failedList.push({ token: Array.isArray(device_token) ? device_token[index] : device_token, errorMsg: item.error?.message || '未知推送错误' }); } }); console.error('存在推送失败的设备:', failedList); } // 执行入库操作,优先处理错误 mySqlConnection.query( `INSERT INTO notifications (notification_id, body, title) VALUES (?,?,?)`, [response.multicastId, message_notification.notification.body, message_notification.notification.title], (err, rows) => { if (err) { console.error('通知入库失败,错误详情:', err); return; } console.log('通知入库成功,插入返回结果:', rows); } ); }) .catch(err => { // 捕获推送阶段的全局异常 console.error('Firebase推送请求失败:', err); });
修复要点
- 所有MySQL查询回调必须先判断
err是否存在,存在则直接打印错误、终止后续逻辑,不要直接操作rows/results参数 - 异步Promise链必须加catch分支,不要静默吞掉任何阶段的错误,否则排查问题时没有任何日志依据
- 去掉回调里无意义的try/catch:mysql驱动已经把异步错误通过第一个err参数返回,外层包裹try/catch只能捕获回调内同步代码的报错,捕获不到SQL执行的异步错误
- 批量推送场景必须校验
failureCount,及时清理无效的device_token,减少无效推送请求
内容的提问来源于stack exchange,提问作者node101
相关产品推荐
相关产品推荐

