Skip to content

Commit 0fae2a8

Browse files
committed
feat: replace mode flag with includeQuery
The availability of the documentId on the query already indicated if we want to do persistent queries vs non persistent queries. With the flag `includeQuery` we can now always append the query
1 parent 76b6a03 commit 0fae2a8

File tree

5 files changed

+87
-113
lines changed

5 files changed

+87
-113
lines changed

.changeset/gentle-plants-rule.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"@labdigital/graphql-fetcher": patch
3+
---
4+
5+
Replace `mode` flag via simpler `includeQuery` to indicate if the query should be sent even if there is a documentId available

src/client.ts

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,6 @@ import {
66
createRequestBody,
77
createRequestURL,
88
isPersistedQuery,
9-
type ModeFlags,
109
} from "request";
1110
import invariant from "tiny-invariant";
1211
import {
@@ -41,7 +40,10 @@ type Options = {
4140
*/
4241
defaultHeaders?: Headers | Record<string, string>;
4342

44-
mode?: ModeFlags;
43+
/**
44+
* If the query should always be sent, even if there is a document id
45+
*/
46+
includeQuery?: boolean;
4547

4648
/**
4749
* Function to customize creating the documentId from a query
@@ -72,7 +74,7 @@ export const initClientFetcher =
7274
persistedQueries = false,
7375
defaultTimeout = 30000,
7476
defaultHeaders = {},
75-
mode = "document",
77+
includeQuery = false,
7678
createDocumentId = getDocumentId,
7779
}: Options = {},
7880
): ClientFetcher =>
@@ -96,7 +98,12 @@ export const initClientFetcher =
9698

9799
const query = isNode(astNode) ? print(astNode) : astNode.toString();
98100
const documentId = createDocumentId(astNode);
99-
const request = await createRequest(mode, query, variables, documentId);
101+
const request = await createRequest(
102+
query,
103+
variables,
104+
documentId,
105+
includeQuery,
106+
);
100107

101108
let response: GqlResponse<TResponse> | undefined = undefined;
102109
const headers = mergeHeaders({ ...defaultHeaders, ...options.headers });

src/request.test.ts

Lines changed: 31 additions & 56 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,11 @@
11
import { createRequestSearchParams, type GraphQLRequest } from "request";
22
import { expect, it } from "vitest";
33

4-
it.each(["both", "document", "persistent"])(
5-
"createRequestURL - mode=%s",
6-
(mode) => {
4+
it.each([true, false])(
5+
"createRequestSearchParams - includeQuery=%s",
6+
(includeQuery) => {
77
const data = createRequestSearchParams({
8-
mode: mode,
8+
includeQuery: includeQuery,
99
query: "query { hello }",
1010
variables: { name: "world" },
1111
operationName: "hello",
@@ -23,42 +23,28 @@ it.each(["both", "document", "persistent"])(
2323
result[key] = value;
2424
});
2525

26-
switch (mode) {
27-
case "both": {
28-
expect(result).toStrictEqual({
29-
documentId: "123",
30-
op: "hello",
31-
variables: '{"name":"world"}',
32-
extensions: '{"persistedQuery":{"version":1,"sha256Hash":"123"}}',
33-
});
34-
break;
35-
}
36-
case "persisted": {
37-
expect(result).toStrictEqual({
38-
documentId: "123",
39-
op: "hello",
40-
variables: '{"name":"world"}',
41-
extensions: '{"persistedQuery":{"version":1,"sha256Hash":"123"}}',
42-
});
43-
break;
44-
}
45-
case "document": {
46-
expect(result).toStrictEqual({
47-
op: "hello",
48-
variables: '{"name":"world"}',
49-
extensions: '{"persistedQuery":{"version":1,"sha256Hash":"123"}}',
50-
});
51-
break;
52-
}
26+
if (includeQuery) {
27+
expect(result).toStrictEqual({
28+
documentId: "123",
29+
op: "hello",
30+
variables: '{"name":"world"}',
31+
extensions: '{"persistedQuery":{"version":1,"sha256Hash":"123"}}',
32+
});
33+
} else {
34+
expect(result).toStrictEqual({
35+
documentId: "123",
36+
op: "hello",
37+
variables: '{"name":"world"}',
38+
});
5339
}
5440
},
5541
);
5642

57-
it.each(["both", "document", "persistent"])(
58-
"createRequestURL - minimal mode=%s",
59-
(mode) => {
43+
it.each([true, false])(
44+
"createRequestSearchParams - minimal includeQuery=%s",
45+
(includeQuery) => {
6046
const data = createRequestSearchParams({
61-
mode: mode,
47+
includeQuery,
6248
query: "query { hello }",
6349
variables: {},
6450
operationName: "hello",
@@ -70,27 +56,16 @@ it.each(["both", "document", "persistent"])(
7056
result[key] = value;
7157
});
7258

73-
switch (mode) {
74-
case "both": {
75-
expect(result).toStrictEqual({
76-
documentId: "123",
77-
op: "hello",
78-
});
79-
break;
80-
}
81-
case "persisted": {
82-
expect(result).toStrictEqual({
83-
documentId: "123",
84-
op: "hello",
85-
});
86-
break;
87-
}
88-
case "document": {
89-
expect(result).toStrictEqual({
90-
op: "hello",
91-
});
92-
break;
93-
}
59+
if (includeQuery) {
60+
expect(result).toStrictEqual({
61+
documentId: "123",
62+
op: "hello",
63+
});
64+
} else {
65+
expect(result).toStrictEqual({
66+
documentId: "123",
67+
op: "hello",
68+
});
9469
}
9570
},
9671
);

src/request.ts

Lines changed: 31 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -1,23 +1,24 @@
11
import type { DocumentTypeDecoration } from "@graphql-typed-document-node/core";
22
import { createSha256, extractOperationName, pruneObject } from "helpers";
33

4-
export type ModeFlags = "persisted" | "document" | "both";
5-
64
export type DocumentIdFn = <TResult, TVariables>(
75
query: DocumentTypeDecoration<TResult, TVariables>,
86
) => string | undefined;
97

108
export type GraphQLRequest<TVariables> = {
11-
mode: ModeFlags;
129
operationName: string;
1310
query: string | undefined;
1411
documentId: string | undefined;
1512
variables: TVariables | undefined;
1613
extensions: Record<string, unknown>;
14+
includeQuery: boolean;
1715
};
1816

19-
export const isPersistedQuery = <T>(request: GraphQLRequest<T>): boolean =>
20-
request.mode === "persisted" || request.mode === "both";
17+
export const isPersistedQuery = <TVariables>(
18+
request: GraphQLRequest<TVariables>,
19+
) => {
20+
return request.documentId !== undefined;
21+
};
2122

2223
export const createRequestSearchParams = <TVariables>(
2324
request: GraphQLRequest<TVariables>,
@@ -26,22 +27,18 @@ export const createRequestSearchParams = <TVariables>(
2627
op: request.operationName,
2728
};
2829

29-
if (request.mode === "both" || request.mode === "persisted") {
30-
if (!request.documentId) {
31-
throw new Error("Persisted query mode requires a documentId");
32-
}
33-
params.documentId = request.documentId;
34-
}
35-
3630
params = {
3731
...params,
3832
...pruneObject({
33+
documentId: request.documentId,
3934
variables: isNotEmpty(request.variables)
4035
? JSON.stringify(request.variables)
4136
: undefined,
42-
extensions: isNotEmpty(request.extensions)
43-
? JSON.stringify(request.extensions)
44-
: undefined,
37+
extensions:
38+
isNotEmpty(request.extensions) &&
39+
(!request.documentId || request.includeQuery)
40+
? JSON.stringify(request.extensions)
41+
: undefined,
4542
}),
4643
};
4744
return new URLSearchParams(params);
@@ -66,53 +63,40 @@ export const createRequestURL = <TVariables>(
6663
export const createRequestBody = <TVariables>(
6764
request: GraphQLRequest<TVariables>,
6865
) => {
69-
switch (request.mode) {
70-
case "both":
71-
return JSON.stringify(
72-
pruneObject({
73-
documentId: request.documentId,
74-
query: request.query,
75-
variables: request.variables,
76-
extensions: request.extensions,
77-
}),
78-
);
79-
case "document":
80-
return JSON.stringify(
81-
pruneObject({
82-
query: request.query,
83-
variables: request.variables,
84-
extensions: request.extensions,
85-
}),
86-
);
87-
case "persisted":
88-
if (!request.documentId) {
89-
throw new Error("Persisted query mode requires a documentId");
90-
}
91-
return JSON.stringify(
92-
pruneObject({
93-
documentId: request.documentId,
94-
variables: request.variables,
95-
extensions: request.extensions,
96-
}),
97-
);
66+
if (!request.documentId || request.includeQuery) {
67+
return JSON.stringify(
68+
pruneObject({
69+
documentId: request.documentId,
70+
query: request.query,
71+
variables: request.variables,
72+
extensions: request.extensions,
73+
}),
74+
);
9875
}
76+
return JSON.stringify(
77+
pruneObject({
78+
documentId: request.documentId,
79+
variables: request.variables,
80+
extensions: request.extensions,
81+
}),
82+
);
9983
};
10084

10185
export const createRequest = async <TVariables>(
102-
mode: ModeFlags,
10386
query: string,
10487
variables: TVariables,
10588
documentId?: string,
89+
includeQuery?: boolean,
10690
): Promise<GraphQLRequest<TVariables>> => {
10791
const operationName = extractOperationName(query) || "(GraphQL)";
10892

10993
const request = {
110-
mode,
11194
documentId,
11295
query,
11396
operationName,
11497
variables,
11598
extensions: {},
99+
includeQuery: includeQuery ?? false,
116100
};
117101

118102
/**
@@ -122,7 +106,7 @@ export const createRequest = async <TVariables>(
122106
* Note that these are not the same hashes as the documentId, which is
123107
* used for allowlisting of query documents
124108
*/
125-
if (mode === "document" || mode === "both") {
109+
if (!documentId || request.includeQuery) {
126110
request.extensions = {
127111
persistedQuery: {
128112
version: 1,

src/server.ts

Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -46,11 +46,9 @@ type Options = {
4646
defaultHeaders?: Headers | Record<string, string>;
4747

4848
/**
49-
* Mode to use for sending requests, persisted means that only the documentId
50-
* will be sent, document means that the full query will be sent and both
51-
* means that the full query and the documentId will be sent
49+
* If the query should always be sent, even if there is a document id
5250
*/
53-
mode?: "persisted" | "document" | "both";
51+
includeQuery?: boolean;
5452

5553
/**
5654
* Function to customize creating the documentId from a query
@@ -79,7 +77,7 @@ export const initServerFetcher =
7977
dangerouslyDisableCache = false,
8078
defaultTimeout = 30000,
8179
defaultHeaders = {},
82-
mode = "document",
80+
includeQuery = false,
8381
createDocumentId = getDocumentId,
8482
}: Options = {},
8583
) =>
@@ -94,7 +92,12 @@ export const initServerFetcher =
9492
const query = isNode(astNode) ? print(astNode) : astNode.toString();
9593

9694
const documentId = createDocumentId(astNode);
97-
const request = await createRequest(mode, query, variables, documentId);
95+
const request = await createRequest(
96+
query,
97+
variables,
98+
documentId,
99+
includeQuery,
100+
);
98101
const requestOptions: RequestOptions = {
99102
signal: options.signal ?? AbortSignal.timeout(defaultTimeout),
100103
headers: mergeHeaders({ ...defaultHeaders, ...options.headers }),

0 commit comments

Comments
 (0)