'SSR crashing in Next.js on unsuccessful GraphQL request (HTTP code 500) using Apollo Client

Well, I'm a little dumpy. I will try to explain my problem as clearly as possible.

I use Apollo client to do my GraphQL queries. I also use NextJS. I have a page that needs to be rendered on the server side for SEO reasons.

So I have a getProductFromSlug function that allows me to execute my request.

export const getProductFromSlug = async (slug: string) => {
  try {
    const { data, error } = await apolloClient.query<{
      product: Product
    }>({
      query: GET_PRODUCT_BY_SLUG_QUERY,
      variables: {
        slug,
      },
    })

    if (error) {
      return { errors: [error.message] }
    }

    if (!('product' in data) || data.product === null) {
      return { errors: ['Product with specified url not found'] }
    }

    return {
      data,
    }
  } catch (error) {
    // @ts-ignore
    const formattedErrors: ApolloError = isApolloError(error)
      ? error.graphQLErrors.map((error) => error.message)
      : [`Unhandled error : ${error}`]

    return {
      errors: formattedErrors,
    }
  }
}

Here's getServerSideProps to pass data to page

export const getServerSideProps = async (
  context: GetServerSidePropsContext
) => {
  // eslint-disable-next-line @typescript-eslint/ban-ts-comment
  // @ts-ignore
  const requestData = await getProductFromSlug(context.params.slug as string)
  return 'errors' in requestData
    ? { notFound: true, props: requestData }
    : { props: requestData }
}

The problem is that when I have a HTTP code 500 from the endpoint, the SSR is crashing and on Vercel, it's causing a serverless crash error.

Error: Response not successful: Received status code 500 This error happened while generating the page. Any console logs will be displayed in the terminal window

If needed, here's my entry point (_app.tsx):

function MyApp(props: AppProps) {
  return (
    <ApolloProvider client={apolloClient}>
      <RecoilRoot>
        <RecoilNexus />
        <AuthenticationFromStorage />
        <Layout>
          <props.Component {...props.pageProps} />
        </Layout>
      </RecoilRoot>
    </ApolloProvider>
  )
}

You can see my Apollo Client here : https://gist.github.com/SirMishaa/d67e7229307b77b43a0b594d0c9e6943

Stack trace of yarn run dev (next dev -p 3005) :

ServerError: Response not successful: Received status code 500
    at Object.throwServerError (C:\Users\misha\Documents\dev\rekk-next\node_modules\@apollo\client\link\utils\utils.cjs:45:17)
    at C:\Users\misha\Documents\dev\rekk-next\node_modules\@apollo\client\link\http\http.cjs:31:19
    at runMicrotasks (<anonymous>)
    at processTicksAndRejections (node:internal/process/task_queues:96:5)
error - uncaughtException: ServerError: Response not successful: Received status code 500
error Command failed with exit code 1.

NOTE : After some try with console.log in try and catch scope, it shows nothing in the Next SSR console, so the internal error of Apollo is not caught for some reason.

I appreciate your help, thank you!

enter image description here



Solution 1:[1]

You are returning empty data if a slug isn't found. You need to check if the data isn't found.

If data isn't found, a query still responds with a valid status code because it's still a valid query, and Apollo is also error-checking.


Commented example

export const getProductFromSlug = async (slug) => {
  try {
    // destructure `errors`
    const { data, errors } = await apolloClient.query({
      query: QUERY, variables: { slug },
    });

    // if apolloError/endpoint error i.e. 404/500/validation etc
    if(errors) {
     // Return notFound or formatted Apollo/validation errors
     return { notFound: true } 
    }

    // didn't find data for a slug and there aren't Apollo errors
    if(!data){ return { notFound: true } }

    // found slug and there aren't any errors
    return { data }

  } catch (e) {
    // hard JS errors not apollo or endpoint errors
    return { notFound: true }
  }
}

Uncommented example

export const getProductFromSlug = async (slug) => {
  try {
    const { data, errors } = await apolloClient.query({
      query: QUERY, variables: { slug },
    });
    return { data, notFound: (errors || !data) };
  } catch (e) {
    return { notFound: true };
  }
}

Doesn't account for input validation errors, but it appears your use case doesn't accept user input


You are already handling errors and data formatting in the abstracted code, return the data as is

export const getServerSideProps = async (context) => {
  if(!context.params) { return { notFound: true }}
  return await getProductFromSlug(context.params.slug);
}

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