使用 Apollo Client 的 GraphQL 请求(HTTP 代码 500)不成功时,SSR 在 Next.js 中崩溃

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

好吧,我有点矮胖。我会尽可能清楚地解释我的问题。

我使用 Apollo 客户端来执行我的 GraphQL 查询。我也使用 NextJS。 出于 SEO 原因,我有一个页面需要在服务器端呈现。

所以我有一个 getProductFromSlug 函数可以让我执行我的请求。

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,
    }
  }
}

这里getServerSideProps将数据传递给页面

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 }
}

问题是,当我从端点收到 HTTP 代码 500 时,SSR 崩溃了,在 Vercel 上,它导致了无服务器崩溃错误。

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

如果需要,这是我的切入点 (_app.tsx):

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

你可以在这里看到我的 Apollo 客户端:https://gist.github.com/SirMishaa/d67e7229307b77b43a0b594d0c9e6943

yarn run dev 的堆栈跟踪(下一个 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.

注意在 try 和 catch 范围内使用 console.log 进行一些尝试后,在 Next SSR 控制台中没有显示任何内容,因此由于某种原因未捕获 Apollo 的内部错误。

感谢您的帮助,谢谢!

更新

问题是 PusherLink 在发生错误时没有继续链中的执行。添加 errorcomplete 处理程序解决了问题。

      forward(operation).subscribe({
        next: (data) => {
          ...
          this.subscribeToChannel(subscriptionChannel, observer)
        },
        // these two were missing
        error: (error) => observer.error(error),
        complete: () => observer.complete(),
      })

JIC,我还添加了一个缺少的条件other link作为参考的代码有

    subscribeObservable.subscribe = (observerOrNext, onError, onComplete) => {
      if (typeof(observerOrNext) == "function") {
        prevSubscribe(observerOrNext, onError, onComplete)
      } else {
        prevSubscribe(observerOrNext)
      }

旧答案

您在 catch 块中的代码可能会抛出错误,这就是问题所在。我不确定这种情况 Array.isArray(apolloError),也许你的意思是 Array.isArray(apolloError.graphQLErrors)

您可以尝试使用 isApolloError 实用程序使其更清晰一些,并从 TS 类型中获得一些提示。

import { isApolloError } from '@apollo/client';
...
        const formattedErrors = isApolloError(e)
          ? e.graphQLErrors.map(error => error.message)
          : [`Unhandled error : ${e}`];

        return {
          errors: formattedErrors,
        };

如果未找到 slug,您 return 正在清空 data。您需要检查是否找不到数据。

如果找不到数据,查询仍会返回有效状态代码,因为它仍然是有效查询,Apollo 也是 error-checking.


评论示例

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 }
  }
}

未注释示例

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 };
  }
}

没有考虑输入验证错误,但您的用例似乎不接受用户输入


您已经在抽象代码中处理错误和数据格式化,return数据按原样

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