Mirror: The highly customizable and versatile GraphQL client with which you add on features like normalized caching as you grow.
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

feat: Add support for persisted documents via the `documentId` property (#3515)

authored by

Phil Pluckthun and committed by
GitHub
614c7326 e8e16bba

+51 -7
+5
.changeset/two-bananas-grow.md
··· 1 + --- 2 + '@urql/core': minor 3 + --- 4 + 5 + Add support for sending persisted documents. Any `DocumentNode` with no/empty definitions and a `documentId` property is considered a persisted document. When this is detected a `documentId` parameter rather than a `query` string is sent to the GraphQL API, similar to Automatic Persisted Queries (APQs). However, APQs are only supported via `@urql/exchange-persisted`, while support for `documentId` is now built-in.
+18
packages/core/src/internal/fetchOptions.test.ts
··· 1 1 // @vitest-environment jsdom 2 2 3 3 import { expect, describe, it } from 'vitest'; 4 + import { Kind } from '@0no-co/graphql.web'; 4 5 import { makeOperation } from '../utils/operation'; 5 6 import { queryOperation, mutationOperation } from '../test-utils'; 6 7 import { makeFetchBody, makeFetchURL, makeFetchOptions } from './fetchOptions'; ··· 10 11 const body = makeFetchBody(queryOperation); 11 12 expect(body).toMatchInlineSnapshot(` 12 13 { 14 + "documentId": undefined, 13 15 "extensions": undefined, 14 16 "operationName": "getUser", 15 17 "query": "query getUser($name: String) { ··· 41 43 42 44 apqOperation.extensions.persistedQuery!.miss = true; 43 45 expect(makeFetchBody(apqOperation).query).not.toBe(undefined); 46 + }); 47 + 48 + it('omits the query property when query is a persisted document', () => { 49 + // A persisted documents is one that carries a `documentId` property and 50 + // has no definitions 51 + const persistedOperation = makeOperation(queryOperation.kind, { 52 + ...queryOperation, 53 + query: { 54 + kind: Kind.DOCUMENT, 55 + definitions: [], 56 + documentId: 'TestDocumentId', 57 + }, 58 + }); 59 + 60 + expect(makeFetchBody(persistedOperation).query).toBe(undefined); 61 + expect(makeFetchBody(persistedOperation).documentId).toBe('TestDocumentId'); 44 62 }); 45 63 }); 46 64
+22 -6
packages/core/src/internal/fetchOptions.ts
··· 10 10 /** Abstract definition of the JSON data sent during GraphQL HTTP POST requests. */ 11 11 export interface FetchBody { 12 12 query?: string; 13 + documentId?: string; 13 14 operationName: string | undefined; 14 15 variables: undefined | Record<string, any>; 15 16 extensions: undefined | Record<string, any>; ··· 24 25 Data = any, 25 26 Variables extends AnyVariables = AnyVariables, 26 27 >(request: Omit<GraphQLRequest<Data, Variables>, 'key'>): FetchBody { 27 - const isAPQ = 28 - request.extensions && 29 - request.extensions.persistedQuery && 30 - !request.extensions.persistedQuery.miss; 31 - return { 32 - query: isAPQ ? undefined : stringifyDocument(request.query), 28 + const body: FetchBody = { 29 + query: undefined, 30 + documentId: undefined, 33 31 operationName: getOperationName(request.query), 34 32 variables: request.variables || undefined, 35 33 extensions: request.extensions, 36 34 }; 35 + 36 + if ( 37 + 'documentId' in request.query && 38 + request.query.documentId && 39 + // NOTE: We have to check that the document will definitely be sent 40 + // as a persisted document to avoid breaking changes 41 + (!request.query.definitions || !request.query.definitions.length) 42 + ) { 43 + body.documentId = request.query.documentId; 44 + } else if ( 45 + !request.extensions || 46 + !request.extensions.persistedQuery || 47 + !!request.extensions.persistedQuery.miss 48 + ) { 49 + body.query = stringifyDocument(request.query); 50 + } 51 + 52 + return body; 37 53 } 38 54 39 55 /** Creates a URL that will be called for a GraphQL HTTP request.
+6 -1
packages/core/src/types.ts
··· 9 9 import type { Client } from './client'; 10 10 import type { CombinedError } from './utils/error'; 11 11 12 + /** A GraphQL persisted document will contain `documentId` that replaces its definitions */ 13 + export interface PersistedDocument extends DocumentNode { 14 + documentId?: string; 15 + } 16 + 12 17 /** A GraphQL `DocumentNode` with attached generics for its result data and variables. 13 18 * 14 19 * @remarks ··· 341 346 * In `urql`, we expect a document to only contain a single operation that is executed rather than 342 347 * multiple ones by convention. 343 348 */ 344 - query: DocumentNode | TypedDocumentNode<Data, Variables>; 349 + query: DocumentNode | PersistedDocument | TypedDocumentNode<Data, Variables>; 345 350 /** Variables used to execute the `query` document. 346 351 * 347 352 * @remarks