···11+---
22+'@urql/core': minor
33+---
44+55+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
···11// @vitest-environment jsdom
2233import { expect, describe, it } from 'vitest';
44+import { Kind } from '@0no-co/graphql.web';
45import { makeOperation } from '../utils/operation';
56import { queryOperation, mutationOperation } from '../test-utils';
67import { makeFetchBody, makeFetchURL, makeFetchOptions } from './fetchOptions';
···1011 const body = makeFetchBody(queryOperation);
1112 expect(body).toMatchInlineSnapshot(`
1213 {
1414+ "documentId": undefined,
1315 "extensions": undefined,
1416 "operationName": "getUser",
1517 "query": "query getUser($name: String) {
···41434244 apqOperation.extensions.persistedQuery!.miss = true;
4345 expect(makeFetchBody(apqOperation).query).not.toBe(undefined);
4646+ });
4747+4848+ it('omits the query property when query is a persisted document', () => {
4949+ // A persisted documents is one that carries a `documentId` property and
5050+ // has no definitions
5151+ const persistedOperation = makeOperation(queryOperation.kind, {
5252+ ...queryOperation,
5353+ query: {
5454+ kind: Kind.DOCUMENT,
5555+ definitions: [],
5656+ documentId: 'TestDocumentId',
5757+ },
5858+ });
5959+6060+ expect(makeFetchBody(persistedOperation).query).toBe(undefined);
6161+ expect(makeFetchBody(persistedOperation).documentId).toBe('TestDocumentId');
4462 });
4563});
4664
+22-6
packages/core/src/internal/fetchOptions.ts
···1010/** Abstract definition of the JSON data sent during GraphQL HTTP POST requests. */
1111export interface FetchBody {
1212 query?: string;
1313+ documentId?: string;
1314 operationName: string | undefined;
1415 variables: undefined | Record<string, any>;
1516 extensions: undefined | Record<string, any>;
···2425 Data = any,
2526 Variables extends AnyVariables = AnyVariables,
2627>(request: Omit<GraphQLRequest<Data, Variables>, 'key'>): FetchBody {
2727- const isAPQ =
2828- request.extensions &&
2929- request.extensions.persistedQuery &&
3030- !request.extensions.persistedQuery.miss;
3131- return {
3232- query: isAPQ ? undefined : stringifyDocument(request.query),
2828+ const body: FetchBody = {
2929+ query: undefined,
3030+ documentId: undefined,
3331 operationName: getOperationName(request.query),
3432 variables: request.variables || undefined,
3533 extensions: request.extensions,
3634 };
3535+3636+ if (
3737+ 'documentId' in request.query &&
3838+ request.query.documentId &&
3939+ // NOTE: We have to check that the document will definitely be sent
4040+ // as a persisted document to avoid breaking changes
4141+ (!request.query.definitions || !request.query.definitions.length)
4242+ ) {
4343+ body.documentId = request.query.documentId;
4444+ } else if (
4545+ !request.extensions ||
4646+ !request.extensions.persistedQuery ||
4747+ !!request.extensions.persistedQuery.miss
4848+ ) {
4949+ body.query = stringifyDocument(request.query);
5050+ }
5151+5252+ return body;
3753}
38543955/** Creates a URL that will be called for a GraphQL HTTP request.
+6-1
packages/core/src/types.ts
···99import type { Client } from './client';
1010import type { CombinedError } from './utils/error';
11111212+/** A GraphQL persisted document will contain `documentId` that replaces its definitions */
1313+export interface PersistedDocument extends DocumentNode {
1414+ documentId?: string;
1515+}
1616+1217/** A GraphQL `DocumentNode` with attached generics for its result data and variables.
1318 *
1419 * @remarks
···341346 * In `urql`, we expect a document to only contain a single operation that is executed rather than
342347 * multiple ones by convention.
343348 */
344344- query: DocumentNode | TypedDocumentNode<Data, Variables>;
349349+ query: DocumentNode | PersistedDocument | TypedDocumentNode<Data, Variables>;
345350 /** Variables used to execute the `query` document.
346351 *
347352 * @remarks