diff --git a/.changeset/late-plants-poke.md b/.changeset/late-plants-poke.md new file mode 100644 index 0000000000..d0c90a67a1 --- /dev/null +++ b/.changeset/late-plants-poke.md @@ -0,0 +1,5 @@ +--- +'@urql/exchange-persisted': patch +--- + +Warn about cached persisted-miss results in development, when a `persistedExchange()` sees a persisted-miss error for a result that's already seen a persisted-miss error (i.e. two misses). This shouldn't happen unless something is caching persisted errors and we should warn about this appropriately. diff --git a/exchanges/persisted/src/persistedExchange.test.ts b/exchanges/persisted/src/persistedExchange.test.ts index 15632568a0..09b4a77778 100644 --- a/exchanges/persisted/src/persistedExchange.test.ts +++ b/exchanges/persisted/src/persistedExchange.test.ts @@ -119,6 +119,40 @@ it('retries query persisted query resulted in unsupported', async () => { expect(operations[2].extensions).toEqual(undefined); }); +it('fails gracefully when an invalid result with `PersistedQueryNotFound` is always delivered', async () => { + const { result, operations, exchangeArgs } = makeExchangeArgs(); + + result.mockImplementation(operation => ({ + ...queryResponse, + operation, + error: new CombinedError({ + graphQLErrors: [{ message: 'PersistedQueryNotFound' }], + }), + })); + + const res = await pipe( + fromValue(queryOperation), + persistedExchange()(exchangeArgs), + take(1), + toPromise + ); + + expect(res.operation.context.persistAttempt).toBe(true); + expect(operations.length).toBe(2); + + expect(operations[1].extensions).toEqual({ + persistedQuery: { + version: 1, + sha256Hash: expect.any(String), + miss: true, + }, + }); + + expect(console.warn).toHaveBeenLastCalledWith( + expect.stringMatching(/two misses/i) + ); +}); + it('skips operation when generateHash returns a nullish value', async () => { const { result, operations, exchangeArgs } = makeExchangeArgs(); diff --git a/exchanges/persisted/src/persistedExchange.ts b/exchanges/persisted/src/persistedExchange.ts index bfa17be738..5473dd7516 100644 --- a/exchanges/persisted/src/persistedExchange.ts +++ b/exchanges/persisted/src/persistedExchange.ts @@ -216,6 +216,18 @@ export const persistedExchange = retries.next(followupOperation); return null; } else if (result.error && isPersistedMiss(result.error)) { + if (result.operation.extensions.persistedQuery.miss) { + if (process.env.NODE_ENV !== 'production') { + console.warn( + 'persistedExchange()’s results include two misses for the same operation.\n' + + 'This is not expected as it means a persisted error has been delivered for a non-persisted query!\n' + + 'Another exchange with a cache may be delivering an outdated result. For example, a server-side ssrExchange() may be caching an errored result.\n' + + 'Try moving the persistedExchange() in past these exchanges, for example in front of your fetchExchange.' + ); + } + + return result; + } // Update operation with unsupported attempt const followupOperation = makeOperation( result.operation.kind,