Node.js后端多Firebase项目初始化失败问题及解决方案
多Firebase项目应用信息API搭建问题与解决方案
问题背景
我在多个Firebase项目下拥有大量应用,希望获取这些应用的信息以便后续更新,因此决定用Node.js搭建API,实现从不同项目中获取信息(例如统计特定项目的应用数量)。已下载每个项目的Service Account密钥,核心实现逻辑如下:
原实现代码
控制器代码
const { initProject } = require('../config/firebaseConfig') async function getAllApps(req, res) { const projectId = req.params.projectId; const project = await initProject(projectId); try { androidApps = await project.projectManagement().listAndroidApps(); iosApps = await project.projectManagement().listIosApps(); const allApps = [...androidApps, ...iosApps]; const appList = allApps.map((app) => ({ appId: app.appId })); res.status(200).json(appList); } catch (error) { console.error('Error retrieving apps:', error); res.status(500).json({ error: 'Failed to retrieve apps for the project' }); } }
初始化项目函数(initProject)
const admin = require('firebase-admin'); // Initialize a project cache const projectCache = new Map(); //initialize the project based on it's id name async function initProject(projectId) { // Check if the project has already been initialized if (projectCache.has(projectId)) { return projectCache.get(projectId); } try { // Construct the path to the service account key file using the provided path or a default value if (process.env.FIREBASE_ACCOUNT_SERVICE_PATH !== null) { var serviceAccountPath = `${process.env.FIREBASE_ACCOUNT_SERVICE_PATH}\\${projectId}.json`; } else { var serviceAccountPath = `C:\\path\\to\\cred\\${projectId}.json`; } // Initialize the Firebase Admin SDK const serviceAccount = require(serviceAccountPath); admin.initializeApp({ credential: admin.credential.cert(serviceAccount), databaseURL: `https://${projectId}.firebaseio.com`, }); // Get the initialized Firebase project const firebaseProject = admin; // Cache the initialized project projectCache.set(projectId, firebaseProject); return firebaseProject; } catch (error) { console.error('Error initializing Firebase project: ', error); throw new Error('Failed to initialize Firebase project'); } } module.exports = { initProject }
路由配置
router.get('/:projectId/apps', projectController.getAllApps);
问题现象
启动Express后,首次调用http://localhost:99/projects/project-name/apps可成功获取数据,但切换为其他项目(如http://localhost:99/projects/project-name-2/apps)时调用失败,原因是未指定应用实例就初始化了firebase-admin。尝试过调整require时机、工厂函数等多种方式均未解决,虽已缓存项目避免重复初始化,但跨项目调用仍异常。
解决方案
samthecodingman提供的方案完美解决问题,调整后的完整代码如下:
调整后的控制器代码
// Should return the list of apps id for this project async function getAllApps(req, res) { const projectId = req.params.projectId; const project = getFirebaseAdminForProject(projectId); const projectManagement = getProjectManagement(project); try { androidApps = await projectManagement.listAndroidApps(); iosApps = await projectManagement.listIosApps(); const allApps = [...androidApps, ...iosApps]; const appList = allApps.map((app) => ({ appId: app.appId })); res.status(200).json(appList); } catch (error) { console.error('Error retrieving apps:', error); res.status(500).json({ error: 'Failed to retrieve apps for the project' }); } }
核心工具函数
const { initializeApp, getApps, cert } = require('firebase-admin/app'); function getFirebaseAdminForProject(projectId) { if (!projectId) throw new Error('Project ID is required!'); let projectApp = getApps().find(app => app.name === projectId); if (projectApp) return projectApp; // if here, project is not yet initialized let serviceAccountPath = process.env.FIREBASE_ACCOUNT_SERVICE_PATH != null ? `${process.env.FIREBASE_ACCOUNT_SERVICE_PATH}\\${projectId}.json` : `C:\\path\\to\\${projectId}.json`; // Initialize the Firebase Admin SDK const serviceAccount = require(serviceAccountPath); return initializeApp({ credential: cert(serviceAccount), databaseURL: `https://${projectId}.firebaseio.com` }, projectId); // <-- using projectId as instance name } module.exports = { getFirebaseAdminForProject };
关键说明
- 无需再用Map缓存项目,firebase-admin内部已实现缓存机制
- 核心要点是理解firebase-admin中的App即对应项目实例,初始化时需传入
projectId作为实例名称,以此区分不同项目 - 修复了原代码中
!== null的判断问题,改用!= null更合理(可同时判断null和undefined)
内容的提问来源于stack exchange,提问作者Joao Victor
相关产品推荐
相关产品推荐

