Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

500 server error Dynamic Page Routes Next js 15 #8244

Open
JwillSDP opened this issue Feb 21, 2025 · 7 comments
Open

500 server error Dynamic Page Routes Next js 15 #8244

JwillSDP opened this issue Feb 21, 2025 · 7 comments

Comments

@JwillSDP
Copy link

JwillSDP commented Feb 21, 2025

<!-- DO NOT DELETE
validate_template=true
template_path=.github/ISSUE_TEMPLATE/bug_report.md
-->

[REQUIRED] Environment info

firebase-tools: 13.31.1

Platform: Windows

[REQUIRED] Test case

I am using Next js 15
The issues is getting data from firebase into server comps on dynamic pages

I can get the slug from the URL but if I use a server action to get any data from firebase, It will show 500 Internal Server Error only in PRODUCTION

This 500 Internal Server Error is not seen in the development and firebase emulator.

I cant get firebase data into a dynamic page routes without getting a 500 error from the server in PRODUCTION

Static routes are able to get data from Firebase without the 500 Error

Using App router in NEXT JS 15

// app/meettheteam/[id]/page.tsx (Server Component)

import type { Coach } from '@/types';
import { getCoachById } from '@/lib/coaches-data';
import CoachComp from './CoachComp';
import { Suspense } from 'react';
import { notFound } from 'next/navigation';

async function CoachPage({ params }: { params: Promise<{ id: string }> }) {
  const id = (await params).id;

  try {
    const coach: Coach | null = await getCoachById(id);

    if (!coach) {
      notFound();
      return;
    }

    return (
      <>
        <Suspense fallback={<p>Loading coach details...</p>}>
          <CoachComp coach={coach} />
        </Suspense>
      </>
    );
  } catch (error) {
    console.error('Error fetching coach:', error);
    return <div>Error loading coach. Please try again later.</div>;
  }
}

export default CoachPage;

'use server';

// lib/coaches-data.ts

import { DocumentData } from 'firebase/firestore';

import { db } from '@/config/firebaseServer';

const getCoaches = async () => {
  try {
    const snapshot = await db.collection('coaches').get();

    if (snapshot.empty) {
      console.log('No matching documents.');
      return;
    }

    const coachesDocs: DocumentData[] = snapshot.docs;

    const coaches = coachesDocs.map((doc) => ({
      id: doc.id,
      ...doc.data(),
    }));

    return coaches;
  } catch (error) {
    console.error('Error fetching coachs:', error);
    throw error;
  }
};

const getCoachById = async (id: string) => {
  try {
    const coachDoc: DocumentData = await db.collection('coaches').doc(id).get();

    if (coachDoc.exists) {
      const coach = coachDoc.data();

      coach.id = coachDoc.id;

      return coach;
    } else {
      console.log('Coach not found with ID:', id);
      return null;
    }
  } catch (error) {
    console.error('Error in getCoachById:', error);
    throw error;
  }
};

export { getCoaches, getCoachById };


// config/firebaseServer.tsx
import { initializeApp, getApps } from 'firebase-admin/app';
import { getAuth } from 'firebase-admin/auth';
import { getFirestore } from 'firebase-admin/firestore';
import { getStorage } from 'firebase-admin/storage';
import { credential } from 'firebase-admin';

function initializeFirebaseAdmin() {
  const projectId = process.env.FIREBASE_PROJECT_ID;
  const storageBucket = process.env.FIREBASE_STORAGE_BUCKET;

  if (!projectId || !storageBucket) {
    // Check for these
    console.error(
      'Missing required Firebase environment variables (projectId or storageBucket).',
    );
    throw new Error(
      'Missing required Firebase environment variables (projectId or storageBucket).',
    );
  }

  try {
    const serviceAccount = require('../firebase.json'); // Directly require the JSON

    if (getApps().length === 0) {
      initializeApp({
        credential: credential.cert(serviceAccount),
        projectId,
        storageBucket,
      });

      console.log('Firebase Admin initialized successfully.');
    } else {
      console.log('Using existing Firebase Admin app.');
    }
  } catch (error) {
    console.error('Error initializing Firebase Admin:', error);
    throw error;
  }
}


initializeFirebaseAdmin();

const admin = getAuth();
const db = getFirestore();
const storage = getStorage();

export { admin, db, storage };

[REQUIRED] Steps to reproduce

Create Next JS app
Create dynamic page routes in app folder
use the dynamic slug as ID for a document in a collection
Create server action to get doc from collection into a server comp on dynamic page route
Pass data to client comp for display

[REQUIRED] Expected behavior

dynamic routes use the slugs as ID for a document in a collection

Server comp on dynamic page uses slug for ID to get data from server action using firebase

Data passed into client comp

Data displayed in client comp

[REQUIRED] Actual behavior

500 Internal Server Error

@JwillSDP JwillSDP changed the title 500 server error Dynamic Routes Next js 15 500 server error Dynamic Page Routes Next js 15 Feb 22, 2025
@emwp
Copy link
Contributor

emwp commented Mar 10, 2025

Thanks for reporting this issue!

I believe the problem is with your Firebase Admin initialization.

There is a tree-shaking issue. When your app is built for production, webpack is tree-shaking the Firebase Admin dependencies out of the bundle because of how you've structured your initialization.

The most straightforward fix is to modify your firebaseServer.tsx file to initialize Firebase Admin at the top level of the module, i've reproduced and the code below seems to work:

import * as firebase from "firebase-admin";
import { getAuth } from "firebase-admin/auth";
import { getStorage } from "firebase-admin/storage";

firebase.initializeApp({
  credential: firebase.credential.cert(require("./firebase.json")),
  projectId: process.env.FIREBASE_PROJECT_ID,
  storageBucket: process.env.FIREBASE_STORAGE_BUCKET,
});

const admin = getAuth();
const db = firebase.firestore();
const storage = getStorage();

export { admin, db, storage };

Also ideally make sure you initialize the firestore passing the app instance, when using the getFirestore function.

const db = getFirestore(firebase.app());

@emwp emwp closed this as completed Mar 10, 2025
@leoortizz leoortizz reopened this Mar 10, 2025
@leoortizz leoortizz added the Needs: Author Feedback Issues awaiting author feedback label Mar 10, 2025
@JwillSDP
Copy link
Author

JwillSDP commented Mar 16, 2025

@emwp @leoortizz
It seems that the ssr Next JS function is giving an error of the app not initializing. It works in dev and emulator but not on production. The 500 error only happens on dynamic pages. I am not sure why that is? Static pages are working.

Here is my Server Admin:

// config/firebaseServer.tsx
import { cert, getApp, getApps, initializeApp } from 'firebase-admin/app';
import { getAuth } from 'firebase-admin/auth';
import { getFirestore } from 'firebase-admin/firestore';
import { getStorage } from 'firebase-admin/storage';

const serviceAccount = require('@/v12elites-firebase-adminsdk.json');

const adminApp =
  getApps().length > 0
    ? getApp()
    : initializeApp({
        credential: cert(serviceAccount),
      });

const auth = getAuth(adminApp);
const db = getFirestore(adminApp);
const storage = getStorage(adminApp);

export { auth, db, storage };

Here is my Client Admin:

// config/firebaseClient.tsx
import { initializeApp, getApp, getApps } from 'firebase/app';
import { getAuth } from 'firebase/auth';
import { getFirestore } from 'firebase/firestore';
import { getStorage } from 'firebase/storage';
import { getFunctions } from 'firebase/functions';
import { getMessaging, isSupported as isMessagingSupported } from 'firebase/messaging';
import { getAnalytics, isSupported as isAnalyticsSupported } from 'firebase/analytics';

// FIREBASE CONFIGURATION

const firebaseConfig = {
  apiKey: process.env.NEXT_PUBLIC_FIREBASE_API_KEY,
  authDomain: process.env.NEXT_PUBLIC_FIREBASE_AUTH_DOMAIN,
  projectId: process.env.NEXT_PUBLIC_FIREBASE_PROJECT_ID,
  storageBucket: process.env.NEXT_PUBLIC_FIREBASE_STORAGE_BUCKET,
  messagingSenderId: process.env.NEXT_PUBLIC_FIREBASE_MESSAGING_SENDER_ID,
  appId: process.env.NEXT_PUBLIC_FIREBASE_APP_ID,
  measurementId: process.env.NEXT_PUBLIC_FIREBASE_MEASUREMENT_ID,
};

// APP INITIALIZATION

const app = getApps().length === 0 ? initializeApp(firebaseConfig) : getApp();

// APP SDK SERVICES

const auth = getAuth(app);

const db = getFirestore(app);
const storage = getStorage(app);
const functions = getFunctions(app);
const analytics = isAnalyticsSupported().then((yes) => (yes ? getAnalytics(app) : null));
const messaging = isMessagingSupported().then((yes) => (yes ? getMessaging(app) : null));

// APP EXPORTS

export { auth, db, storage, analytics, messaging, functions };

When I look into the SSR next function log, I see an error of:

Error: The default Firebase app does not exist. Make sure you call initializeApp() before using any of the Firebase services.

My package.json:

{
  "name": "next-app-template",
  "version": "0.0.1",
  "private": true,
  "scripts": {
    "dev": "next dev --turbopack",
    "build": "next build",
    "start": "next start",
    "lint": "eslint . --ext .ts,.tsx -c .eslintrc.json --fix",
    "format": "prettier --check './**/*.{js,jsx,ts,tsx,css,md,json}' --config ./.prettierrc",
    "format:fix": "prettier --write './**/*.{js,jsx,ts,tsx,css,md,json}' --config ./.prettierrc"
  },
  "dependencies": {
    "@heroui/button": "2.2.9",
    "@heroui/code": "2.2.6",
    "@heroui/input": "2.4.9",
    "@heroui/kbd": "2.2.6",
    "@heroui/link": "2.2.7",
    "@heroui/listbox": "2.3.9",
    "@heroui/navbar": "2.2.8",
    "@heroui/react": "^2.6.14",
    "@heroui/snippet": "2.2.10",
    "@heroui/switch": "2.2.8",
    "@heroui/system": "2.4.6",
    "@heroui/theme": "2.4.5",
    "@react-aria/ssr": "3.9.7",
    "@react-aria/visually-hidden": "3.8.18",
    "clsx": "2.1.1",
    "eslint-plugin-tailwindcss": "^3.18.0",
    "firebase": "^11.4.0",
    "firebase-admin": "^12.7.0",
    "firebase-functions": "^6.3.2",
    "firebase-tools": "^13.34.0",
    "framer-motion": "11.13.1",
    "google-auth-library": "^9.15.0",
    "googleapis": "^144.0.0",
    "intl-messageformat": "^10.5.0",
    "next": "^15.2.0-canary.57",
    "next-themes": "^0.4.4",
    "prettier-plugin-tailwindcss": "^0.6.11",
    "react": "18.3.1",
    "react-dom": "18.3.1",
    "react-error-boundary": "^5.0.0",
    "usehooks-ts": "^3.1.0"
  },
  "devDependencies": {
    "@next/eslint-plugin-next": "^15.0.4",
    "@react-types/shared": "3.25.0",
    "@types/node": "20.5.7",
    "@types/react": "18.3.3",
    "@types/react-dom": "18.3.0",
    "@typescript-eslint/eslint-plugin": "^8.11.0",
    "@typescript-eslint/parser": "^8.11.0",
    "autoprefixer": "10.4.19",
    "eslint": "^8.57.0",
    "eslint-config-next": "15.0.4",
    "eslint-config-prettier": "^9.1.0",
    "eslint-plugin-import": "^2.26.0",
    "eslint-plugin-jsx-a11y": "^6.4.1",
    "eslint-plugin-node": "^11.1.0",
    "eslint-plugin-prettier": "^5.2.1",
    "eslint-plugin-react": "^7.23.2",
    "eslint-plugin-react-hooks": "^4.6.0",
    "eslint-plugin-unused-imports": "4.1.4",
    "postcss": "8.4.49",
    "prettier": "3.3.3",
    "tailwind-variants": "0.1.20",
    "tailwindcss": "3.4.16",
    "typescript": "5.6.3"
  }
}

@google-oss-bot google-oss-bot added Needs: Attention and removed Needs: Author Feedback Issues awaiting author feedback labels Mar 16, 2025
@JwillSDP
Copy link
Author

JwillSDP commented Mar 17, 2025

@emwp
I also tried this for my server:

// config/firebaseServer.ts
import * as firebase from 'firebase-admin';
import { App } from 'firebase-admin/app';
import { Firestore } from 'firebase-admin/firestore';
import { Auth } from 'firebase-admin/auth';
import { Storage } from 'firebase-admin/storage';

const serviceAccount: {} = JSON.parse(process.env.FIREBASE_ADMIN_CREDENTIALS || '{}');

const app: App =
  firebase.apps.length > 0
    ? firebase.app()
    : firebase.initializeApp({
        credential: firebase.credential.cert(serviceAccount),
        projectId: process.env.FIREBASE_PROJECT_ID,
        storageBucket: process.env.FIREBASE_STORAGE_BUCKET,
      });

const auth: Auth = firebase.auth(app);
const db: Firestore = firebase.firestore(app);
const storage: Storage = firebase.storage(app);

export { auth, db, storage };

I get this error logged in the ssr function:

Error: The default Firebase app does not exist. Make sure you call initializeApp() before using any of the Firebase services.

@emwp
Copy link
Contributor

emwp commented Mar 18, 2025

Hey @JwillSDP, in those examples you're still caliing firebase.initializeApp conditionally.
That is why I suggested calling it a the top level on your file, that way, webpack won't tree-shake it out of the final bundle.

Calling it like this should resolve the problem:

import * as firebase from "firebase-admin";
import { getAuth } from "firebase-admin/auth";
import { getStorage } from "firebase-admin/storage";

firebase.initializeApp({
  credential: firebase.credential.cert(require("./firebase.json")),
  projectId: process.env.FIREBASE_PROJECT_ID,
  storageBucket: process.env.FIREBASE_STORAGE_BUCKET,
});

const admin = getAuth();
const db = firebase.firestore();
const storage = getStorage();

export { admin, db, storage };

@JwillSDP
Copy link
Author

JwillSDP commented Mar 20, 2025

@emwp

I got this to work but ONLY in Production:

// config/firebaseServer.ts
import * as admin from 'firebase-admin';

const serviceAccount: {} = JSON.parse(process.env.FIREBASE_ADMIN_CREDENTIALS || '{}');

admin.initializeApp({
  credential: admin.credential.cert(serviceAccount),
  projectId: process.env.FIREBASE_PROJECT_ID,
  storageBucket: process.env.FIREBASE_STORAGE_BUCKET,
});

const auth: admin.auth.Auth = admin.auth();
const db: admin.firestore.Firestore = admin.firestore();
const storage: admin.storage.Storage = admin.storage();

export { auth, db, storage };

I have to use the conditional to make it work in development. What is that about?

// config/firebaseServer.ts
import * as admin from 'firebase-admin';

const serviceAccount: {} = JSON.parse(process.env.FIREBASE_ADMIN_CREDENTIALS || '{}');

if (!admin.apps.length) {
  admin.initializeApp({
    credential: admin.credential.cert(serviceAccount),
    projectId: process.env.FIREBASE_PROJECT_ID,
    storageBucket: process.env.FIREBASE_STORAGE_BUCKET,
  });
}

const auth: admin.auth.Auth = admin.auth();
const db: admin.firestore.Firestore = admin.firestore();
const storage: admin.storage.Storage = admin.storage();

export { auth, db, storage };

I hope that the client SDK will still work in production. I need it for the tokens to the server.

// config/firebaseClient.ts
import { initializeApp, getApp, getApps } from 'firebase/app';
import { getAuth } from 'firebase/auth';
import { getFirestore } from 'firebase/firestore';
import { getStorage } from 'firebase/storage';
import { getFunctions } from 'firebase/functions';
import { getMessaging, isSupported as isMessagingSupported } from 'firebase/messaging';
import { getAnalytics, isSupported as isAnalyticsSupported } from 'firebase/analytics';

// FIREBASE CONFIGURATION

const firebaseConfig = {
  apiKey: process.env.NEXT_PUBLIC_FIREBASE_API_KEY,
  authDomain: process.env.NEXT_PUBLIC_FIREBASE_AUTH_DOMAIN,
  projectId: process.env.NEXT_PUBLIC_FIREBASE_PROJECT_ID,
  storageBucket: process.env.NEXT_PUBLIC_FIREBASE_STORAGE_BUCKET,
  messagingSenderId: process.env.NEXT_PUBLIC_FIREBASE_MESSAGING_SENDER_ID,
  appId: process.env.NEXT_PUBLIC_FIREBASE_APP_ID,
  measurementId: process.env.NEXT_PUBLIC_FIREBASE_MEASUREMENT_ID,
};

// APP INITIALIZATION

const app = getApps().length === 0 ? initializeApp(firebaseConfig) : getApp();

// APP SDK SERVICES

const auth = getAuth(app);

const db = getFirestore(app);
const storage = getStorage(app);
const functions = getFunctions(app);
const analytics = isAnalyticsSupported().then((yes) => (yes ? getAnalytics(app) : null));
const messaging = isMessagingSupported().then((yes) => (yes ? getMessaging(app) : null));

// APP EXPORTS

export { auth, db, storage, analytics, messaging, functions };


Do I need to put the app in the Client? I took it out in the Server

const auth = getAuth(app);

@emwp
Copy link
Contributor

emwp commented Mar 31, 2025

Hey @JwillSDP thanks for following up.
I'm glad it is working (partially).

We've checked with the internal Firebase team, and a specific fix for this issue won't be pursued at the moment.
So for now at least, we'll have to deal with how webpack is transforming the code.

I've checked locally and with a very similar codebase to yours I am able to run it both in production as well as in development locally, here is the example repository I was testing with: https://github.com/emwp/firebase-example

Hope that helps!

@emwp emwp added the Needs: Author Feedback Issues awaiting author feedback label Apr 7, 2025
@google-oss-bot
Copy link
Contributor

Hey @JwillSDP. We need more information to resolve this issue but there hasn't been an update in 7 weekdays. I'm marking the issue as stale and if there are no new updates in the next 3 days I will close it automatically.

If you have more information that will help us get to the bottom of this, just add a comment!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

5 participants