apollographql / apollo-client-nextjs

Apollo Client support for the Next.js App Router
https://www.npmjs.com/package/@apollo/experimental-nextjs-app-support
MIT License
358 stars 25 forks source link

DOMException [AbortError]: This operation was aborted #24

Closed jasperzs closed 2 months ago

jasperzs commented 12 months ago

Hi,

Thanks for putting up this awesome library! I recently started to use Apollo client to hit our graphQL api using Next.js app router. Currently, after a (SSR/SSG with export const revalidate = xxx) page is first loaded, then subsequently refreshing it in browser gives DOMException [AbortError]: This operation was aborted. From an old post while googling, I was able to make this error disappear by configuring an abortController in the httpLink, but my gut feeling tells me this probably is not the right way to address this problem.

Could you please shed some light on how to solve this the right way? Thank you!

image

My apollo client configuration is this:

image
phryneas commented 12 months ago

Could you create a minimal reproduction for this issue? I'd need to see that myself to look into that.

jasperzs commented 11 months ago

@phryneas I found out it's this line that caused the above DOM Exception AbortError. Any idea why this line is needed? https://github.com/apollographql/apollo-client/blob/249076fd908c79350077369961ea8e78fb06d862/src/link/http/createHttpLink.ts#L201

Currently, to avoid this abortController call to trigger the AbortError, I passed my own signal to fetchOptions, but I don't know what side effect will this incur? Will it affect any cleanup action that apollo client does? https://github.com/apollographql/apollo-client/blob/249076fd908c79350077369961ea8e78fb06d862/src/link/http/createHttpLink.ts#L123

And in the polls-demo example, you can reproduce this issue by removing export const dynamic and replace it with export const revalidate = 5. https://github.com/apollographql/apollo-client-nextjs/blob/main/examples/polls-demo/app/rsc/page.tsx#L6

phryneas commented 11 months ago

I've done some digging here.

Essentially: this is the doing of Next.js, and there is probably nothing we can do about it.

You can find the related code here: https://github.com/vercel/next.js/blob/05f6de10869021dbb6124e174778e2108c9b4e8e/packages/next/src/server/lib/patch-fetch.ts#L231-L233

Here, Next.js calls fetch with the AbortSignal we pass in, and then it doesn't re-throw errors, but just console.logs and swallows then.

Now, we make every request with an AbortSignal and as a general cleanup, we cancel that signal at the end. Any errors that would be thrown by that are caught by us, and discarded. We can't catch & discard this AbortError, because Next.js handles it on their own. And honestly, in a completely irresponsible fashion. This could be any kind of error, and it will never surface.

Your workaround essentially means that you take control over the fetch request lifetime - we cannot stop outgoing requests anymore, and that is your job now.

Honestly, at this point I don't really know what we can do here :/ I'll have to discuss this internally.

Maybe you can also open an issue over at the Next repo?

t-zander commented 10 months ago

For me it looks like this revalidate option is not taken into account at all. This doesn't work

const { data } = await getClient().query({
      query,
      context: {
        fetchOptions: {
          next: { revalidate: 5 },
        },
      },
    });

and

export const revalidate = 5;

doesn't work too.

I am taking this directly from this resource https://www.apollographql.com/blog/apollo-client/next-js/how-to-use-apollo-client-with-next-js-13/#revalidating-graphql-requests

If I do this:

const { data } = await fetch(
    "https://main--time-pav6zq.apollographos.net/graphql",
    {
      method: "POST",
      body: JSON.stringify({
        query: '{ now(id: "1") }',
      }),
      headers: {
        "Content-Type": "application/json",
      },
      next: { revalidate: 10 },
    }
  ).then((res) => res.json());

it works perfectly fine

phryneas commented 10 months ago

It's very possible that the fact that we cancel our AbortController after the request, and they just blindly reuse that AbortController means that their revalidation request never happens. :/

That's why you don't mess with fetch :(

phryneas commented 2 months ago

I believe this has been fixed quite a while ago in HttpLink, so I'm closing this here.

github-actions[bot] commented 2 months ago

Do you have any feedback for the maintainers? Please tell us by taking a one-minute survey. Your responses will help us understand Apollo Client usage and allow us to serve you better.