Major Changes
-
#12384
6aa6fd3
Thanks @jerelmiller! - Remove theasyncMap
utility function. Instead use one of the RxJS operators that creates Observables from promises, such asfrom
. -
#12398
8cf5077
Thanks @jerelmiller! - Removes theisApolloError
utility function to check if the error object is anApolloError
instance. Useinstanceof
to check for more specific error types that replaceApolloError
. -
#12379
ef892b4
Thanks @jerelmiller! - Removes theaddTypename
option fromInMemoryCache
andMockedProvider
.__typename
is now always added to the outgoing query document when usingInMemoryCache
and cannot be disabled.If you are using
<MockedProvider />
withaddTypename={false}
, ensure that your mocked responses include a__typename
field. This will ensure cache normalization kicks in and behaves more like production. -
#12396
00f3d0a
Thanks @jerelmiller! - Remove the deprecatederrors
property fromuseQuery
anduseLazyQuery
. Read errors from theerror
property instead. -
#12222
d1a9054
Thanks @jerelmiller! - Drop support for React 16. -
#12376
a0c996a
Thanks @jerelmiller! - Remove deprecatedignoreResults
option fromuseMutation
. If you don't want to synchronize component state with the mutation, useuseApolloClient
to access your client instance and useclient.mutate
directly. -
#12384
6aa6fd3
Thanks @jerelmiller! - Unusubscribing fromObservableQuery
while a request is in flight will no longer terminate the request by unsubscribing from the link observable. -
#12367
e6af35e
Thanks @jerelmiller! - ThepreviousData
property onuseLazyQuery
will now change only whendata
changes. PreviouslypreviousData
would change to the same value asdata
while the query was loading. -
#12224
51e6c0f
Thanks @jerelmiller! - Remove deprecatedpartialRefetch
option. -
#12407
8b1390b
Thanks @jerelmiller! - Callingrefetch
with new variables will now set thenetworkStatus
torefetch
instead ofsetVariables
. -
#12384
6aa6fd3
Thanks @jerelmiller! - Remove theiterateObserversSafely
utility function. -
#12398
8cf5077
Thanks @jerelmiller! - Apollo Client no longer wraps errors inApolloError
.ApolloError
has been replaced with separate error classes depending on the cause of the error. As such, APIs that return anerror
property have been updated to use the genericError
type. Useinstanceof
to check for more specific error types.Migration guide
ApolloError
encapsulated 4 main error properties. The type of error would determine which property was set:graphqlErrors
- Errors returned from theerrors
field by the GraphQL servernetworkError
- Any non-GraphQL error that caused the query to failprotocolErrors
- Transport-level errors that occur during multipart HTTP subscriptionsclientErrors
- A space to define custom errors. Mostly unused.
These errors were mutally exclusive, meaning both
networkError
andgraphqlErrors
were never set simultaneously. The following replaces each of these fields fromApolloError
.graphqlErrors
GraphQL errors are now encapsulated in a
CombinedGraphQLErrors
instance. You can access the raw GraphQL errors via theerrors
property.import { CombinedGraphQLErrors } from "@apollo/client"; // ... const { error } = useQuery(query); if (error && error instanceof CombinedGraphQLErrors) { console.log(error.errors); }
networkError
Network errors are no longer wrapped and are instead passed through directly.
const client = new ApolloClient({ link: new ApolloLink(() => { return new Observable((observer) => { observer.error(new Error("Test error")); }); }), }); // ... const { error } = useQuery(query); // error is `new Error('Test error')`;
protocolErrors
Protocol errors are now encapsulated in a
CombinedProtocolErrors
instance. You can access the raw protocol errors via theerrors
property.import { CombinedProtocolErrors } from "@apollo/client"; // ... const { error } = useSubscription(subscription); if (error && error instanceof CombinedProtocolErrors) { console.log(error.errors); }
clientErrors
These were unused by the client and have no replacement. Any non-GraphQL or non-protocol errors are now passed through unwrapped.
Strings as errors
If the link sends a string error, Apollo Client will wrap this in an
Error
instance. This ensureserror
properties are guaranteed to be of typeError
.const client = new ApolloClient({ link: new ApolloLink(() => { return new Observable((observer) => { // Oops we sent a string instead of wrapping it in an `Error` observer.error("Test error"); }); }), }); // ... const { error } = useQuery(query); // The error string is wrapped and returned as `new Error('Test error')`;
Non-error types
If the link chain sends any other object type as an error, Apollo Client will wrap this in an
UnknownError
instance with thecause
set to the original object. This ensureserror
properties are guaranteed to be of typeError
.const client = new ApolloClient({ link: new ApolloLink(() => { return new Observable((observer) => { observer.error({ message: "Not a proper error type" }); }); }), }); // ... const { error } = useQuery(query); // error is an `UnknownError` instance. error.cause returns the original object.
-
#12384
6aa6fd3
Thanks @jerelmiller! - RemovefromError
utility function. UsethrowError
instead. -
#12211
c2736db
Thanks @jerelmiller! - Remove the deprecatedgraphql
,withQuery
,withMutation
,withSubscription
, andwithApollo
hoc components. Use the provided React hooks instead. -
#12262
10ef733
Thanks @jerelmiller! - RemoveitAsync
test utility. -
#12398
8cf5077
Thanks @jerelmiller! - Updates theServerError
andServerParseError
types to be properError
subclasses. Perviously these were plainError
intances with additional properties added at runtime. All properties are retained, butinstanceof
checks now work correctly.import { ServerError, ServerParseError } from "@apollo/client"; if (error instanceof ServerError) { // ... } if (error instanceof ServerParseError) { // ... }
-
#12367
e6af35e
Thanks @jerelmiller! -useLazyQuery
no longer supports SSR environments and will now throw if theexecute
function is called in SSR. If you need to run a query in an SSR environment, useuseQuery
instead. -
#12367
e6af35e
Thanks @jerelmiller! - The execute function returned fromuseLazyQuery
now only supports thecontext
andvariables
options. This means that passing options supported by the hook no longer override the hook value.To change options, rerender the component with new options. These options will take effect with the next query execution.
-
#12384
6aa6fd3
Thanks @jerelmiller! -ObservableQuery
will no longer terminate on errors and will instead emit anext
value with anerror
property. This ensures thatObservableQuery
instances can continue to receive updates after errors are returned in requests without the need to resubscribe to the observable. -
#12398
8cf5077
Thanks @jerelmiller! - Removes thethrowServerError
utility function. Now thatServerError
is an
Error
subclass, you can throw these errors directly:import { ServerError } from "@apollo/client"; // instead of throwServerError(response, result, "error message"); // Use throw new ServerError("error message", { response, result });
-
#12304
86469a2
Thanks @jerelmiller! - TheCache.DiffResult<T>
type is now a union type with better type safety for both complete and partial results. Checkingdiff.complete
will now narrow the type ofresult
depending on whether the value istrue
orfalse
.When
true
,diff.result
will be a non-null value equal to theT
generic type. Whenfalse
,diff.result
now reportsresult
asDeepPartial<T> | null
indicating that fields in the result may be missing (DeepPartial<T>
) or empty entirely (null
). -
#12396
00f3d0a
Thanks @jerelmiller! - Remove theerrors
property from the results emitted fromObservableQuery
or returned fromclient.query
. Read errors from theerror
property instead. -
#12367
e6af35e
Thanks @jerelmiller! - The result resolved from the promise returned from the execute function inuseLazyQuery
is now anApolloQueryResult
type and no longer includes all the fields returned from theuseLazyQuery
hook tuple.If you need access to the additional properties such as
called
,refetch
, etc. not included inApolloQueryResult
, read them from the hook instead. -
#12367
e6af35e
Thanks @jerelmiller! -useLazyQuery
will no longer rerender with the loading state when calling the execute function the first time unless thenotifyOnNetworkStatusChange
option is set totrue
(which is the new default).If you prefer the behavior from 3.x, rerender the component with
notifyOnNetworkStatusChange
set tofalse
after the execute function is
called the first time.function MyComponent() { const [notifyOnNetworkStatusChange, setNotifyOnNetworkStatusChange] = useState(true); const [execute] = useLazyQuery(query, { notifyOnNetworkStatusChange }); async function runExecute() { await execute(); // Set to false after the initial fetch to stop receiving notifications // about changes to the loading states. setNotifyOnNetworkStatusChange(false); } // ... }
-
#12254
0028ac0
Thanks @jerelmiller! - Changes the defaultAccept
header toapplication/graphql-response+json
. -
#12430
2ff66d0
Thanks @jerelmiller! -ObservableQuery.setVariables
will now resolve with the last emitted result instead ofundefined
when either the variables match the current variables or there are no subscribers to the query. -
#12385
cad5117
Thanks @phryneas! - Apollo Client now defaults to production mode, not development mode, if the
environment cannot be determined.In modern bundlers, this should automatically be handled by the bundler loading
the bundler with thedevelopment
export condition.If neither the
production
nor thedevelopment
export condition are
used by the bundler/runtime, Apollo Client will fall back toglobalThis.__DEV__
to determine if it should run in production or development mode.Unlike Apollo Client 3 though, if
globalThis.__DEV__
is not set totrue
,
Apollo Client will now default toproduction
, not todevelopment
, behaviour.This switch to explicilty requiring
true
also resolves a situation where
an HTML element withid="__DEV__"
would create a global__DEV__
variable
with a referent to the DOM element, which in the past was picked up as "truthy" and
would have triggered development mode. -
#12367
e6af35e
Thanks @jerelmiller! - Thereobserve
option is no longer available in the result returned fromuseLazyQuery
. This was considered an internal API and should not be used directly. -
#12333
3e4beaa
Thanks @jerelmiller! - Fix type ofdata
property onApolloQueryResult
. Previously this field was non-optional, non-nullTData
, however at runtime this value could be set toundefined
. This field is now reported asTData | undefined
.This will affect you in a handful of places:
- The
data
property emitted from the result passed to thenext
callback fromclient.watchQuery
- Fetch-based APIs that return an
ApolloQueryResult
type such asobservableQuery.refetch
,observableQuery.fetchMore
, etc.
- The
-
#12367
e6af35e
Thanks @jerelmiller! - The promise returned when calling the execute function fromuseLazyQuery
will now reject when using anerrorPolicy
ofnone
when GraphQL errors are returned from the result. -
#12223
69c1cb6
Thanks @jerelmiller! - RemovesubscribeAndCount
testing utility from@apollo/client/testing
. -
#12300
4d581e4
Thanks @jerelmiller! - Moves all React-related exports to the@apollo/client/react
entrypoint and out of the main@apollo/client
entrypoint. This prevents the need to install React in order to use the core client.The following is a list of exports available in
@apollo/client
that should now import from@apollo/client/react
.ApolloConsumer
ApolloProvider
createQueryPreloader
getApolloContext
skipToken
useApolloClient
useBackgroundQuery
useFragment
useLazyQuery
useLoadableQuery
useMutation
useQuery
useQueryRefHandlers
useReactiveVar
useReadQuery
useSubscription
useSuspenseQuery
The following is a list of exports available in
@apollo/client/testing
that should now import from@apollo/client/testing/react
:MockedProvider
-
#12428
abed922
Thanks @jerelmiller! - Removes theurql
multipart subscriptions utilities. Use the native multipart subscriptions support inurql
instead. -
#12384
6aa6fd3
Thanks @jerelmiller! - Switch to RxJS as the observable implementation.rxjs
is now a peer dependency of Apollo Client which means you will now need to installrxjs
in addition to@apollo/client
.This change is mostly transparent, however transforming values on observables, common in link implementations, differs in RxJS vs
zen-observable
. For example, you could modify values in the link chain emitted from a downstream link by using the.map
function. In RxJS, this is done with the.pipe
function and passing amap
operator instead.import { map } from "rxjs"; const link new ApolloLink((operation, forward) => { return forward(operation).pipe( map((result) => performTransform(result)) ); });
For a full list of operators and comprehensive documentation on the capabilities of RxJS, check out the documentation.
-
#12329
61febe4
Thanks @phryneas! - Rework package publish format (#12329, #12382)We have reworked the way Apollo Client is packaged.
- shipping ESM and CJS
- fixing up source maps
- the build targets a modern runtime environment (browserslist query:
"since 2023, node >= 20, not dead"
) - removed the "proxy directory"
package.json
files, e.g.cache/core/package.json
andreact/package.json
. While these helped with older build tools, modern build tooling uses theexports
field in the rootpackage.json
instead and the presence of these files can confuse modern build tooling. If your build tooling still relies on those, please update your imports to import from e.g.@apollo/client/cache/core/index.js
instead of@apollo/client/cache/core
- but generally, this should not be necessary. - added an
exports
field topackage.json
to expose entry points - instead of
globalThis.__DEV__
, Apollo Client now primarily relies on thedevelopment
andproduction
exports conditions. It falls back toglobalThis.__DEV__
if the bundler doesn't know these, though.
-
#12397
2545a54
Thanks @jerelmiller! - RemoveObservableQuery.resetQueryStoreErrors
method. This method reset some internal state that was not consumed elsewhere in the client and resulted in a no-op. -
#12384
6aa6fd3
Thanks @jerelmiller! - RemovefromPromise
utility function. Usefrom
instead. -
#12388
0d825be
Thanks @jerelmiller! - Require environments that supportWeakMap
,WeakSet
and symbols. Apollo Client would fallback toMap
andSet
if the weak versions were not available. This has been removed and expects that these features are available in the source environment.If you are running in an environment without
WeakMap
,WeakSet
or symbols, you will need to find appropriate polyfills. -
#12367
e6af35e
Thanks @jerelmiller! -useLazyQuery
no longer supports calling the execute function in render and will now throw. If you need to execute the query immediately, useuseQuery
instead or move the call to auseEffect
. -
#12367
e6af35e
Thanks @jerelmiller! - ThedefaultOptions
andinitialFetchPolicy
options are no longer supported withuseLazyQuery
.If you use
defaultOptions
, pass those options directly to the hook instead. If you useinitialFetchPolicy
, usefetchPolicy
instead. -
#12367
e6af35e
Thanks @jerelmiller! -useLazyQuery
no longer supportsvariables
in the hook options and therefore no longer performs variable merging. The execute function must now be called withvariables
instead.function MyComponent() { const [execute] = useLazyQuery(query); function runExecute() { execute({ variables: { ... }}); } }
This change means the execute function returned from
useLazyQuery
is more type-safe. The execute function will require you to pass avariables
option if the query type includes required variables. -
#12304
86469a2
Thanks @jerelmiller! - ### Changes for users ofInMemoryCache
cache.diff
now returnsnull
instead of an empty object ({}
) whenreturnPartialData
istrue
and the result is empty.If you use
cache.diff
directly withreturnPartialData: true
, you will need to check fornull
before accessing any other fields on theresult
property. A non-null value indicates that at least one field was present in the cache for the given query document.Changes for third-party cache implementations
The client now expects
cache.diff
to returnnull
instead of an empty object when there is no data that can be fulfilled from the cache andreturnPartialData
istrue
. If your cache implementation returns an empty object, please update this to returnnull
. -
#12430
2ff66d0
Thanks @jerelmiller! - RemovesObservableQuery.result()
method. If you use this method and need similar functionality, use thefirstValueFrom
helper in RxJS.import { firstValueFrom, from } from "rxjs"; // The `from` is necessary to turn `observableQuery` into an RxJS observable const result = await firstValueFrom(from(observableQuery));
-
#12359
ebb4d96
Thanks @jerelmiller! - Remove theonCompleted
andonError
callbacks fromuseQuery
anduseLazyQuery
.See #12352 for more context on this change.
-
#12384
6aa6fd3
Thanks @jerelmiller! - Subscriptions are no longer eagerly started after callingclient.subscribe
. To kick off the subscription, you will now need to subscribe to the returned observable.// Subscriptions are no longer started when calling subscribe on its own. const subscriptionObservable = client.subscribe(...); // Instead, subscribe to the returned observable to kick off the subscription. subscriptionObservable.subscribe({ next: (value) => console.log(value) });
-
#12367
e6af35e
Thanks @jerelmiller! -useLazyQuery
will now only execute the query when the execute function is called. PreviouslyuseLazyQuery
would behave likeuseQuery
after the first call to the execute function which means changes to options might perform network requests.You can now safely rerender
useLazyQuery
with new options which will take effect the next time you manually trigger the query. -
#12384
6aa6fd3
Thanks @jerelmiller! - RemovetoPromise
utility function. UsefirstValueFrom
instead. -
#12304
86469a2
Thanks @jerelmiller! - ### Changes for users ofInMemoryCache
cache.diff
no longer throws whenreturnPartialData
is set tofalse
without a complete result. Instead,cache.diff
will returnnull
when it is unable to read a full cache result.If you use
cache.diff
directly withreturnPartialData: false
, remove thetry
/catch
block and replace with a check fornull
.Changes for third-party cache implementations
The client now expects
cache.diff
to returnnull
instead of throwing when the cache returns an incomplete result andreturnPartialData
isfalse
. The internaltry
/catch
blocks have been removed aroundcache.diff
. If your cache implementation throws for incomplete results, please update this to returnnull
. -
#12211
c2736db
Thanks @jerelmiller! - Remove the deprecatedQuery
,Mutation
, andSubscription
components. Use the provided React hooks instead.
Minor Changes
-
#12385
cad5117
Thanks @phryneas! - Apollo Client is no longer usingts-invariant
, but ships with a modified variant of it.The existing export
setLogVerbosity
from@apollo/client
is still available and
now points to this new integration.
In most cases, you should be using this export.
It will no longer adjust the verbosity ofts-invariant
and as such no longer
influence other packages relying onts-invariant
.The new entry point
@apollo/client/utilities/invariant
now exportsinvariant
,
InvariantError
andsetVerbosity
.
(Note that these tools are mostly meant to be used by Apollo Client and libraries directly
based on Apollo Client like the@apollo/client-integration-*
packages.) -
#12333
3e4beaa
Thanks @jerelmiller! - Deprecate thepartial
flag onApolloQueryResult
and make it a non-optional property. Previouslypartial
was only set conditionally if the result emitted was partial. This value is now available with all results that return anApolloQueryResult
.
Patch Changes
-
#12291
ae5d06a
Thanks @phryneas! - Remove deprecatedresetApolloContext
export -
#12402
903c3ef
Thanks @jerelmiller! - Use an an empty object ({}
) rather than an object withnull
prototype (Object.create(null)
) in all areas that instantiate objects. -
#12385
cad5117
Thanks @phryneas! - * dropped the deprecatedDEV
export from@apollo/client/utilities
and@apollo/client/utilities/globals
- moved the
__DEV__
export from@apollo/client/utilities/globals
to@apollo/client/utilities/environment
- moved the
invariant
,newInvariantError
andInvariantError
exports from@apollo/client/utilities/globals
to@apollo/client/utilities/invariant
- moved the
-
#12432
c7c2f61
Thanks @phryneas! - ObservableQuery: implement therxjs
InteropObservable
interface to ensurefrom(observableQuery)
stays possible -
#12385
cad5117
Thanks @phryneas! -@apollo/client
,@apollo/client/core
and@apollo/client/cache
no longer export an emptyCache
runtime object. This is meant to be a type-only namespace. -
#12384
6aa6fd3
Thanks @jerelmiller! - Don't emit a partial cache result fromcache-only
queries whenreturnPartialData
isfalse
.