Skip to content

Update persisted query link error handling to match the rest of the client #12704

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 50 commits into from
Jun 18, 2025

Conversation

jerelmiller
Copy link
Member

@jerelmiller jerelmiller commented Jun 17, 2025

Closes #12569

Updates createPersistedQueryLink to combine graphQLErrors and networkError properties into a single error property to match the rest of the client. error is of type ErrorLike.

The response property has also been removed in favor of accessing data from the CombinedGraphQLErrors instance.

@jerelmiller jerelmiller requested a review from phryneas June 17, 2025 00:51
Copy link

changeset-bot bot commented Jun 17, 2025

🦋 Changeset detected

Latest commit: e5b8e87

The changes in this PR will be included in the next version bump.

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

Copy link

pkg-pr-new bot commented Jun 17, 2025

npm i https://pkg.pr.new/apollographql/apollo-client/@apollo/client@12704

commit: e5b8e87

Copy link
Contributor

github-actions bot commented Jun 17, 2025

size-limit report 📦

Path Size
import { ApolloClient, InMemoryCache, HttpLink } from "@apollo/client" (CJS) 42.52 KB (0%)
import { ApolloClient, InMemoryCache, HttpLink } from "@apollo/client" (production) (CJS) 37.67 KB (0%)
import { ApolloClient, InMemoryCache, HttpLink } from "@apollo/client" 32.52 KB (0%)
import { ApolloClient, InMemoryCache, HttpLink } from "@apollo/client" (production) 26.95 KB (0%)
import { ApolloProvider } from "@apollo/client/react" 5.68 KB (0%)
import { ApolloProvider } from "@apollo/client/react" (production) 963 B (0%)
import { useQuery } from "@apollo/client/react" 7.51 KB (0%)
import { useQuery } from "@apollo/client/react" (production) 2.73 KB (0%)
import { useLazyQuery } from "@apollo/client/react" 6.94 KB (0%)
import { useLazyQuery } from "@apollo/client/react" (production) 2.2 KB (0%)
import { useMutation } from "@apollo/client/react" 6.52 KB (0%)
import { useMutation } from "@apollo/client/react" (production) 1.76 KB (0%)
import { useSubscription } from "@apollo/client/react" 6.62 KB (0%)
import { useSubscription } from "@apollo/client/react" (production) 1.86 KB (0%)
import { useSuspenseQuery } from "@apollo/client/react" 8.48 KB (0%)
import { useSuspenseQuery } from "@apollo/client/react" (production) 3.75 KB (0%)
import { useBackgroundQuery } from "@apollo/client/react" 8.22 KB (0%)
import { useBackgroundQuery } from "@apollo/client/react" (production) 3.51 KB (0%)
import { useLoadableQuery } from "@apollo/client/react" 8.52 KB (0%)
import { useLoadableQuery } from "@apollo/client/react" (production) 3.82 KB (0%)
import { useReadQuery } from "@apollo/client/react" 6.43 KB (0%)
import { useReadQuery } from "@apollo/client/react" (production) 1.68 KB (0%)
import { useFragment } from "@apollo/client/react" 6.47 KB (0%)
import { useFragment } from "@apollo/client/react" (production) 1.74 KB (0%)

});
```

The `response` property has also been removed. To access partial data returned in GraphQL responses, access the `data` property on the `CombinedGraphQLErrors` instance.
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think it makes sense to remove this, but one thing we did lose in this process is access to extensions since CombinedGraphQLErrors doesn't have an extensions property. Is that something we want to add? Seems more relevant here especially since persisted queries does a lot with extensions.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, it sounds like a good idea to add that to CombinedGraphQLErrors!

@@ -789,3 +796,693 @@ describe("failure path", () => {
});
});
});

test("disables persisted queries from future requests when `disable` returns true", async () => {
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We didn't test disable or retry at all 😱


const graphQLErrors: GraphQLFormattedError[] = [];
function handleRetry(errorResponse: ErrorResponse, cb: () => void) {
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The diff is a bit horrendous, but if you want to look at the refactor to see how I got here, check out the diff commit-by-commit.

@jerelmiller jerelmiller linked an issue Jun 17, 2025 that may be closed by this pull request
@@ -24,9 +29,7 @@ import { defaultCacheSizes } from "../../utilities/caching/sizes.js";
export const VERSION = 1;

export interface ErrorResponse {
graphQLErrors?: ReadonlyArray<GraphQLFormattedError>;
networkError?: Error | ServerParseError | ServerError | null;
response?: FormattedExecutionResult;
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Response seems to also be available in onError link so we should probably keep this around.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Keep this. Rename it to result. Rename the interface and have both a RetryFunctionOptions and DisableFunctionOptions that point to the same type.


- if (errorResponse.networkError) {
+ if (errorResponse.error) {
// ... handle a NetworkError errors
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
// ... handle a NetworkError errors
// ... handle NetworkError-type errors

not sure if this is strictly better, but the a was irritiating me,.

});
```

The `response` property has also been removed. To access partial data returned in GraphQL responses, access the `data` property on the `CombinedGraphQLErrors` instance.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, it sounds like a good idea to add that to CombinedGraphQLErrors!

disable: (errorResponse) => {
- if (errorResponse.response) {
- const data = errorResponse.response.data;
+ if (CombinedGraphQLErrors.is(errorResponse.error)) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe we should rename errorResponse and the ErrorResponse type here? errorResponse.error is seriously weird. Otoh., errorResponse.response wasn't strictly better previously.
Just as food for thought: What about disablePayload or even just payload?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe we should just destructure in these examples? 🤔

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good call! I did a bunch of updates in 0ed0931 to give better examples and use destructuring.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

And I agree, I'm not a huge fan of ErrorResponse, but it does match what we have in onError link. the only thing about something like disablePayload is that this object is also passed to retry.

Copy link
Member

@phryneas phryneas left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Generally approved, but needs the incremental error handling like the error link.

Comment on lines 235 to 246
error: new CombinedGraphQLErrors(response),
meta: processErrors(response.errors),
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I added a better way to handle incremental stuff here - take a look at

const handler = operation.client["queryManager"].incrementalHandler;
const errors =
handler?.isIncrementalResult(result) ?
handler.extractErrors(result)
: (result as FormattedExecutionResult).errors;

});
});

test("calls `retry` with GraphQL errors when parsed from non-2xx response", async () => {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm thinking here... do we want to do that kind of parsing or go the "GraphQL over Http" route and only do this with the right mime-type (in which case we don't need any extra logic)?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's leave this for now until servers catch up with the GraphQL over HTTP spec.

@github-actions github-actions bot added the auto-cleanup 🤖 label Jun 18, 2025
@jerelmiller jerelmiller force-pushed the jerel/persisted-query-link-error branch from 0ed0931 to e1f553e Compare June 18, 2025 16:59
@jerelmiller jerelmiller merged commit 45dba43 into release-4.0 Jun 18, 2025
41 of 42 checks passed
@jerelmiller jerelmiller deleted the jerel/persisted-query-link-error branch June 18, 2025 17:44
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

Successfully merging this pull request may close these issues.

[4.0] Update PersistedQueryLink to work with new error structure
2 participants