Prismic & MongoDb - Your `getServerSideProps` function did not return an object

Hello there,

With the evolution of my website, I need to fetch data slices from Prismic and Data from my MongoDb database on my Homepage.

However, I get "Error: Your getServerSideProps function did not return an object. Did you forget to add a return?" when trying to add the mongoDb client in the getServerSideProps async function and I dont understand why. Could anybody help me on that matter please?

import React from "react";
import Head from "next/head";
import HeroHome from "../components/ui/HomePage/HeroHome";
// import NewsletterAlertsCta from "../components/ui/Shared/NewsletterAlertsCta";
import { useRouter } from "next/router";

import { SliceZone } from "@prismicio/react";

import { createClient } from "../prismicio";
import { components } from "../slices";
import clientPromise from "../lib/mongodb";

// import Script from "next/script";
import JobsFiltering from "../components/ui/Shared/Filtering/JobsFiltering";

const Homepage = (props) => {
  const router = useRouter();
  const currentRoute = router.pathname;

  const {
    metaTitle,
    metaDescription,
    ogImage,
    pagePrismic,
    navigation,
    settings,
    jobs,
    totalPages,
    page,
  } = props;

  console.log(jobs);

  function structuredDataOrg() {
    return {
      __html: `{
      "@context": "https://schema.org",
      "@type": "Corporation",
      "name": "Dataai Jobs",
      "url": "https://dataai-jobs.com/",
      "logo": "https://res.cloudinary.com/dl4mhtc4y/image/upload/v1668117398/dataai-jobs-logo-transparent_dxx3g4.png"
    }`,
    };
  }

  return (
    <React.Fragment>
      <Head>
        <title>{metaTitle}</title>
        <meta name="description" content={metaDescription} />
        <link rel="canonical" href="/" key="canonical" />

        {/* <!-- Twitter Card data --> */}
        <meta name="twitter:card" content="summary" />
        {/* <meta name="twitter:site" content="@publisher_handle" /> */}
        <meta name="twitter:title" content={metaTitle} />
        <meta name="twitter:description" content={metaDescription} />
        {/* <meta name="twitter:creator" content="@author_handle" /> */}
        {/* <-- Twitter Summary card images must be at least 120x120px --> */}
        <meta name="twitter:image" content={ogImage} />

        {/* <!-- Open Graph data --> */}
        <meta property="og:title" content={metaTitle} />
        <meta property="og:type" content="page" />
        <meta property="og:url" content={currentRoute} />
        <meta property="og:image" content={ogImage} />
        <meta property="og:description" content={metaDescription} />
        <meta property="og:site_name" content="Dataai Jobs" />
        <script
          type="application/ld+json"
          dangerouslySetInnerHTML={structuredDataOrg()}
          key="org-jsonld"
        />
      </Head>
      <HeroHome />
      <JobsFiltering jobs={jobs} />

      <SliceZone slices={pagePrismic.data.slices} components={components} />
      {/* <NewsletterAlertsCta /> */}
    </React.Fragment>
  );
};

export default Homepage;

export async function getServerSideProps({ previewData }, context) {
  try {
    // Prismic client
    const clientPrismic = createClient({ previewData });
    const pagePrismic = await clientPrismic.getSingle("homepage");

    // MongoDB
    const clientMongoDb = await clientPromise;
    const mongoDb = clientMongoDb.db("main");

    const pageSize = 20;
    // How to get to context.query? previewData.query?
    let { page } = context.query;
    page = page || 1;

    // console.log("Page: " + page);

    const jobs = await mongoDb
      .collection("jobs")
      .find({})
      .sort({})
      .skip((page - 1) * pageSize)
      .limit(pageSize)
      .toArray();

    const totalJobs = await mongoDb.collection("jobs").count();

    const totalPages = Math.ceil(totalJobs / pageSize);

    // console.log("total pages in jobs: " + totalPages);

    return {
      props: {
        metaTitle: page.data.meta_title,
        metaDescription: page.data.meta_description,
        ogImage: page.data.og_image.url,
        pagePrismic: pagePrismic,

        totalPages: totalPages,
        page: page,
        jobs: JSON.parse(JSON.stringify(jobs)),
      },
    };
  } catch (e) {
    console.log(e);
  }
}

I mean,

I know that the issue comes from my parameters.

If I only go for ({previewData}) and remove everything linked to context (pagination) it works fine.
But I wonder how can I make my pagination if I cannot access the context.query?

Thanks!

Hello Marving,

Thanks for reaching out to us.

@prismicio/client - v6 provides different params object for pagination, and all query helpers accept params object. Please find more details and the code example in the @prismicio/client - v6 technical reference: @prismicio/client Technical Reference - Documentation - Prismic.

Let me know if you need further assistance.

Thanks,
Priyanka

1 Like

hello @Priyanka,

Apologies, I think I will need further assistance:

Correct me if I am wrong - I have been developing for less than 6 months - but the presented params object in the v6 technical reference are all used with the Prismic Client so on the Prismic Database.
However, I am in a situation where I am getting my data from the MongoDB's client. I will not be able to use such params here, right?

Hello @Marving

What is context.query returning? What are you getting on the console?

Thanks,
Priyanka

Hello @Priyanka,

I am getting an typeError: Cannot read properties of undefined (reading 'context') if I try to access "context" somehow:

export async function getServerSideProps({ previewData }, context) {
  // Prismic client
  const clientPrismic = createClient({ previewData });
  const pagePrismic = await clientPrismic.getSingle("homepage");

  // MongoDB
  const clientMongoDb = await clientPromise;
  const mongoDb = clientMongoDb.db("main");

  const pageSize = 20;
  // How to get to context.query? previewData.query?
  let { page } = context.query;
  page = page || 1;

  console.log("Page: " + page);

  const jobs = await mongoDb.collection("jobs").find({}).sort({}).toArray();

  const totalJobs = await mongoDb.collection("jobs").count();

  const totalPages = Math.ceil(totalJobs / pageSize);

  console.log("total pages in jobs: " + totalPages);

  return {
    props: {
      metaTitle: pagePrismic.data.meta_title,
      metaDescription: pagePrismic.data.meta_description,
      ogImage: pagePrismic.data.og_image.url,
      pagePrismic: pagePrismic,

      totalPages: totalPages,
      page: page,
      jobs: JSON.parse(JSON.stringify(jobs)),
    },
  };
}

To give you an example, this is how I retrieve the pagination of my getServerSideProps for mongoDB on a page not related at all to Prismic:

export async function getServerSideProps(context) {
  try {
    const client = await clientPromise;
    const db = client.db("main");

    const pageSize = 20;
    let { page } = context.query;
    page = page || 1;

    // console.log("Page: " + page);

    const companies = await db
      .collection("companies")
      .find({})
      .sort({})
      .skip((page - 1) * pageSize)
      .limit(pageSize)
      .toArray();

    const totalCompanies = await db.collection("companies").count();

    const totalPages = Math.ceil(totalCompanies / pageSize);

    // console.log("total pages in companies: " + totalPages);

    return {
      props: {
        companies: JSON.parse(JSON.stringify(companies)),
        totalPages: totalPages,
        page: page,
      },
    };
  } catch (e) {
    console.log(e);
  }
}

Here, I can access the context, so I can access the query, which allow me to build my pagination component.

Nevertheless, on a page displayed on Prismic, as I have to pass previewData, I am not able to access context and the params does not provide it from not prismic related database (I might be wrong?).
Would you be as kind as telling me how to access it?

Then I am able to build my pagination component such as (same example, with companies collection coming from MongoDB):


function Content(props) {
  const { totalPages } = props;
  // console.log("Total pages from pagination: " + totalPages);

  const router = useRouter();
  let { page } = router.query;
  page = parseInt(page) || 1;

  function handlePageChange(_, page) {
    router.push(`/companies?page=${page}`);
  }

  return (
    <Stack spacing={2}>
      <Pagination
        page={page}
        count={totalPages}
        variant="outlined"
        shape="rounded"
        size="large"
        className={classes["pagination"]}
        onChange={handlePageChange}
        renderItem={(item) => (
          <PaginationItem
            count={2}
            component={Link}
            to={`/companies${item.page === 1 ? "" : `?page=${item.page}`}`}
            {...item}
          />
        )}
      />
    </Stack>
  );
}

export default function PaginationLink(props) {
  const { totalPages } = props;
  return (
    <MemoryRouter initialEntries={["/inbox"]} initialIndex={0}>
      <Routes>
        <Route path="*" element={<Content totalPages={totalPages} />} />
      </Routes>
    </MemoryRouter>
  );
}

Apologies for the code not being DRY, first time doing this here :grin:

Hello @Marving

Sorry for being delayed.

I see that your code has an error in getStaticProps() function. getStaticProps() only receives one parameter (context ), which contains both previewData and query (along with some other data). You are trying to pull out previewData from that parameter, but also pull out query from that same parameter. The issue is that you are taking query from a non-existent second parameter when it should be taken from the first. Here’s how the function should be written:

export async function getServerSideProps({ previewData, query }) {
  // Prismic client
  const clientPrismic = createClient({ previewData });
  const pagePrismic = await clientPrismic.getSingle("homepage");

  // MongoDB
  const clientMongoDb = await clientPromise;
  const mongoDb = clientMongoDb.db("main");

  const pageSize = 20;
  let { page } = query;
  page = page || 1;

  // ...the rest of the function...
}

Let me know if you need any further assistance.

Thanks,
Priyanka

Hello Priyanka,

Thank you for coming back to me.

I have found another way to do it, but I had try to get the query like that, it did not worked though.

Thank you!

Hello @Marving,

I am glad that you found a way to solve it. Would you mind sharing it here?

Thanks,
Priyanka

Well sure thing!

Sooo I am still a junior, so might be a clever way to do it.

At the end, I have not built my pagination & fetch the documents of the external database (mongodb) through "getServerSideProps", but directly in the route API (queries.ts) and I fetch it within a component through a POST request:

Extract of queries.ts where the pagination is built & documents filters in accordance:

async function searchJobs(
	mongoDb: Db,
	// salaryRanges: SalaryRange[],
	experienceLevels: string[],
	remoteTypes: string[],
	workTypes: string[],
	tags: string[],
	terms: string,
	termsLocation: string,
	// sortBy: string,
	page: number,
	// context,
) {
	const filters = [];

	addExperienceLevelToQuery(experienceLevels, filters);
	addRemoteTypeToQuery(remoteTypes, filters);
	addWorkTypeToQuery(workTypes, filters);
	addTagsToQuery(tags, filters);
	addTermsToQuery(terms, termsLocation, filters);
	// addTermsLocationToQuery(termsLocation, filters);
	// addSalaryRangesToQuery(salaryRanges, filters);

	let totalJobs = 0;
	let query = {};
	if (filters.length) {
		query = { $and: filters };
	}

	totalJobs = await mongoDb.collection("jobs").countDocuments(query);

	const pageSize = 7;

	page = page || 1;
	page = page > totalJobs ? 1 : page;

	// REFACTOR V2
	let elements = [];

	elements = await mongoDb
		.collection("jobs")
		.find(query)
		.sort({ publicationDate: -1 })
		.skip((page - 1) * pageSize)
		.limit(pageSize)
		.toArray();

	const totalPages = Math.ceil(totalJobs / pageSize);

	let pagination = {
		totalPages: totalPages,
		pageCount: pageSize,
		page: page,
	};

	let jobs = {};

	return (jobs = {
		elements,
		pagination,
	});
}

Extract of the component.js:

	// SEARCH JOBS ASYNC FUNCTION API CALL
	async function searchJobs(formProperties = {}) {
		return fetch(`/api/jobs/queries/`, {
			method: "POST",
			body: JSON.stringify(formProperties),
			headers: {
				"Content-Type": "application/json",
			},
		})
			.then((response) => response.json())
			.then((json) => {
				setJobs(json.elements);
				setPagination(json.pagination);
				setLoading(false);
			})
			.catch((error) => console.log(error));
	}

And so thanks to the json.pagination the const pagination made with useState contains the totalPages, the pageCount and the current page number

Hope it is clear!