diff --git a/integration-test/nextjs/src/app/graphql/route.ts b/integration-test/nextjs/src/app/graphql/route.ts index 743fa22b..c977ce7a 100644 --- a/integration-test/nextjs/src/app/graphql/route.ts +++ b/integration-test/nextjs/src/app/graphql/route.ts @@ -6,7 +6,13 @@ const server = new ApolloServer({ schema, }); -const handler = startServerAndCreateNextHandler(server); +const handler = startServerAndCreateNextHandler(server, { + context: async () => { + return { + from: "network", + }; + }, +}); export async function GET(request: Request) { return handler(request); diff --git a/integration-test/nextjs/src/app/graphql/schema.ts b/integration-test/nextjs/src/app/graphql/schema.ts index aef2ed67..b6354f32 100644 --- a/integration-test/nextjs/src/app/graphql/schema.ts +++ b/integration-test/nextjs/src/app/graphql/schema.ts @@ -1,5 +1,7 @@ import { makeExecutableSchema } from "@graphql-tools/schema"; import gql from "graphql-tag"; +import * as entryPoint from "@apollo/client-react-streaming"; +import type { IResolvers } from "@graphql-tools/utils"; const typeDefs = gql` type Product { @@ -8,6 +10,7 @@ const typeDefs = gql` } type Query { products: [Product!]! + env: String! } `; @@ -39,8 +42,20 @@ const resolvers = { title: "The Apollo Socks", }, ], + env: (source, args, context, info) => { + console.log({ source, args, context, info }); + return context && context.from === "network" + ? "browser" + : "built_for_ssr" in entryPoint + ? "SSR" + : "built_for_browser" in entryPoint + ? "Browser" + : "built_for_rsc" in entryPoint + ? "RSC" + : "unknown"; + }, }, -}; +} satisfies IResolvers; export const schema = makeExecutableSchema({ typeDefs, diff --git a/integration-test/nextjs/src/app/rsc/dynamic/PreloadQuery/PreloadQuery.test.ts b/integration-test/nextjs/src/app/rsc/dynamic/PreloadQuery/PreloadQuery.test.ts index 521e5642..8c5c41ae 100644 --- a/integration-test/nextjs/src/app/rsc/dynamic/PreloadQuery/PreloadQuery.test.ts +++ b/integration-test/nextjs/src/app/rsc/dynamic/PreloadQuery/PreloadQuery.test.ts @@ -18,6 +18,9 @@ test.describe("PreloadQuery", () => { await expect(page).toBeInitiallyLoading(true); await expect(page.getByText("loading")).not.toBeVisible(); await expect(page.getByText("Soft Warm Apollo Beanie")).toBeVisible(); + await expect( + page.getByText("Queried in RSC environment") + ).toBeVisible(); }); test("query errors on the server, restarts in the browser", async ({ @@ -42,7 +45,28 @@ test.describe("PreloadQuery", () => { await expect(page.getByText("loading")).not.toBeVisible(); await expect(page.getByText("Soft Warm Apollo Beanie")).toBeVisible(); + await expect( + page.getByText("Queried in Browser environment") + ).toBeVisible(); }); }); } + test("queryRef works with useQueryRefHandlers", async ({ page }) => { + await page.goto( + `http://localhost:3000/rsc/dynamic/PreloadQuery/queryRef-useReadQuery`, + { + waitUntil: "commit", + } + ); + + await expect(page).toBeInitiallyLoading(true); + await expect(page.getByText("loading")).not.toBeVisible(); + await expect(page.getByText("Soft Warm Apollo Beanie")).toBeVisible(); + await expect(page.getByText("Queried in RSC environment")).toBeVisible(); + + await page.getByRole("button", { name: "refetch" }).click(); + await expect( + page.getByText("Queried in Browser environment") + ).toBeVisible(); + }); }); diff --git a/integration-test/nextjs/src/app/rsc/dynamic/PreloadQuery/queryRef-useReadQuery/ClientChild.tsx b/integration-test/nextjs/src/app/rsc/dynamic/PreloadQuery/queryRef-useReadQuery/ClientChild.tsx index fac78dda..26dff8c5 100644 --- a/integration-test/nextjs/src/app/rsc/dynamic/PreloadQuery/queryRef-useReadQuery/ClientChild.tsx +++ b/integration-test/nextjs/src/app/rsc/dynamic/PreloadQuery/queryRef-useReadQuery/ClientChild.tsx @@ -1,14 +1,28 @@ "use client"; -import { QueryReference, useReadQuery } from "@apollo/client"; +import { + QueryReference, + useQueryRefHandlers, + useReadQuery, +} from "@apollo/client"; +import { DynamicProductResult } from "../shared"; -export function ClientChild({ queryRef }: { queryRef: QueryReference }) { - const { data } = useReadQuery(queryRef); +export function ClientChild({ + queryRef, +}: { + queryRef: QueryReference; +}) { + const { data } = useReadQuery(queryRef); + const { refetch } = useQueryRefHandlers(queryRef); return ( - + <> + +

Queried in {data.env} environment

+ + ); } diff --git a/integration-test/nextjs/src/app/rsc/dynamic/PreloadQuery/queryRef-useReadQuery/page.tsx b/integration-test/nextjs/src/app/rsc/dynamic/PreloadQuery/queryRef-useReadQuery/page.tsx index d8672135..b4452721 100644 --- a/integration-test/nextjs/src/app/rsc/dynamic/PreloadQuery/queryRef-useReadQuery/page.tsx +++ b/integration-test/nextjs/src/app/rsc/dynamic/PreloadQuery/queryRef-useReadQuery/page.tsx @@ -22,7 +22,7 @@ export default function Page({ searchParams }: { searchParams?: any }) { > {(queryRef) => ( loading}> - + )} diff --git a/integration-test/nextjs/src/app/rsc/dynamic/PreloadQuery/shared.tsx b/integration-test/nextjs/src/app/rsc/dynamic/PreloadQuery/shared.tsx index 0fabe82d..8e48c010 100644 --- a/integration-test/nextjs/src/app/rsc/dynamic/PreloadQuery/shared.tsx +++ b/integration-test/nextjs/src/app/rsc/dynamic/PreloadQuery/shared.tsx @@ -1,15 +1,18 @@ import { TypedDocumentNode, gql } from "@apollo/client"; -export const QUERY: TypedDocumentNode<{ +export interface DynamicProductResult { products: { id: string; title: string; }[]; -}> = gql` + env: string; +} +export const QUERY: TypedDocumentNode = gql` query dynamicProducts { products { id title } + env } `; diff --git a/integration-test/nextjs/src/app/rsc/dynamic/PreloadQuery/useSuspenseQuery/ClientChild.tsx b/integration-test/nextjs/src/app/rsc/dynamic/PreloadQuery/useSuspenseQuery/ClientChild.tsx index 4e1c0521..192a073c 100644 --- a/integration-test/nextjs/src/app/rsc/dynamic/PreloadQuery/useSuspenseQuery/ClientChild.tsx +++ b/integration-test/nextjs/src/app/rsc/dynamic/PreloadQuery/useSuspenseQuery/ClientChild.tsx @@ -6,10 +6,13 @@ import { QUERY } from "../shared"; export function ClientChild() { const { data } = useSuspenseQuery(QUERY); return ( -
    - {data.products.map(({ id, title }: any) => ( -
  • {title}
  • - ))} -
+ <> +
    + {data.products.map(({ id, title }: any) => ( +
  • {title}
  • + ))} +
+

Queried in {data.env} environment

+ ); } diff --git a/integration-test/package.json b/integration-test/package.json index 89327d5e..e8360c5d 100644 --- a/integration-test/package.json +++ b/integration-test/package.json @@ -4,7 +4,7 @@ "resolutions": { "@apollo/client-react-streaming": "exec:./shared/build-client-react-streaming.cjs", "@apollo/experimental-nextjs-app-support": "exec:./shared/build-experimental-nextjs-app-support.cjs", - "@integration-test/nextjs/@apollo/client": "0.0.0-pr-11757-20240405111250" + "@integration-test/nextjs/@apollo/client": "0.0.0-pr-11771-20240409102352" }, "workspaces": [ "*" diff --git a/integration-test/yarn.lock b/integration-test/yarn.lock index d7d54447..15618dde 100644 --- a/integration-test/yarn.lock +++ b/integration-test/yarn.lock @@ -44,9 +44,9 @@ __metadata: languageName: node linkType: hard -"@apollo/client@npm:0.0.0-pr-11757-20240405111250": - version: 0.0.0-pr-11757-20240405111250 - resolution: "@apollo/client@npm:0.0.0-pr-11757-20240405111250" +"@apollo/client@npm:0.0.0-pr-11771-20240409102352": + version: 0.0.0-pr-11771-20240409102352 + resolution: "@apollo/client@npm:0.0.0-pr-11771-20240409102352" dependencies: "@graphql-typed-document-node/core": "npm:^3.1.1" "@wry/caches": "npm:^1.0.0" @@ -77,7 +77,7 @@ __metadata: optional: true subscriptions-transport-ws: optional: true - checksum: 10/a9ff604e63042a1d40263d63f93ef823d84a23e716a310dc9491a22663512dd449e81583bbd677fe4b8b6ac10ec82254858705f32ebc21e4a669605bfadd09d3 + checksum: 10/055f65bdb82a8e60c98c39a5e895d85296aa905a88fbf16f168f4acb0b8c2a717a978c21bb719c30f10507f5e2c7d6070369eec99c9d5f1072366c56f12bb5c1 languageName: node linkType: hard diff --git a/package.json b/package.json index acbd825d..02547a48 100644 --- a/package.json +++ b/package.json @@ -22,7 +22,8 @@ "react@18.2.0": "18.3.0-canary-60a927d04-20240113", "react-dom@18.2.0": "18.3.0-canary-60a927d04-20240113", "superjson": "1.13.3", - "@microsoft/api-documenter": "7.24.1" + "@microsoft/api-documenter": "7.24.1", + "@apollo/client": "0.0.0-pr-11771-20240409102352" }, "devDependencies": { "@microsoft/api-documenter": "7.24.1", diff --git a/packages/client-react-streaming/src/DataTransportAbstraction/hooks.ts b/packages/client-react-streaming/src/DataTransportAbstraction/hooks.ts index 480cca16..05d9e4fe 100644 --- a/packages/client-react-streaming/src/DataTransportAbstraction/hooks.ts +++ b/packages/client-react-streaming/src/DataTransportAbstraction/hooks.ts @@ -30,27 +30,37 @@ export const hookWrappers: HookWrappers = { return wrap( (queryRef) => { if (isTransportedQueryRef(queryRef)) { - if (queryRef.__transportedQueryRef === true) { - const preloader = createQueryPreloader( - use(getApolloContext()).client! - ); - const { query, ...options } = queryRef.options; - // TODO: discuss what to do with the fetchPolicy here - options.fetchPolicy = "cache-first"; - queryRef.__transportedQueryRef = preloader( - query, - options as typeof options & { fetchPolicy: "cache-first" } - ); - } - queryRef = queryRef.__transportedQueryRef; + queryRef = reviveTransportedQueryRef(queryRef); } return orig_useReadQuery(queryRef); }, ["data", "networkStatus"] ); }, + useQueryRefHandlers(orig_useQueryRefHandlers) { + return wrap((queryRef) => { + if (isTransportedQueryRef(queryRef)) { + queryRef = reviveTransportedQueryRef(queryRef); + } + return orig_useQueryRefHandlers(queryRef); + }, []); + }, }; +function reviveTransportedQueryRef(queryRef: TransportedQueryRef) { + if (queryRef.__transportedQueryRef === true) { + const preloader = createQueryPreloader(use(getApolloContext()).client!); + const { query, ...options } = queryRef.options; + // TODO: discuss what to do with the fetchPolicy here + options.fetchPolicy = "cache-first"; + queryRef.__transportedQueryRef = preloader( + query, + options as typeof options & { fetchPolicy: "cache-first" } + ); + } + return queryRef.__transportedQueryRef; +} + function isTransportedQueryRef( queryRef: object ): queryRef is TransportedQueryRef { @@ -68,6 +78,9 @@ function wrap any>( ): T { return ((...args: any[]) => { const result = useFn(...args); + if (transportKeys.length == 0) { + return result; + } const transported: Partial = {}; for (const key of transportKeys) { transported[key] = result[key]; diff --git a/yarn.lock b/yarn.lock index 8b9353e9..32735cbc 100644 --- a/yarn.lock +++ b/yarn.lock @@ -85,9 +85,9 @@ __metadata: languageName: unknown linkType: soft -"@apollo/client@npm:3.9.9, @apollo/client@npm:^3.9.9": - version: 3.9.9 - resolution: "@apollo/client@npm:3.9.9" +"@apollo/client@npm:0.0.0-pr-11771-20240409102352": + version: 0.0.0-pr-11771-20240409102352 + resolution: "@apollo/client@npm:0.0.0-pr-11771-20240409102352" dependencies: "@graphql-typed-document-node/core": "npm:^3.1.1" "@wry/caches": "npm:^1.0.0" @@ -118,7 +118,7 @@ __metadata: optional: true subscriptions-transport-ws: optional: true - checksum: 10/9e67f8867eb4fd8a29c36267d919f20d003584040134744db99d85db18cfc2ba61364586c76799bef413b690fc3957a37b79b9b1e084bf1730217aa95d7efd2f + checksum: 10/055f65bdb82a8e60c98c39a5e895d85296aa905a88fbf16f168f4acb0b8c2a717a978c21bb719c30f10507f5e2c7d6070369eec99c9d5f1072366c56f12bb5c1 languageName: node linkType: hard