'Next.js ISR pass additional data to getStaticProps from getStaticPaths

In SingleBlogPost.jsx i have:

export async function getStaticPaths() {
  const res = await fetch("http://localhost:1337/api/posts");
  let { data } = await res.json();

  const paths = data.map((data) => ({
    params: { slug: data.attributes.slug },
  }));

  return {
    paths,
    fallback: "blocking",
  };
}

where I generate blog pages by their slug. But then in getStaticProps I need to fetch single post by slug but I want to do it by id.

export async function getStaticProps(context) {
  console.log("context", context);

  const { slug } = context.params;

  console.log("slug is:", slug);
  const res = await fetch("http://localhost:1337/api/posts");
  const { data } = await res.json();

  return {
    props: {
      data,
    },

    revalidate: 10, // In seconds
  };
}

And I want to keep url like /blog/:slug , I dont want to include id. in url .When I already fetch all posts in getStaticPaths how I can access post id in getStaticProps to avoid fetching by slug?



Solution 1:[1]

I found a concise work around that uses the object-hash package. I basically create a hash of the params object and use that to create the tmp filename both on set and get. The tmp file contains a json with the data I want to pass between the two infamous static callbacks.

The gist of it:

function setParamsData({params, data}) {
  const hash = objectHash(params)
  const tmpFile = `/tmp/${hash}.json`

  fs.writeFileSync(tmpFile, JSON.stringify(data))
}

function getParamsData (context) {
  const hash = objectHash(context.params)
  const tmpFile = `/tmp/${hash}.json`

  context.data = JSON.parse(fs.readFileSync(tmpFile))

  return context
}

We can then use these helpers in the getStaticPaths and getStaticProps callbacks to pass data between them.

export function getStaticPaths(context) {
  setParamsData({...context, data: {some: 'extra data'})

  return {
    paths: [],
    fallback: false,
  }
}

export function getStaticProps(context) {
  context = getParamsData(context)

  context.data // => {some: 'extra data'}
}

I'm sure someone can think of a nicer API then re-assigning a argument variable.

The tmp file creation is likely not OS independent enough and could use some improvement.

Solution 2:[2]

It looks like recently released a workaround using a file system cache.

The crux of the solution is that they save the body object in memory, using something like this:

this.cache = Object.create(null)

and creating methods to update and fetch data from the cache.

Discussion here: https://github.com/vercel/next.js/discussions/11272#discussioncomment-2257876

Example code: https://github.com/vercel/examples/blob/main/build-output-api/serverless-functions/.vercel/output/functions/index.func/node_modules/y18n/index.js#L139:10

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 bas080
Solution 2 Michael Tran