firebase / firebase-tools

The Firebase Command Line Tools
MIT License
3.97k stars 916 forks source link

Next.js webframework: Adding a client-side firebase dependency leads to a 500 #7224

Open nidegen opened 1 month ago

nidegen commented 1 month ago

I have a Next.js page hosted on firebase. It has one SSR page that loads firebase admin SDK as

export const getServerSideProps: GetServerSideProps = async ({..}) => {
  console.log("Init Firebase admin App");
  if (admin.apps.length == 0) {
    admin.initializeApp();
  }

and everything works fine.

As soon as I add "firebase": "^10.8.0", to package.json, the firebase-deployed site crashes during SSR with

The default Firebase app does not exist. Make sure you call initializeApp() before using ...

However, the logs show that Init Firebase admin App was logged before the crash.

Any ideas what could cause it?

Here an MRE. Running it locally with Next.js works fine, but after deploying it to firebase, / works, but /invite/1234 crashes with a 500 error.

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", // Removing this line fixes everything
    "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>My App</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>
    </>
  );
}
aalej commented 1 month ago

Hey @nidegen, thanks for sharing code snippets. Looking through the Google Cloud Function logs while trying to replicate this, it seems like a Firebase app with the name of firebase-frameworks was initialized. In your pages/invite/[id].tsx file, could you try changing:

  if (admin.apps.length == 0) {
    admin.initializeApp();
  }
  const host = req.headers.host;
  const projectId = admin.instanceId().app.options.projectId;

To

 const ADMIN_APP_NAME = "firebase-frameworks";
 const adminApp =
   admin.apps.find((app) => app?.name === ADMIN_APP_NAME) ||
   admin.initializeApp(
     {
       credential: admin.credential.applicationDefault(),
     },
     ADMIN_APP_NAME
   );

 const host = req.headers.host;
 const projectId = admin.instanceId(adminApp).app.options.projectId;

After deploying, is a 500 error still being raised when visiting /invite/1234?