From 22b7e4c4ea80f2ffbb3f62b4956cb2decb970f69 Mon Sep 17 00:00:00 2001
From: Paul Vaneveld
Date: Wed, 22 Oct 2025 10:36:46 +0200
Subject: [PATCH 1/8] feat: make apq a explicitly opt in feature
* fixed an issue were POST calls where implicitly converted into GET calls when a documentId was provided, even though apq was not enabled.
* removed a deprecated isPersistedQuery flag
---
.changeset/pre.json | 8 ++++++++
.changeset/rich-ants-shop.md | 5 +++++
package.json | 2 +-
src/client.test.ts | 2 +-
src/client.ts | 8 +-------
5 files changed, 16 insertions(+), 9 deletions(-)
create mode 100644 .changeset/pre.json
create mode 100644 .changeset/rich-ants-shop.md
diff --git a/.changeset/pre.json b/.changeset/pre.json
new file mode 100644
index 0000000..748c6ce
--- /dev/null
+++ b/.changeset/pre.json
@@ -0,0 +1,8 @@
+{
+ "mode": "pre",
+ "tag": "beta",
+ "initialVersions": {
+ "@labdigital/graphql-fetcher": "2.0.0"
+ },
+ "changesets": []
+}
diff --git a/.changeset/rich-ants-shop.md b/.changeset/rich-ants-shop.md
new file mode 100644
index 0000000..205905d
--- /dev/null
+++ b/.changeset/rich-ants-shop.md
@@ -0,0 +1,5 @@
+---
+"@labdigital/graphql-fetcher": major
+---
+
+Remove deprecated is "isPersistedQuery" and make apq explicitly opt in
diff --git a/package.json b/package.json
index 9a1d227..4032b22 100644
--- a/package.json
+++ b/package.json
@@ -70,4 +70,4 @@
"react-dom": ">= 18.2.0"
},
"packageManager": "pnpm@9.15.3"
-}
+}
\ No newline at end of file
diff --git a/src/client.test.ts b/src/client.test.ts
index a1983d4..f21d23c 100644
--- a/src/client.test.ts
+++ b/src/client.test.ts
@@ -37,7 +37,7 @@ describe("gqlClientFetch", () => {
const fetcher = initClientFetcher("https://localhost/graphql");
const persistedFetcher = initClientFetcher("https://localhost/graphql", {
- persistedQueries: true,
+ apq: true,
});
it("should perform a query", async () => {
diff --git a/src/client.ts b/src/client.ts
index a8b0a11..e8ecb37 100644
--- a/src/client.ts
+++ b/src/client.ts
@@ -25,9 +25,6 @@ type Options = {
*/
apq?: boolean;
- /** Deprecated: use `apq: ` */
- persistedQueries?: boolean;
-
/**
* Sets the default timeout duration in ms after which a request will throw a timeout error
*
@@ -71,7 +68,6 @@ export const initClientFetcher =
endpoint: string,
{
apq = false,
- persistedQueries = false,
defaultTimeout = 30000,
defaultHeaders = {},
includeQuery = false,
@@ -110,10 +106,8 @@ export const initClientFetcher =
const queryType = getQueryType(query);
- apq = apq || persistedQueries;
-
// For queries we can use GET requests if persisted queries are enabled
- if (queryType === "query" && (apq || isPersistedQuery(request))) {
+ if (queryType === "query" && apq) {
const url = createRequestURL(endpoint, request);
response = await parseResponse>(() =>
fetch(url.toString(), {
From 0fa98ebb4ad34c498f38cc793109e9c0b468951d Mon Sep 17 00:00:00 2001
From: Paul Vaneveld
Date: Wed, 22 Oct 2025 15:02:57 +0200
Subject: [PATCH 2/8] chore: version bump
---
.changeset/pre.json | 4 +++-
CHANGELOG.md | 6 ++++++
package.json | 2 +-
3 files changed, 10 insertions(+), 2 deletions(-)
diff --git a/.changeset/pre.json b/.changeset/pre.json
index 748c6ce..e53cfb9 100644
--- a/.changeset/pre.json
+++ b/.changeset/pre.json
@@ -4,5 +4,7 @@
"initialVersions": {
"@labdigital/graphql-fetcher": "2.0.0"
},
- "changesets": []
+ "changesets": [
+ "rich-ants-shop"
+ ]
}
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 8bda83a..e34256b 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,5 +1,11 @@
# @labdigital/react-query-opal
+## 3.0.0-beta.0
+
+### Major Changes
+
+- c570e03: Remove deprecated is "isPersistedQuery" and make apq explicitly opt in
+
## 2.0.0
### Minor Changes
diff --git a/package.json b/package.json
index 4032b22..c7b54f4 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
{
"name": "@labdigital/graphql-fetcher",
- "version": "2.0.0",
+ "version": "3.0.0-beta.0",
"description": "Custom fetcher for react-query to use with @labdigital/node-federated-token",
"type": "module",
"main": "./dist/index.cjs",
From 3755bc98d9e30c57026189baa85bf683534b9315 Mon Sep 17 00:00:00 2001
From: Paul Vaneveld
Date: Wed, 22 Oct 2025 16:56:47 +0200
Subject: [PATCH 3/8] Revert "chore: version bump"
This reverts commit 0c672362c50507fecbb05eca980ad99ce99a25f2.
---
.changeset/pre.json | 4 +---
CHANGELOG.md | 6 ------
package.json | 2 +-
3 files changed, 2 insertions(+), 10 deletions(-)
diff --git a/.changeset/pre.json b/.changeset/pre.json
index e53cfb9..748c6ce 100644
--- a/.changeset/pre.json
+++ b/.changeset/pre.json
@@ -4,7 +4,5 @@
"initialVersions": {
"@labdigital/graphql-fetcher": "2.0.0"
},
- "changesets": [
- "rich-ants-shop"
- ]
+ "changesets": []
}
diff --git a/CHANGELOG.md b/CHANGELOG.md
index e34256b..8bda83a 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,11 +1,5 @@
# @labdigital/react-query-opal
-## 3.0.0-beta.0
-
-### Major Changes
-
-- c570e03: Remove deprecated is "isPersistedQuery" and make apq explicitly opt in
-
## 2.0.0
### Minor Changes
diff --git a/package.json b/package.json
index c7b54f4..4032b22 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
{
"name": "@labdigital/graphql-fetcher",
- "version": "3.0.0-beta.0",
+ "version": "2.0.0",
"description": "Custom fetcher for react-query to use with @labdigital/node-federated-token",
"type": "module",
"main": "./dist/index.cjs",
From 8149bf792770199ae598e0c2e84155f00425662a Mon Sep 17 00:00:00 2001
From: Paul Vaneveld
Date: Wed, 22 Oct 2025 16:59:10 +0200
Subject: [PATCH 4/8] Reapply "chore: version bump"
This reverts commit c279db512aa2cbf2f2631fbc0fff90e526dd1b64.
---
.changeset/pre.json | 4 +++-
CHANGELOG.md | 6 ++++++
package.json | 2 +-
3 files changed, 10 insertions(+), 2 deletions(-)
diff --git a/.changeset/pre.json b/.changeset/pre.json
index 748c6ce..e53cfb9 100644
--- a/.changeset/pre.json
+++ b/.changeset/pre.json
@@ -4,5 +4,7 @@
"initialVersions": {
"@labdigital/graphql-fetcher": "2.0.0"
},
- "changesets": []
+ "changesets": [
+ "rich-ants-shop"
+ ]
}
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 8bda83a..e34256b 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,5 +1,11 @@
# @labdigital/react-query-opal
+## 3.0.0-beta.0
+
+### Major Changes
+
+- c570e03: Remove deprecated is "isPersistedQuery" and make apq explicitly opt in
+
## 2.0.0
### Minor Changes
diff --git a/package.json b/package.json
index 4032b22..c7b54f4 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
{
"name": "@labdigital/graphql-fetcher",
- "version": "2.0.0",
+ "version": "3.0.0-beta.0",
"description": "Custom fetcher for react-query to use with @labdigital/node-federated-token",
"type": "module",
"main": "./dist/index.cjs",
From b4df3f0d1cec4e79e2db21c2b883bd30c00a94ef Mon Sep 17 00:00:00 2001
From: Paul Vaneveld
Date: Wed, 22 Oct 2025 17:07:20 +0200
Subject: [PATCH 5/8] Revert "Reapply "chore: version bump""
This reverts commit f83a91fdd542c604d565c5303efcbb4dbd0fe872.
---
.changeset/pre.json | 4 +---
CHANGELOG.md | 6 ------
package.json | 2 +-
3 files changed, 2 insertions(+), 10 deletions(-)
diff --git a/.changeset/pre.json b/.changeset/pre.json
index e53cfb9..748c6ce 100644
--- a/.changeset/pre.json
+++ b/.changeset/pre.json
@@ -4,7 +4,5 @@
"initialVersions": {
"@labdigital/graphql-fetcher": "2.0.0"
},
- "changesets": [
- "rich-ants-shop"
- ]
+ "changesets": []
}
diff --git a/CHANGELOG.md b/CHANGELOG.md
index e34256b..8bda83a 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,11 +1,5 @@
# @labdigital/react-query-opal
-## 3.0.0-beta.0
-
-### Major Changes
-
-- c570e03: Remove deprecated is "isPersistedQuery" and make apq explicitly opt in
-
## 2.0.0
### Minor Changes
diff --git a/package.json b/package.json
index c7b54f4..4032b22 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
{
"name": "@labdigital/graphql-fetcher",
- "version": "3.0.0-beta.0",
+ "version": "2.0.0",
"description": "Custom fetcher for react-query to use with @labdigital/node-federated-token",
"type": "module",
"main": "./dist/index.cjs",
From 0b99c22d541495ddc2254c9006d8f562b20d1fb6 Mon Sep 17 00:00:00 2001
From: Paul Vaneveld
Date: Wed, 22 Oct 2025 17:08:22 +0200
Subject: [PATCH 6/8] chore: verion files
---
.changeset/pre.json | 4 +++-
CHANGELOG.md | 6 ++++++
package.json | 2 +-
3 files changed, 10 insertions(+), 2 deletions(-)
diff --git a/.changeset/pre.json b/.changeset/pre.json
index 748c6ce..e53cfb9 100644
--- a/.changeset/pre.json
+++ b/.changeset/pre.json
@@ -4,5 +4,7 @@
"initialVersions": {
"@labdigital/graphql-fetcher": "2.0.0"
},
- "changesets": []
+ "changesets": [
+ "rich-ants-shop"
+ ]
}
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 8bda83a..73184a2 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,5 +1,11 @@
# @labdigital/react-query-opal
+## 3.0.0-beta.0
+
+### Major Changes
+
+- ff7f7e4: Remove deprecated is "isPersistedQuery" and make apq explicitly opt in
+
## 2.0.0
### Minor Changes
diff --git a/package.json b/package.json
index 4032b22..c7b54f4 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
{
"name": "@labdigital/graphql-fetcher",
- "version": "2.0.0",
+ "version": "3.0.0-beta.0",
"description": "Custom fetcher for react-query to use with @labdigital/node-federated-token",
"type": "module",
"main": "./dist/index.cjs",
From 6d280ddca9ecee94c54c9c2429946b2d530ca225 Mon Sep 17 00:00:00 2001
From: Paul Vaneveld
Date: Mon, 27 Oct 2025 13:56:36 +0100
Subject: [PATCH 7/8] feat: explicitly require apq on server fetcher
---
src/server.test.ts | 56 +++++++++++++++--
src/server.ts | 150 +++++++++++++++++++++++++++++----------------
2 files changed, 148 insertions(+), 58 deletions(-)
diff --git a/src/server.test.ts b/src/server.test.ts
index a739085..94ad16a 100644
--- a/src/server.test.ts
+++ b/src/server.test.ts
@@ -25,7 +25,9 @@ const errorResponse = JSON.stringify({
describe("gqlServerFetch", () => {
it("should fetch a persisted query", async () => {
- const gqlServerFetch = initServerFetcher("https://localhost/graphql");
+ const gqlServerFetch = initServerFetcher("https://localhost/graphql", {
+ apq: true,
+ });
const mockedFetch = fetchMock.mockResponse(successResponse);
const gqlResponse = await gqlServerFetch(
query,
@@ -57,7 +59,9 @@ describe("gqlServerFetch", () => {
});
it("should persist the query if it wasn't persisted yet", async () => {
- const gqlServerFetch = initServerFetcher("https://localhost/graphql");
+ const gqlServerFetch = initServerFetcher("https://localhost/graphql", {
+ apq: true,
+ });
// Mock server saying: 'PersistedQueryNotFound'
const mockedFetch = fetchMock
.mockResponseOnce(errorResponse)
@@ -134,7 +138,9 @@ describe("gqlServerFetch", () => {
});
it("should fetch a persisted query without revalidate", async () => {
- const gqlServerFetch = initServerFetcher("https://localhost/graphql");
+ const gqlServerFetch = initServerFetcher("https://localhost/graphql", {
+ apq: true,
+ });
const mockedFetch = fetchMock.mockResponse(successResponse);
const gqlResponse = await gqlServerFetch(
query,
@@ -166,7 +172,9 @@ describe("gqlServerFetch", () => {
});
it("should fetch a with custom headers", async () => {
- const gqlServerFetch = initServerFetcher("https://localhost/graphql");
+ const gqlServerFetch = initServerFetcher("https://localhost/graphql", {
+ apq: true,
+ });
const mockedFetch = fetchMock.mockResponse(successResponse);
const gqlResponse = await gqlServerFetch(
query,
@@ -257,6 +265,7 @@ describe("gqlServerFetch", () => {
const gqlServerFetch = initServerFetcher("https://localhost/graphql", {
defaultTimeout: 1,
+ apq: true,
});
fetchMock.mockResponse(successResponse);
@@ -338,3 +347,42 @@ describe("gqlServerFetch", () => {
expect(fetchMock).toHaveBeenCalledTimes(1);
});
});
+
+it("should skip persisted queries if operation apq is disabled", async () => {
+ const gqlServerFetch = initServerFetcher("https://localhost/graphql", {
+ apq: false,
+ });
+ const mockedFetch = fetchMock.mockResponseOnce(successResponse);
+
+ const gqlResponse = await gqlServerFetch(
+ query,
+ { myVar: "baz" },
+ {
+ next: { revalidate: 900 },
+ },
+ );
+
+ expect(gqlResponse).toEqual(response);
+ expect(mockedFetch).toHaveBeenCalledTimes(1);
+ expect(mockedFetch).toHaveBeenNthCalledWith(
+ 1,
+ "https://localhost/graphql?op=myQuery",
+ {
+ method: "POST",
+ body: JSON.stringify({
+ query: query.toString(),
+ variables: { myVar: "baz" },
+ extensions: {
+ persistedQuery: {
+ version: 1,
+ sha256Hash: await createSha256(query.toString()),
+ },
+ },
+ }),
+ headers: new Headers({
+ "Content-Type": "application/json",
+ }),
+ next: { revalidate: 900 },
+ },
+ );
+});
diff --git a/src/server.ts b/src/server.ts
index b5ce68e..6aa2493 100644
--- a/src/server.ts
+++ b/src/server.ts
@@ -31,6 +31,12 @@ type RequestOptions = {
};
type Options = {
+ /**
+ * Enable use of automated persisted queries, this will always add a extra
+ * roundtrip to the server if queries aren't cacheable
+ * @default false
+ */
+ apq?: boolean;
/**
* Disables all forms of caching for the fetcher, use only in development
*
@@ -81,6 +87,7 @@ export const initServerFetcher =
defaultTimeout = undefined,
defaultHeaders = {},
includeQuery = false,
+ apq = false,
createDocumentId = getDocumentId,
}: Options = {},
) =>
@@ -137,63 +144,36 @@ export const initServerFetcher =
});
}
- // Skip automatic persisted queries if operation is a mutation
const queryType = getQueryType(query);
- if (queryType === "mutation") {
- return tracer.startActiveSpan(request.operationName, async (span) => {
- try {
- const response = await gqlPost(
- url,
- request,
- { cache, next },
- requestOptions,
- );
-
- span.end();
- return response as GqlResponse;
- } catch (err: unknown) {
- span.setStatus({
- code: SpanStatusCode.ERROR,
- message: err instanceof Error ? err.message : String(err),
- });
- throw err;
- }
- });
+ if (!apq) {
+ return post(
+ request,
+ url,
+ cache,
+ next,
+ requestOptions,
+ );
}
- // Otherwise, try to get the cached query
- return tracer.startActiveSpan(request.operationName, async (span) => {
- try {
- let response = await gqlPersistedQuery(
- url,
- request,
- { cache, next },
- requestOptions,
- );
-
- // If this is not a persisted query, but we tried to use automatic
- // persisted queries (APQ) then we retry with a POST
- if (!isPersistedQuery(request) && hasPersistedQueryError(response)) {
- // If the cached query doesn't exist, fall back to POST request and
- // let the server cache it.
- response = await gqlPost(
- url,
- request,
- { cache, next },
- requestOptions,
- );
- }
+ // if apq is enabled, only queries are converted into get calls
+ // https://www.apollographql.com/docs/apollo-server/performance/apq#using-get-requests-with-apq-on-a-cdn
+ if (queryType === "mutation") {
+ return post(
+ request,
+ url,
+ cache,
+ next,
+ requestOptions,
+ );
+ }
- span.end();
- return response as GqlResponse;
- } catch (err: any) {
- span.setStatus({
- code: SpanStatusCode.ERROR,
- message: err?.message ?? String(err),
- });
- throw err;
- }
- });
+ return get(
+ request,
+ url,
+ cache,
+ next,
+ requestOptions,
+ );
};
const gqlPost = async (
@@ -204,7 +184,6 @@ const gqlPost = async (
) => {
const endpoint = new URL(url);
endpoint.searchParams.append("op", request.operationName);
-
const response = await fetch(endpoint.toString(), {
headers: options.headers,
method: "POST",
@@ -253,3 +232,66 @@ const parseResponse = async (
return await response.json();
};
+function get(
+ request: GraphQLRequest,
+ url: string,
+ cache: RequestCache | undefined,
+ next: NextFetchRequestConfig,
+ requestOptions: RequestOptions,
+): GqlResponse | PromiseLike> {
+ return tracer.startActiveSpan(request.operationName, async (span) => {
+ try {
+ let response = await gqlPersistedQuery(
+ url,
+ request,
+ { cache, next },
+ requestOptions,
+ );
+
+ // If this is not a persisted query, but we tried to use automatic
+ // persisted queries (APQ) then we retry with a POST
+ if (!isPersistedQuery(request) && hasPersistedQueryError(response)) {
+ // If the cached query doesn't exist, fall back to POST request and
+ // let the server cache it.
+ response = await gqlPost(url, request, { cache, next }, requestOptions);
+ }
+
+ span.end();
+ return response as GqlResponse;
+ } catch (err: any) {
+ span.setStatus({
+ code: SpanStatusCode.ERROR,
+ message: err?.message ?? String(err),
+ });
+ throw err;
+ }
+ });
+}
+
+function post(
+ request: GraphQLRequest,
+ url: string,
+ cache: RequestCache | undefined,
+ next: NextFetchRequestConfig,
+ requestOptions: RequestOptions,
+): GqlResponse | PromiseLike> {
+ return tracer.startActiveSpan(request.operationName, async (span) => {
+ try {
+ const response = await gqlPost(
+ url,
+ request,
+ { cache, next },
+ requestOptions,
+ );
+
+ span.end();
+ return response as GqlResponse;
+ } catch (err: unknown) {
+ span.setStatus({
+ code: SpanStatusCode.ERROR,
+ message: err instanceof Error ? err.message : String(err),
+ });
+ throw err;
+ }
+ });
+}
From d3cb9db317696f3d254186591530a52309e06e1a Mon Sep 17 00:00:00 2001
From: Paul Vaneveld
Date: Mon, 27 Oct 2025 14:21:04 +0100
Subject: [PATCH 8/8] chore: version bump
---
.changeset/pre.json | 3 ++-
.changeset/sharp-radios-relate.md | 5 +++++
CHANGELOG.md | 6 ++++++
package.json | 2 +-
4 files changed, 14 insertions(+), 2 deletions(-)
create mode 100644 .changeset/sharp-radios-relate.md
diff --git a/.changeset/pre.json b/.changeset/pre.json
index e53cfb9..83827a3 100644
--- a/.changeset/pre.json
+++ b/.changeset/pre.json
@@ -5,6 +5,7 @@
"@labdigital/graphql-fetcher": "2.0.0"
},
"changesets": [
- "rich-ants-shop"
+ "rich-ants-shop",
+ "sharp-radios-relate"
]
}
diff --git a/.changeset/sharp-radios-relate.md b/.changeset/sharp-radios-relate.md
new file mode 100644
index 0000000..2a1553a
--- /dev/null
+++ b/.changeset/sharp-radios-relate.md
@@ -0,0 +1,5 @@
+---
+"@labdigital/graphql-fetcher": minor
+---
+
+Make apq opt in for the server side client instead of opt in
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 73184a2..7074d90 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,5 +1,11 @@
# @labdigital/react-query-opal
+## 3.0.0-beta.1
+
+### Minor Changes
+
+- Make apq opt in for the server side client instead of opt in
+
## 3.0.0-beta.0
### Major Changes
diff --git a/package.json b/package.json
index c7b54f4..10d8b87 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
{
"name": "@labdigital/graphql-fetcher",
- "version": "3.0.0-beta.0",
+ "version": "3.0.0-beta.1",
"description": "Custom fetcher for react-query to use with @labdigital/node-federated-token",
"type": "module",
"main": "./dist/index.cjs",