添加firebase包导致部署在Firebase的Next.js SSR崩溃
问题描述
我有一个部署在Firebase上的Next.js项目,包含一个SSR页面,原本通过以下代码加载firebase-admin SDK运行正常:
export const getServerSideProps: GetServerSideProps = async ({..}) => { console.log("Init Firebase admin App"); if (admin.apps.length == 0) { admin.initializeApp(); }
但在package.json中添加"firebase": "^10.8.0"依赖后,部署到Firebase的站点在SSR过程中崩溃,报错:
The default Firebase app does not exist. Make sure you call initializeApp() before using ...
日志显示崩溃前已经打印了Init Firebase admin App。
补充最小复现示例(MRE):本地运行Next.js一切正常,但部署到Firebase后,根路径/可正常访问,/invite/1234会崩溃返回500错误。
相关代码如下:
package.json
{ "name": "echo-photos", "version": "0.0.1", "private": true, "scripts": { "dev": "next dev", "build": "next build", "start": "next start", "lint": "next lint" }, "dependencies": { "eslint-config-next": "^14.1.0", "firebase": "^10.8.1", // 移除这一行后问题解决 "firebase-admin": "^11.4.1", "next": "^14.1.0", "react": "^18.2.0", "react-hooks-global-state": "^2.0.0", "typescript": "^4.9.5" }, "devDependencies": { "@next/swc-darwin-arm64": "^13.4.2", "@types/node": "18.11.9", "@types/react": "^18.2.8", "@types/react-dom": "^18.2.19", "@types/react-router-dom": "^5.3.3", "autoprefixer": "^10.4.13", "postcss": "^8.4.21", "tailwindcss": "^3.2.7" } }
pages/_app.tsx
import type { AppProps } from "next/app"; import Head from "next/head"; import "../styles/style.css"; function App({ Component, pageProps }: AppProps) { return ( <div> <Head> <title>Echo Photos</title> </Head> <Component {...pageProps} /> </div> ); } export default App; // pages/index.tsx export default function HomePage() { return ( <> Hello Home </> ); }
pages/invite/[id].tsx
import Head from "next/head"; import { useRouter } from "next/router"; import { GetServerSideProps } from "next"; import admin from "firebase-admin"; interface PropsData { domain: string; } export const getServerSideProps: GetServerSideProps = async ({ req, }) => { if (admin.apps.length == 0) { admin.initializeApp(); } const host = req.headers.host; const projectId = admin.instanceId().app.options.projectId; let domain = `https://${projectId}.web.app`; if (host?.includes("localhost") || host?.includes("127.0.0.1")) { domain = "https://echo-photos-dev.web.app"; } let propsData: PropsData = { domain: domain, }; return { props: propsData, }; }; export default function InvitePage(props: PropsData) { const router = useRouter(); const fullInviteId = router.query.id as string; const inviteCode = fullInviteId.substring(0, 8); return ( <> <Head> <title> Title Example </title> </Head> <section> Hello "{props.domain}" with code {inviteCode} </section> </> ); }
原因分析与解决方案
核心原因
这是**firebase客户端SDK和firebase-admin服务端SDK在Firebase托管环境中的命名空间冲突**导致的:
- 同时安装两个SDK后,Firebase托管环境会优先加载客户端SDK的全局变量,导致服务端代码中导入的
firebase-admin对象或其内部实例检测逻辑被意外干扰。 - 虽然代码执行了
initializeApp(),但由于冲突,初始化的实例并未正确绑定到admin对象,后续调用admin.instanceId()时就会提示默认应用不存在。 - 本地开发环境无此问题,是因为Next.js本地服务端运行环境和Firebase托管的隔离机制不同,不会触发这种冲突。
解决方案
方案1:抽离服务端SDK初始化逻辑
将firebase-admin的初始化单独放在服务端专用文件中,确保仅在服务端加载:
- 创建
lib/firebase-admin.ts:
import admin from "firebase-admin"; // 确保只初始化一次 if (!admin.apps.length) { admin.initializeApp(); } export const adminDb = admin;
- 在
pages/invite/[id].tsx中替换导入:
// 替换原有的import admin from "firebase-admin" import { adminDb } from "../../lib/firebase-admin"; export const getServerSideProps: GetServerSideProps = async ({ req }) => { // 使用adminDb替代admin const projectId = adminDb.instanceId().app.options.projectId; // ...其他逻辑保持不变 };
方案2:动态导入服务端SDK
在getServerSideProps中动态导入firebase-admin,避免客户端打包时包含它,绕开命名空间冲突:
export const getServerSideProps: GetServerSideProps = async ({ req }) => { // 动态导入确保仅在服务端加载 const admin = await import("firebase-admin"); if (!admin.apps.length) { admin.initializeApp(); } const host = req.headers.host; const projectId = admin.instanceId().app.options.projectId; // ...其他逻辑不变 };
方案3:标记客户端SDK为服务端外部依赖(不推荐)
如果不需要在服务端使用客户端SDK,可通过next.config.js配置将firebase标记为服务端外部依赖:
/** @type {import('next').NextConfig} */ const nextConfig = { serverComponentsExternalPackages: ['firebase'], } module.exports = nextConfig
此方案稳定性不如前两种,优先推荐方案1或2。
内容的提问来源于stack exchange,提问作者Nicolas Degen
相关产品推荐
相关产品推荐

