Search code examples
javascriptnext.jsvercelcustom-error-pages

404 Page not found Vercel Deployment - using dynamic routing in Next JS


I am making a full-stack web-application using Next JS where I allow the user to create and manage letters (applications) based on pre-defined templates. So when the user successfully creates an application, it is sent to the database (POSTGRES) which is hosted on Supabase. On the home page, the applications created by the user are fetched and displayed in the form of a list. Here on, when the user chooses to preview an application, dynamic routing is put in place where the application IDs work as the dynamic parameter. By using getStaticPaths() to get the route parameters from the database, and then fetching the data for the page from the database based on the application ID in the getStaticProps() method at build time, we render the page. It works seamlessly on localhost but not on Vercel. The interesting part however is,that dynamic routing works on Vercel for past applications for every deployment, that is if the user wants to preview their past applications they can do so without any problem, but when they create an application and then try to preview it, they are prompted with the 404 error. But if I trigger a redeployment either manually or by a commit to the main branch of my repository, the error is fixed for the particular application which was giving the error. `

export const getStaticPaths = async () => {
  var APIendpoint;
  if (process.env.NODE_ENV === 'development') {
    APIendpoint = 'http://localhost:3000/api/fetchApplication'
  }
  else if (process.env.NODE_ENV === 'production') {
    APIendpoint = 'https://templaterepo.vercel.app/api/fetchApplication';
  }
  const data = await getPaths(APIendpoint)
  const paths = data.map((application) => {
    return {
      params: { id: application.appid.toString() }
    }
  })

 return {
    paths,
    fallback: 'blocking'
  }

}

export async function getStaticProps(context) {
  const appID = context.params.id;

  var APIendpoint;
  if (process.env.NODE_ENV === 'development') {
    APIendpoint = 'http://localhost:3000/api/fetchApplicationwithID'
  }
  else if (process.env.NODE_ENV === 'production') {
    APIendpoint = 'https://templaterepo.vercel.app/api/fetchApplicationwithID';
  }

  let data = await getPageData(APIendpoint, appID);
 
  return {
    props: { data }
  }
}

Here is the code for the dynamic [id].js page where in I first get the paths based on the application IDs and then in the getStaticProps() function I fetch the data for the page corresponding to the application ID at build time. It works as expected in localhost but in Vercel deployment, even before the functions are executed, I get a 404 error. Note: Vercel Framework Preset is set to Next.js.

I tried a variety of solutions including adding to and as parameters in the Link component. Also I changed my vercel.json file to the below configuration `

{
    "rewrites": [{ "source": "/(.*)", "destination": "/index.html" }]
}

` But nothing seems to work.


Solution

  • When they create an application and then try to preview it, they are prompted with the 404 error. But if I trigger a redeployment either manually or by a commit to the main branch of my repository, the error is fixed for the particular application which was giving the error.

    This is expected, the data necessary for each dynamic page to be built is fetched ONLY at build time. Since you are using getStaticProps, you can implement ISR by adding a revalidate prop in getStaticProps, that way when a page (like a new application) has not been generated at build-time, Next.js will server-render it on first request and then cache it for subsequent requests.

    On development mode, both getStaticProps and getStaticPaths run per-request (much like getServerSideProps), that's why you don't have this issue on the dev environment. Reference to this on the docs.

    If you decide to implement ISR and want to display a loading UI while the page is being server-rendered, make sure to set fallback: true on getStaticPaths and at component level, you can access the router.isFallback flag to display the loading UI accordingly, otherwise, leave it as you already have with fallback: 'blocking'.

    Also, make sure you write the server-side code directly in getStaticProps and getStaticPaths instead of calling your own API endpoints on these functions. This according to the docs.