@apollo/client
4.3.5 · 19 Oct 2026MIT5.3M / weekRepositoryHomepagenpmAtom feed
3.14.1 is the newest release of 3.x.
In short
2 deprecated28 new5 fixed
- deprecated
Deprecates
canonizeResults. - deprecated
Adds a deprecation warning to the HOC and render prop APIs.
Release note
Minor Changes
Memory optimizations
Simplify RetryLink, fix potential memory leak
Historically,
RetryLinkwould keep avaluesarray of all previous values, in case the operation would get an additional subscriber at a later point in time.In practice, this could lead to a memory leak (#11393) and did not serve any further purpose, as the resulting observable would only be subscribed to by Apollo Client itself, and only once - it would be wrapped in a
Concastbefore being exposed to the user, and thatConcastwould handle subscribers on its own.- deprecated
Deprecates
canonizeResults.Using
canonizeResultscan result in memory leaks so we generally do not recommend using this option anymore. A future version of Apollo Client will contain a similar feature without the risk of memory leaks. Decouple
canonicalStringifyfromObjectCanonfor better time and memory performance.Fix a potential memory leak in
FragmentRegistry.transformandFragmentRegistry.findFragmentSpreadsthat would hold on to passed-inDocumentNodesfor too long.parsefunction: improve memory managementuse LRU
WeakCacheinstead ofMapto keep a limited number of parsed resultscache is initiated lazily, only when needed
expose
parse.resetCache()method
documentTransform: useoptimismandWeakCacheinstead of directly storing data on theTrieFixes a potential memory leak in
Concastthat might have been triggered whenConcastwas used outside of Apollo Client.Add a
resetCachemethod toDocumentTransformand hookInMemoryCache.addTypenameTransformup toInMemoryCache.gcprint: useWeakCacheinstead ofWeakMapQueryManager.transformCache: useWeakCacheinstead ofWeakMapPersisted Query Link: Improve memory management
use LRU
WeakCacheinstead ofWeakMapto keep a limited number of hash resultshash cache is initiated lazily, only when needed
expose
persistedLink.resetHashCache()methodreset hash cache if the upstream server reports it doesn't accept persisted queries
Use WeakMap in React Native with Hermes
InMemoryCache.gc now also triggers FragmentRegistry.resetCaches (if there is a FragmentRegistry)
Adds an experimental
ApolloClient.getMemoryInternalshelperAdd
resetmethod toprint, hook up toInMemoryCache.gc
Suspense-enabled data fetching on user interaction with useLoadableQuery
Introduces a new
useLoadableQueryhook. This hook works similarly touseBackgroundQueryin that it returns aqueryRefthat can be used to suspend a component via theuseReadQueryhook. It provides a more ergonomic way to load the query during a user interaction (for example when wanting to preload some data) that would otherwise be clunky withuseBackgroundQuery.function App() { const [loadQuery, queryRef, { refetch, fetchMore, reset }] = useLoadableQuery(query, options); return ( <> <button onClick={() => loadQuery(variables)}>Load query</button> <Suspense fallback={<SuspenseFallback />}> {queryRef && <Child queryRef={queryRef} />} </Suspense> </> ); } function Child({ queryRef }) { const { data } = useReadQuery(queryRef); // ... }
Begin preloading outside of React with createQueryPreloader
Add the ability to start preloading a query outside React to begin fetching as early as possible. Call
createQueryPreloaderto create apreloadQueryfunction which can be called to start fetching a query. This returns aqueryRefwhich is passed touseReadQueryand suspended until the query is done fetching.const preloadQuery = createQueryPreloader(client); const queryRef = preloadQuery(QUERY, { variables, ...otherOptions }); function App() { return { <Suspense fallback={<div>Loading</div>}> <MyQuery /> </Suspense> } } function MyQuery() { const { data } = useReadQuery(queryRef); // do something with data }
Testing utility improvements
Support re-using of mocks in the MockedProvider
Ability to dynamically match mocks
Adds support for a new property
MockedResponse.variableMatcher: a predicate function that accepts avariablesparam. Iftrue, thevariableswill be passed into theResultFunctionto help dynamically build a response.
New useQueryRefHandlers hook
Create a new
useQueryRefHandlershook that returnsrefetchandfetchMorefunctions for a givenqueryRef. This is useful to get access to handlers for aqueryRefthat was created bycreateQueryPreloaderor when the handlers for aqueryRefproduced by a different component are inaccessible.const MyComponent({ queryRef }) { const { refetch, fetchMore } = useQueryRefHandlers(queryRef); // ... }
Bail out of optimisticResponse updates with the IGNORE sentinel object
Allow returning
IGNOREsentinel object fromoptimisticResponsefunctions to bail-out from the optimistic update.Consider this example:
const UPDATE_COMMENT = gql` mutation UpdateComment($commentId: ID!, $commentContent: String!) { updateComment(commentId: $commentId, content: $commentContent) { id __typename content } } `; function CommentPageWithData() { const [mutate] = useMutation(UPDATE_COMMENT); return ( <Comment updateComment={({ commentId, commentContent }) => mutate({ variables: { commentId, commentContent }, optimisticResponse: (vars, { IGNORE }) => { if (commentContent === "foo") { // conditionally bail out of optimistic updates return IGNORE; } return { updateComment: { id: commentId, __typename: "Comment", content: commentContent, }, }; }, }) } /> ); }The
IGNOREsentinel can be destructured from the second parameter in the callback function signature passed tooptimisticResponse.
Network adapters for multipart subscriptions usage with Relay and urql
Add multipart subscription network adapters for Relay and urql
Relay
import { createFetchMultipartSubscription } from "@apollo/client/utilities/subscriptions/relay";
import { Environment, Network, RecordSource, Store } from "relay-runtime";
const fetchMultipartSubs = createFetchMultipartSubscription(
"http://localhost:4000"
);
const network = Network.create(fetchQuery, fetchMultipartSubs);
export const RelayEnvironment = new Environment({
network,
store: new Store(new RecordSource()),
});Urql
import { createFetchMultipartSubscription } from "@apollo/client/utilities/subscriptions/urql";
import { Client, fetchExchange, subscriptionExchange } from "@urql/core";
const url = "http://localhost:4000";
const multipartSubscriptionForwarder = createFetchMultipartSubscription(url);
const client = new Client({
url,
exchanges: [
fetchExchange,
subscriptionExchange({
forwardSubscription: multipartSubscriptionForwarder,
}),
],
});skipPollAttempt callback function
Adds a new
skipPollAttemptcallback function that's called whenever a refetch attempt occurs while polling. If the function returnstrue, the refetch is skipped and not reattempted until the next poll interval. This will solve the frequent use-case of disabling polling when the window is inactive.useQuery(QUERY, { pollInterval: 1000, skipPollAttempt: () => document.hidden, // or !document.hasFocus() }); // or define it globally new ApolloClient({ defaultOptions: { watchQuery: { skipPollAttempt: () => document.hidden, // or !document.hasFocus() }, }, });
QueryManager.inFlightLinkObservables now uses a strong Trie as an internal data structure
Warning: requires @apollo/experimental-nextjs-app-support update
If you are using @apollo/experimental-nextjs-app-support, you will need to update that to at least 0.5.2, as it accesses this internal data structure.
More Minor Changes
Prevent
QueryInfo#markResultmutation ofresult.dataand return cache data consistently whether complete or incomplete.Remove the need to call
retainfromuseLoadableQuerysinceuseReadQuerywill now retain the query. This means that aqueryRefthat is not consumed byuseReadQuerywithin the givenautoDisposeTimeoutMswill now be auto diposed for you.Thanks to #11412, disposed query refs will be automatically resubscribed to the query when consumed by
useReadQueryafter it has been disposed.Remove the need to call
retainfromuseBackgroundQuerysinceuseReadQuerywill now retain the query. This means that aqueryRefthat is not consumed byuseReadQuerywithin the givenautoDisposeTimeoutMswill now be auto diposed for you.Thanks to #11412, disposed query refs will be automatically resubscribed to the query when consumed by
useReadQueryafter it has been disposed.To work around issues in React Server Components, especially with bundling for
the Next.js "edge" runtime we now use an external package to wrapreactimports
instead of importing React directly.Increase the default memory limits for
executeSelectionSetandexecuteSelectionSetArray.
Patch Changes
Add a
defaultContextoption and property onApolloClient, e.g. for keeping track of changing auth tokens or dependency injection.This can be used e.g. in authentication scenarios, where a new token might be generated outside of the link chain and should passed into the link chain.
import { ApolloClient, createHttpLink, InMemoryCache } from "@apollo/client"; import { setContext } from "@apollo/client/link/context"; const httpLink = createHttpLink({ uri: "/graphql", }); const authLink = setContext((_, { headers, token }) => { return { headers: { ...headers, authorization: token ? `Bearer ${token}` : "", }, }; }); const client = new ApolloClient({ link: authLink.concat(httpLink), cache: new InMemoryCache(), }); // somewhere else in your application function onNewToken(newToken) { // token can now be changed for future requests without need for a global // variable, scoped ref or recreating the client client.defaultContext.token = newToken; }- deprecated
Adds a deprecation warning to the HOC and render prop APIs.
The HOC and render prop APIs have already been deprecated since 2020,
but we previously didn't have a@deprecatedtag in the DocBlocks. Ensure
defaultContextis also used for mutations and subscriptionsRelease changes from
v3.8.10ObservableQuery: Prevent reporting results of previous queries if the variables changed since
Address bundling issue introduced in #11412 where the
react/cacheinternals ended up duplicated in the bundle. This was due to the fact that we had areact/hooksentrypoint that imported these files along with the newly introducedcreateQueryPreloaderfunction, which lived outside of thereact/hooksfolder.Clarify types of
EntityStore.makeCacheKey.