Repository: urql-graphql/urql
Pull request #3575 (2024-04-28 02:17:33)

fix(core): Use documentId for operation hash if it's available
## Summary

When persisted documents have hidden definitions (i.e. `definitions: []`), but have a `documentId`, we'd generate identical keys for all of these documents.

Instead, we should always use `documentId` to create a request key when it's available.

## Set of changes

- Use `documentId` in `hashDocument`


changeset-bot[bot]: ###  🦋  Changeset detected

Latest commit: e6db80e9677088fc3ab42312b008fd50695c738b

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

<details><summary>This PR includes changesets to release 1 package</summary>

  | Name       | Type  |
| ---------- | ----- |
| @urql/core | Patch |

</details>

Not sure what this means? [Click here  to learn what changesets are](https://github.com/changesets/changesets/blob/main/docs/adding-a-changeset.md).

[Click here if you're a maintainer who wants to add another changeset to this PR](https://github.com/urql-graphql/urql/new/fix/persisted-keys?filename=.changeset/tasty-ways-double.md&value=---%0A%22%40urql%2Fcore%22%3A%20patch%0A---%0A%0Afix(core)%3A%20Use%20documentId%20for%20operation%20hash%20if%20it's%20available%0A)



Patch:
diff --git a/.changeset/forty-walls-hug.md b/.changeset/forty-walls-hug.md
new file mode 100644
index 00000000..a6885bbb
--- /dev/null
+++ b/.changeset/forty-walls-hug.md
@@ -0,0 +1,5 @@
+---
+'@urql/core': patch
+---
+
+Use `documentId` from persisted documents for document keys, when it's available.
diff --git a/packages/core/src/utils/request.ts b/packages/core/src/utils/request.ts
index 110e5914..eba0ac9b 100644
--- a/packages/core/src/utils/request.ts
+++ b/packages/core/src/utils/request.ts
@@ -12,6 +12,10 @@ import type {
   RequestExtensions,
 } from '../types';
 
+type PersistedDocumentNode = TypedDocumentNode & {
+  documentId?: string;
+};
+
 /** A `DocumentNode` annotated with its hashed key.
  * @internal
  */
@@ -90,11 +94,16 @@ export const stringifyDocument = (
 const hashDocument = (
   node: string | DefinitionNode | DocumentNode
 ): HashValue => {
-  let key = phash(stringifyDocument(node));
-  // Add the operation name to the produced hash
-  if ((node as DocumentNode).definitions) {
-    const operationName = getOperationName(node as DocumentNode);
-    if (operationName) key = phash(`\n# ${operationName}`, key);
+  let key: HashValue;
+  if ((node as PersistedDocumentNode).documentId) {
+    key = phash((node as PersistedDocumentNode).documentId!);
+  } else {
+    key = phash(stringifyDocument(node));
+    // Add the operation name to the produced hash
+    if ((node as DocumentNode).definitions) {
+      const operationName = getOperationName(node as DocumentNode);
+      if (operationName) key = phash(`\n# ${operationName}`, key);
+    }
   }
   return key;
 };


Test patch:
diff --git a/packages/core/src/utils/request.test.ts b/packages/core/src/utils/request.test.ts
index 9373ff80..f6079f9f 100644
--- a/packages/core/src/utils/request.test.ts
+++ b/packages/core/src/utils/request.test.ts
@@ -81,6 +81,16 @@ describe('createRequest', () => {
       variables: { test: 5 },
     });
   });
+
+  it('should hash persisted documents consistently', () => {
+    const doc = parse('{ testG }');
+    const docPersisted = parse('{ testG }');
+    (docPersisted as any).documentId = 'testG';
+
+    const req = createRequest(doc, undefined);
+    const reqPersisted = createRequest(docPersisted, undefined);
+    expect(req.key).not.toBe(reqPersisted.key);
+  });
 });
 
 describe('stringifyDocument ', () => {