Error Encountered While Serializing Products in getServerSideProps in Next.js v14

I am using Next.js version 14.2.3 and I am currently trying to retrieve a list of products from Firestore by utilizing the getProducts function from @stripe/firestore-stripe-payments within getServerSideProps. However, I am facing a serialization error:

Server Error
Error: Error serializing .products returned from getServerSideProps in "/".
Reason: undefined cannot be serialized as JSON. Please use null or omit this value.

This is the relevant portion of my code:

import Head from "next/head"; import Header from "@/components/Header"; import Banner from "@/components/Banner"; import { Movie } from "@/typings"; import requests from "@/utils/requests"; import Row from "@/components/Row"; import useAuth from "@/hooks/useAuth"; import { useRecoilValue } from "recoil"; import { modalState } from "@/atoms/moduleAtom"; import Modal from "@/components/Modal"; import Plans from "@/components/Plans"; import { Product, getProducts } from "@stripe/firestore-stripe-payments"; import payments from "@/lib/stripe"; interface Props { netflixOriginals: Movie[]; trendingNow: Movie[]; topRated: Movie[]; actionMovies: Movie[]; comedyMovies: Movie[]; horrorMovies: Movie[]; romanceMovies: Movie[]; documentaries: Movie[] products: Product[] } const Home = ({ netflixOriginals, actionMovies, comedyMovies, documentaries, horrorMovies, romanceMovies, topRated, trendingNow, products Props) => { console.log(products) const { logout, loading } = useAuth() const showModal = useRecoilValue(modalState) const subscription = false if (loading || subscription === null) return null if (!subscription) return <Plans/> return ( <div this is so we cant scroll when we open the modal className={`relative h-screen bg-gradient-to-b lg:h-[140vh] ${showModal && "!h-screen overflow-hidden"}`} > <Head>
        <title>Home - Netflix</title>
        <link rel="icon" href="/favicon.ico" />
      </Head>
      <Header />
      <main className="relative pl-4 pb-24 lg:space-y24 lg:pl-16">
        <Banner netflixOriginals={netflixOriginals}/>
        <section className="md:space-y-24">
        <Row title="Trending Now" movies={trendingNow} />
          <Row title="Top Rated" movies={topRated} />
          <Row title="Action Thrillers" movies={actionMovies} />
My List Component*/   <Row title="Comedies" movies={comedyMovies} />   <Row title="Scary Movies" movies={horrorMovies} />   <Row title="Romance Movies" movies={romanceMovies} />   <Row title="Documentaries" movies={documentaries} />
        </section>
      </main>    {showModal} && <Modal />
    </div> );

} export const getServerSideProps = async () => {
const products = await getProducts(payments, {
includePrices: true,
activeOnly: true }).then((res) => res).catch((error) => console.log(error.message))

const [ netflixOriginals, trendingNow, topRated, actionMovies, comedyMovies, horrorMovies, romanceMovies, documentaries, await Promise.all([ fetch(requests.fetchNetflixOriginals).then((res) => res.json()), fetch(requests.fetchTrending).then((res) => res.json()), fetch(requests.fetchTopRated).then((res) => res.json()), fetch(requests.fetchActionMovies).then((res) => res.json()), fetch(requests.fetchComedyMovies).then((res) => res.json()), fetch(requests.fetchHorrorMovies).then((res) => res.json()), fetch(requests.fetchRomanceMovies).then((res) => res.json()), fetch(requests.fetchDocumentaries).then((res) => res.json()), ]); return { props: { netflixOriginals: netflixOriginals.results, trendingNow: trendingNow.results, topRated: topRated.results, actionMovies: actionMovies.results, comedyMovies: comedyMovies.results, horrorMovies: horrorMovies.results, romanceMovies: romanceMovies.results, documentaries: documentaries.results, products }, }; };

export default Home;

What I Attempted:

  • I implemented error handling for the getProducts call to ensure that it logs any problems.
  • I tried to sanitize the products array by converting undefined values to null using JSON.stringify and JSON.parse.
  • I restarted Firebase multiple times, created a new database, and set up new Stripe products.
  • I validated the presence of the collection of products in my Firebase database.
  • I confirmed that Firebase is correctly connected to my app, as new users are successfully added to the Firebase database during registration. Despite these efforts, the serialization error persists, suggesting that there may still be an undefined value within the products array.

I anticipated the getServerSideProps function to retrieve the products and other movie data without encountering serialization issues, enabling the products array to be passed to the component without any errors.

Answer №1

Welcome to everyone reading this,

If you're facing similar issues, it appears that the root of the problem lies in using an outdated version of the Stripe SDK. To resolve this issue, my recommendation is to switch to implementing the Stripe API directly instead of relying on the outdated SDK.

Here's a step-by-step guide on how you can approach this:

Retrieve Products and Prices: If you still require fetching product and price data from Firestore, create a file named fetchProducts.ts to dynamically retrieve this information. Below is the code snippet for fetching products:

// fetchProducts.ts

    import { db } from '../firebase';
    import { collection, query, where, getDocs } from 'firebase/firestore';
    
    const fetchProducts = async (): Promise<any[]> => {
      const products: any[] = [];
    
      try {
        const productsCollection = collection(db, 'products');
        const q = query(productsCollection, where('active', '==', true));
        const querySnapshot = await getDocs(q);
    
        for (const doc of querySnapshot.docs) {
          const productData = doc.data();
          const pricesCollection = collection(doc.ref, 'prices');
          const priceSnap = await getDocs(pricesCollection);
    
          const prices = priceSnap.docs.map((priceDoc) => ({
            id: priceDoc.id,
            ...priceDoc.data(),
          }));
    
          products.push({
            id: doc.id,
            ...productData,
            prices,
          });
        }
      } catch (error) {
        console.error('Error fetching products:', error);
      }
    
      return products;
    };
    
    export default fetchProducts;

Initiate a Subscription with Stripe Checkout: Use the provided code snippet to start a subscription by adding a new document in the checkout_sessions collection for the user. The Firestore extension will update this document with a Stripe Checkout session ID, which can be used to redirect the user to the checkout page.

import { collection, addDoc, doc, onSnapshot } from 'firebase/firestore';
import { auth, db } from '../firebase';

const createCheckoutSession = async (priceId: string) => {
  const user = auth.currentUser;
  if (!user) throw new Error('No authenticated user found.');

  const uid = user.uid;

  // Create a checkout session in Firestore
  const col = collection(db, 'customers', uid, 'checkout_sessions');
  const docRef = await addDoc(col, {
    price: priceId,
    success_url: window.location.origin,
    cancel_url: window.location.origin,
  });

  

// Listen for the checkout session to be created and fetch the URL

return new Promise<string>((resolve, reject) => {
    const unsub = onSnapshot(doc(db, 'customers', uid, 'checkout_sessions', docRef.id), (snap) => {
      const data = snap.data();
      if (data?.url) {
        unsub();
        resolve(data.url);
      } else if (data?.error) {
        unsub();
        reject(new Error(`An error occurred: ${data.error.message}`));
      }
    });
  });
};

export default createCheckoutSession;

Integrate it into Your Component: Utilize the createCheckoutSession function within your component to manage the subscription process. Here's a sample of how you could incorporate it:

import createCheckoutSession from './path/to/your/createCheckoutSession';

const subscribeToPlan = async (priceId: string) => {
  try {
    const url = await createCheckoutSession(priceId);
    window.location.assign(url);
  } catch (error) {
    console.error('Error creating checkout session:', error);
  }
};

For further guidance, feel free to check out this video

https://youtu.be/xi3F2Zv91UE?t=1311

for a detailed walkthrough on setting up Stripe Checkout with Firestore.

I believe this information will assist in resolving the challenges you are encountering!

Edit : Here is my package.json. I have downgraded firebase and stripe versions, not tested with the latest ones, but it should function correctly as we don't rely on the problematic SDK.

package.json

{ "name": "nx-clone", "version": "0.1.0", "private": true, "scripts": { "dev": "next dev", "build": "next build", "start": "next start", "lint": "next lint" }, "dependencies": { "@emotion/react": "^11.11.4", "@emotion/styled": "^11.11.5", "@heroicons/react": "^1.0.6", "@invertase/firestore-stripe-payments": "^0.0.7", "@mui/material": "^5.15.18", "@stripe/firestore-stripe-payments": "^0.0.6", "firebase": "^9.0.0", "next": "^14.2.3", "next-transpile-modules": "^10.0.1", "react-icons": "^5.2.1", "react-player": "^2.16.0", "recoil": "^0.7.7", "tailwind-scrollbar-hide": "^1.1.7" }, "resolutions": { "@firebase/firestore": "3.13.0", "@firebase/auth": "0.23.2", "@firebase/app": "0.9.13" }, "devDependencies": { "@types/node": "^20", "@types/react": "^18", "@types/react-dom": "^18", "autoprefixer": "^10.4.19", "eslint": "^8", "eslint-config-next": "14.2.3", "postcss": "^8.4.38", "tailwind-scrollbar": "^3.1.0", "tailwindcss": "^3.4.3", "tailwindcss-no-scrollbar": "^1.0.1", "tailwindcss-textshadow": "^2.1.3", "typescript": "^5" } }

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

Having trouble locating the bootstrap import statement

Currently, I am working on a project in Angular where I have defined two styles in the angular.json file - styles.css and node_modules/bootstrap/dist/css/bootstrap.min.css. After running ng serve, it shows that it compiled successfully. However, upon ins ...

Click on the next tab within the ExtJS tab menu

I am looking to deactivate a tab and if it happens to be active, I want the system to automatically switch to the next tab. I attempted myPanel.tab.disable(); if(myPanel.tab.active) myPanel.NextSibling().tab.setActive(); and myPanel.tab.disable(); ...

The error of "Uncaught ReferenceError" is being triggered by the use of `process?.env?

A bug was discovered in our front-end code related to the following snippet: <span style={{ color: 'red' }}>{process?.env?.REACT_APP_HEADER_SUFFIX ?? ''}</span> The bug triggered the following error message: Uncaught Refere ...

Customize default progress bar in browsers

One feature of my website allows users to update their personal information, profile image, background image, and more. After clicking the submit button, a loading screen appears with a progress percentage displayed at the bottom left corner of the page (i ...

States are consistently maintained in React and do not impact the rendering process

I am keeping track of a state value by declaring it like this: const [count, setCount] = useState(0); To increment the count: const incrementCount = () => { setCount(count + 1); } I use this function in a loop to iterate through an array, exec ...

Having an issue with jQuery where trying to append a remove button to a list results in the

Looking to create a dynamic list where users can easily add new items by clicking a button. As each item is added, a corresponding remove button should also be displayed. The issue I'm facing is that every time a new item is added, an extra close but ...

Vuetify ensures that elements remain in a single row and adjust their size according to the content

I'm trying to create a layout with a single row that has a button aligned to the right edge, and the rest of the space filled with multiple buttons inside a v-chip-group. There are more buttons than can fit in the available space, so I want the v-chip ...

Update the display using a button without the need to refresh the entire webpage

I am currently working on a website project that requires randomized output. I have successfully implemented a solution using Javascript, but the output only changes when the page is reloaded. Is there a way to update the output without refreshing the en ...

Retrieving CSS style values with Node.js

I am looking to design a new CSS style that includes the lowest and highest values. table[my-type="myStyle"] { width: 255.388px !important; } This code snippet is included in numerous CSS files within my style directory. ...

Importing JavaScript files to work with Vue-Router: A Step-by-Step Guide

Seeking guidance on importing JavaScript files correctly throughout my Vue project, including within the components used to implement changes with Vue-Router. Currently, encountering an issue where I must reload the component in order for the JavaScript to ...

Following the application of the filter function, a singular array was generated as the output

function sortByAge(arr) { let ageSorted = arr.sort(function(a, b) { return Object.fromEntries(a).age - Object.fromEntries(b).age }); // Arrange by young age. it's working well. let result = []; let names = ageSor ...

Facing the issue of "Protractor not syncing with the page" while trying to navigate an Angular website

I'm currently attempting to follow the tutorial for Protractor on the official Protractor website, but I've hit a roadblock at step 0. My setup involves using protractor and webdriver-manager version 6.0.0. I am running Linux (Ubuntu 18.06) as m ...

What changes can be made to the HTML structure to ensure that two form tags function separately?

Hey there! I'm currently tackling a web project that involves incorporating two form tags on one page, each with its own distinct purpose. Nevertheless, it appears that the inner form tag isn't behaving as it should. My suspicion is that this iss ...

Generate an additional element for each element when clicked, triggering an error warning

When creating a form, I want to display an error message next to each invalid element when the submit button is clicked. Although I am close, the issue is that it adds the span tag twice after the element if two elements are invalid. I need it to be added ...

The sonar scanner encountered an error while attempting to parse a file using the espree parser in module mode

While executing sonar-scanner on a node project, I encounter a Failed to parse file issue, as shown below: ERROR: Failed to parse file [file:///home/node-app/somedir/index.js] at line 1: Unexpected token './AddCat' (with espree parser in mod ...

Generating a JSON download link using AngularJS

I'm attempting to generate a link that will enable the download of a JSON file in this way Controller $scope.url = "data:text/json;charset=utf-8," + encodeURIComponent(JSON.stringify(obj)); View <a href="url" download="download.json">downlo ...

Tips for correctly implementing CORS (Cross-Origin Resource Sharing)

Is there a way to securely access a resource from a third-party domain using XML HTTP Requests (XHR, AJAX)? I have set up CORS on both the target and origin sides with the following configuration: Access-Control-Allow-Origin: http://www.example.com, http ...

Switch the scroll direction in the middle of the page and then switch it back

Yesterday, while browsing online, I stumbled upon this website and was amazed by the unique scroll direction change from vertical to horizontal mid-page. I'm curious about how they managed to achieve that effect. Does anyone have insight into the pro ...

The smooth transition of my collapsible item is compromised when closing, causing the bottom border to abruptly jump to the top

Recently, I implemented a collapsible feature using React and it's functioning well. However, one issue I encountered is that when the collapsible div closes, it doesn't smoothly collapse with the border bottom moving up as smoothly as it opens. ...

How can I style the options and optgroups of a select dropdown with CSS, focusing on padding and margins?

In my experience, I have found that padding or margin CSS properties only seem to affect the appearance of options within the select menu in Firefox browser. I attempted the same styling in Internet Explorer and Chrome but it did not have any effect. Is ...