Skip to content

Commit

Permalink
Warn if makeClient returns the same client for multiple requests.
Browse files Browse the repository at this point in the history
  • Loading branch information
phryneas committed Mar 1, 2024
1 parent 0abc532 commit 4b786c2
Showing 1 changed file with 44 additions and 7 deletions.
51 changes: 44 additions & 7 deletions packages/client-react-streaming/src/registerApolloClient.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,13 @@
import type { ApolloClient } from "@apollo/client/index.js";
import { cache } from "react";

const seenWrappers = WeakSet
? new WeakSet<{ client: ApolloClient<any> | Promise<ApolloClient<any>> }>()
: undefined;
const seenClients = WeakSet
? new WeakSet<ApolloClient<any> | Promise<ApolloClient<any>>>()
: undefined;

export function registerApolloClient(
makeClient: () => Promise<ApolloClient<any>>
): { getClient: () => Promise<ApolloClient<any>> };
Expand All @@ -10,20 +17,50 @@ export function registerApolloClient(makeClient: () => ApolloClient<any>): {
export function registerApolloClient(
makeClient: (() => Promise<ApolloClient<any>>) | (() => ApolloClient<any>)
) {
function wrappedMakeClient() {
// React invalidates the cache on each server request, so the wrapping
// object is needed to properly detect whether the client is a unique
// reference or not. We can warn if `cachedMakeWrappedClient` creates a new "wrapper",
// but with a `client` property that we have already seen before.
// In that case, not every call to `makeClient` would create a new
// `ApolloClient` instance.
function makeWrappedClient() {
return { client: makeClient() };
}

const cachedMakeWrappedClient = cache(makeWrappedClient);

function getClient() {
if (arguments.length) {
throw new Error(
`
You cannot pass arguments into \`getClient\`.
Passing arguments to \`getClient\` returns a different instance
of Apollo Client each time it is called with different arguments, potentially
resulting in duplicate requests and a non-functional cache.
You cannot pass arguments into \`getClient\`.
Passing arguments to \`getClient\` returns a different instance
of Apollo Client each time it is called with different arguments, potentially
resulting in duplicate requests and a non-functional cache.
`.trim()
);
}
return makeClient();
const wrapper = cachedMakeWrappedClient();
if (seenWrappers && seenClients) {
if (!seenWrappers.has(wrapper)) {
if (seenClients.has(wrapper.client)) {
console.warn(
`
Multiple calls to \`getClient\` for different requests returned the same client instance.
This means that private user data could accidentally be shared between requests.
This for example happens if you create a global \`ApolloClient\` instance and your \`makeClient\`
implementation just looks like \`() => client\`.
Please insure to always call \`new ApolloClient\` **inside** your \`makeClient\` function and to
return a new instance every time \`makeClient\` is called.
`.trim()
);
}
seenWrappers.add(wrapper);
seenClients.add(wrapper.client);
}
}
return wrapper.client;
}
const getClient = cache(wrappedMakeClient);
return {
getClient,
};
Expand Down

0 comments on commit 4b786c2

Please sign in to comment.