How to avoid fetching the same data twice in Next.js v13?
Asked Answered
R

3

6

I need to lookup an entry from the database and generate the metadata and render the actual page contents.

At the moment, I am doing it like so:

export const generateMetadata = async ({
  params: { questionSlug },
}: Props): Promise<Metadata> => {
  const sql = await connectToPostgres();

  const question = await findQuestion(sql, questionSlug);

  if (!question) {
    return {};
  }

  return {
    alternates: {
      canonical: `https://ray.run/questions/${question.slug}`,
    },
    title: question.question,
  };
};

const Page = async ({ params: { questionSlug } }: Props) => {
  const sql = await connectToPostgres();

  const question = await findQuestion(sql, questionSlug);

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

  // ...
};

but this means that I have to make the same database query twice to render the same page.

Is there a way to fetch data once and use it in both methods?

Roister answered 7/6, 2023 at 23:43 Comment(0)
R
4

It is not pretty. However, the official way is to use the cache function:

import { cache } from 'react';

const findQuestionWithCache = cache(async (questionSlug: string) => {
  const sql = await connectToPostgres();

  return await findQuestion(sql, questionSlug);
});

export const generateMetadata = async ({
  params: { questionSlug },
}: Props): Promise<Metadata> => {
  const question = await findQuestionWithCache(questionSlug);

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

  return {
    alternates: {
      canonical: `https://ray.run/questions/${question.slug}`,
    },
    title: question.question,
  };
};

const Page = async ({ params: { questionSlug } }: Props) => {
  const question = await findQuestionWithCache(questionSlug);

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

  // ...
};

The contents of the findQuestionWithCache function is only going to be called once per request.

Sadly, the downside of this approach is that cache wrapped function cannot be called outside of React/Next.js. Therefore, if you are using the same function outside of your React.js codebase, you have to wrap it locally like I do in the above example. If you only call this function within React.js codebase, then you can probably just wrap the original implementation.

Roister answered 23/10, 2023 at 13:3 Comment(0)
G
-1

Next.js deduplicates fetch requests for generateMetadata but I'm not sure about your database connection. Good to know section

Gayn answered 8/6, 2023 at 9:21 Comment(2)
It does not do that with database queries. I can see both queries come in.Roister
You may need to move the database connection to the root layout.Gayn
D
-1

Try This: By adding the cache object on the topmost.


let cache ={};

export const generateMetadata = async ({
  params: { questionSlug },
}: Props): Promise<Metadata> => {
// This if statement return data from cache without send request to server.
if(cache) return cache;
  const sql = await connectToPostgres();

  const question = await findQuestion(sql, questionSlug);

  if (!question) {
    return {};
  }
  cache = question;
  return {
    alternates: {
      canonical: `https://ray.run/questions/${question.slug}`,
    },
    title: question.question,
  };
};

const Page = async ({ params: { questionSlug } }: Props) => {
  
  if (!cache) {
    await connectToPostgres();
    const question = await findQuestion(sql, questionSlug);
    if(!question) return notFound();
  }
  const response = cache;

  // ... use response variable as cache data.
};
Delatorre answered 8/6, 2023 at 9:59 Comment(1)
This is a hack. Next.js already has cache support built inChlamydeous

© 2022 - 2024 — McMap. All rights reserved.