NextJs 14- GenerateMetaData using static export

202 views Asked by At

We are trying to add Meta information based on the data fetched from an API. We are statically exporting pages using export. It works perfectly on the development but when I try to make a build of it (next build with export enabled) this function is invoked multiple times and hence the API throws a 429 time-out error.

Following is the function doing the business logic:

export async function generateMetadata(): Promise<Metadata> {
  const portal = await GetPortalTheme().then((res: any) => {
    if (res.data) {
      return res.data.portal;
    }
  });
  const setting = JSON.parse(portal.settings || "{}");
  return {
    title: portal.name || "portal",
    description: "Innovate your out bound call",
    icons: {
      icon: setting.logo?.url,
    },
  };
}

What it should do?

This API should be called once and the MetaData should be set for all the pages.

2

There are 2 answers

0
Munyyb Ur Ryhman On

So, to come up with a solution to solve this problem we have implemented a strategy that stores values locally, and upon fetching from API, the data is stored in the local variable. Then we placed a condition that only if that value does not exist, only then call the API otherwise do not call it.

Here is the approach we followed:

let portalSettings: any;

export async function generateMetadata(): Promise<Metadata> {
  let portal;

  if (!portalSettings) {
    portal = await GetPortalTheme().then((res: any) => {
      if (res.data) {
        return res.data.portal;
      }
    });
    portalSettings = {
      setting: JSON.parse(portal.settings || "{}"),
      name: portal.name,
    };
  }
  return {
    title: portalSettings.name || "portal",
    description: "Innovate your out bound call",
    icons: {
      icon: portalSettings.setting.logo?.url,
    },
  };
}

Please let me know if we can improve this code or if you have any suggestions.

0
Fabio Nettis On

Actually the 429 Statuscode indicates: "Too Many Requests". To provide metadata to all your pages, you need to add it into the root layout.tsx file.

// app/layout.js
export default function RootLayout({ children }) {
  return (
    <html>
      <head />
      <body>{children}</body>
    <html>
  );
}

export async function generateMetadata() {/* */}

After doing so, it should only ever be called once. Even for subsequent page builds. At least, thats the way it should work.


Alternative approach

If Next.js somehow would need to call the generateMetdata function for every single page, even when contained in the root layout file when statically exporting, you can always generate the metadata in an additional build step.

// utils/static_metadata.js
const path = require("path");
const fs = require("fs/promises");

const file_path = path.join(process.cwd(), "data", "static_metadata.json");

async function static_metadata() {
  const portal = const portal = await GetPortalTheme();
  const metadata = result?.data?.portal ?? JSON.parse(portal.settings || "{}");
  await fs.writeFile(path, JSON.stringify(metadata), "utf-8"); 
}

static_metadata();

Inside your layout's generateMetadata function you can then simply import the file to return the contents of your API request.

// app/layout.js
export async function generateMetadata() {
  const metadata = await import("data/static_metadata.json");
  // ...
}

Now you can build your app by running the additional build step before calling the default build command : node ./utils/static_metadata.js && yarn build.