Apollo Client 4.0 Release Notes
Apollo Client 4.0 delivers a more modern, efficient, and type-safe GraphQL client experience through various architectural improvements and API refinements. This release focuses on developer experience, bundle size optimization, and framework flexibility.
Key Improvements
🎯 Framework-Agnostic Core
Apollo Client 4.0 separates React functionality from the core library, making @apollo/client
truly framework-agnostic. React exports now live in @apollo/client/react
, allowing developers to use Apollo Client with any JavaScript framework without React dependencies.
📦 Smaller Bundle Sizes
- Opt-in Local State Management: The
@client
directive functionality is now opt-in via theLocalState
class, reducing bundle size when not using local state - Modern Build Target: Transpiled to target
since 2023, node >= 20, not dead
, leveraging modern JavaScript features for better performance - Improved Tree-Shaking: Proper
exports
field in package.json enables better dead code elimination
💥 Unified Error Handling
Apollo Client 4.0 completely reimagines error handling for better clarity and debugging:
ApolloError
removed in favor of specific error classes- Unification of errors to a single
error
property - Network errors now respect
errorPolicy
settings - External errors passed through without wrapping
- New, more granular error classes with static
.is()
methods for robust type narrowing
🔧 Enhanced TypeScript Support
- Namespaced Types: Types are now colocated with their APIs (e.g.,
useQuery.Options
instead ofQueryHookOptions
) - Precise Return Types: Return types accurately reflect the options passed (e.g.,
returnPartialData
makesdata
typeDeepPartial<TData>
) - Stricter Type Safety: Required variables are now enforced more consistently throughout the client
- New
dataState
Property: Enables accurate type narrowing of query results - Module Augmentation: Custom context types via declaration merging instead of fragile generics
- Customizable Type Implementations: Select types can now be customized to provide your own type implementation to seamlessly integrate with external tools such as GraphQL Codegen or
gql.tada
⚡ Modern Observable Implementation
Apollo Client 4.0 migrates from zen-observable
to RxJS, providing the industry-standard Observable implementation backed by a rich ecosystem of utilities.
Major Features
Unified Error Handling
Apollo Client 4.0 completely reimagines error handling for better clarity and debugging:
Key Changes:
ApolloError
removed in favor of specific error classes- Network errors now respect
errorPolicy
settings - External errors passed through without wrapping
- New error classes with static
.is()
methods for type checking
Error Classes:
CombinedGraphQLErrors
- GraphQL errors from the serverServerError
- Non-GraphQL server errorsServerParseError
- Server response parsing errorsUnconventionalError
- Wrapper for non-error thrown valuesLinkError
- Errors from the link chain (via.is()
check)
Migration Example:
// Apollo Client 3
if (error instanceof ApolloError) {
console.log(error.graphQLErrors);
console.log(error.networkError);
}
// Apollo Client 4
import { CombinedGraphQLErrors } from "@apollo/client";
if (CombinedGraphQLErrors.is(error)) {
console.log(error.errors); // GraphQL errors
} else if (error) {
console.log(error.message); // Other errors
}
The dataState
Property
A new property that clearly indicates the completeness of query results:
Values:
empty
- No data available (data
isundefined
)partial
- Incomplete data from cache whenreturnPartialData
istrue
streaming
- Incomplete data from a deferred query still streamingcomplete
- Fully satisfied query result
Benefits:
- Accurate TypeScript type narrowing
- Clear loading state distinction
- Better handling of partial results
const { data, dataState } = useQuery(MY_QUERY);
if (dataState === "complete") {
// TypeScript knows data is fully populated
console.log(data.allFields);
} else if (dataState === "partial") {
// TypeScript knows data might be missing fields
console.log(data?.someField);
}
Pluggable Incremental Delivery (@defer
Support)
Apollo Client 4.0 makes incremental delivery configurable and future-proof:
import { Defer20220824Handler } from "@apollo/client/incremental";
const client = new ApolloClient({
// ...
incrementalHandler: new Defer20220824Handler(),
});
Available Handlers:
NotImplementedHandler
- Default, throws if@defer
is usedDefer20220824Handler
- Apollo Router format support (also aliased asGraphQL17Alpha2Handler
)
Local State Management Improvements
Local state is now opt-in via the LocalState
class:
import { LocalState } from "@apollo/client/local-state";
const client = new ApolloClient({
cache,
localState: new LocalState({
resolvers: {
Query: {
myField: () => "Hello World",
},
},
}),
});
Resolver Context Changes:
// Apollo Client 3
const resolver = (parent, args, context, info) => {
const { cache } = context;
};
// Apollo Client 4
const resolver = (parent, args, context, info) => {
const { client, requestContext, phase } = context;
const cache = client.cache;
};
React-Specific Improvements
More Predictable Hooks
useLazyQuery
Overhaul:
- No longer accepts
variables
orcontext
options (pass toexecute
instead) execute
function only acceptsvariables
andcontext
- Cannot be called during render or SSR
- Automatic cancellation of in-flight queries when new ones start
useMutation
Changes:
- Removed
ignoreResults
option - useclient.mutate
directly for fire-and-forget mutations
useQuery
Changes:
notifyOnNetworkStatusChange
now defaults totrue
- Removed deprecated
onCompleted
andonError
callbacks
New SSR API
The new prerenderStatic
API replaces deprecated SSR functions:
import { prerenderStatic } from "@apollo/client/react/ssr";
// Works with React 19's prerender APIs
const html = await prerenderStatic(<App />, {
client,
});
React Compiler Support
Pre-compiled React hooks optimized by the React Compiler:
// Use compiled hooks for potential performance improvements
import { useQuery } from "@apollo/client/react/compiled";
The compiled hooks are built with React Compiler v19.1.0-rc.2 and include a runtime polyfill for compatibility with React 17+.
Link System Evolution
All Links Now Classes
Migration from creator functions to classes:
// Apollo Client 3
import { createHttpLink, setContext } from "@apollo/client";
const httpLink = createHttpLink({ uri: "/graphql" });
const authLink = setContext((operation, prevContext) => {
/*...*/
});
// Apollo Client 4
import { HttpLink, SetContextLink } from "@apollo/client";
const httpLink = new HttpLink({ uri: "/graphql" });
const authLink = new SetContextLink((prevContext, operation) => {
/*...*/
});
ErrorLink Changes
// Apollo Client 3
onError(({ graphQLErrors, networkError }) => {
// Handle errors separately
});
// Apollo Client 4
new ErrorLink(({ error }) => {
if (CombinedGraphQLErrors.is(error)) {
// Handle GraphQL errors
} else if (error) {
// Handle other errors
}
});
Migration Tools
Automated Codemod
Apollo Client 4.0 provides a comprehensive codemod to automate migration:
# Basic usage
npx @apollo/client-codemod-migrate-3-to-4 src
# TypeScript projects (run separately)
npx @apollo/client-codemod-migrate-3-to-4 --parser ts --extensions ts src
npx @apollo/client-codemod-migrate-3-to-4--parser tsx --extensions tsx src
The codemod handles:
- Import updates - Moves React imports to
@apollo/client/react
- Type migrations - Updates types to new namespaced locations
- Link updates - Converts creator functions to classes
- Removed exports - Moves to
@apollo/client/v4-migration
with migration instructions
Breaking Changes Summary
Installation
# RxJS is now a peer dependency
npm install @apollo/client graphql rxjs
ApolloClient Constructor
link
option is now required (no more implicitHttpLink
creation)uri
,headers
,credentials
removed - useHttpLink
directlyname
andversion
moved toclientAwareness
optionresolvers
moved toLocalState
constructorconnectToDevTools
replaced withdevtools.enabled
disableNetworkFetches
renamed toprioritizeCacheValues
Type System
- Removed
TContext
andTCacheShape
generics - Types moved to namespaces (see migration guide for full list)
- Custom context via module augmentation
Observable Changes
- Requires calling
.pipe()
for transformations - Use RxJS operators instead of method chaining
Testing
MockedProvider
now has realistic delays by default (20-50ms)createMockClient
removed - useMockLink
directly
Performance & Build Improvements
- Modern JavaScript: No downlevel transpilation for modern features
- No Polyfills: Cleaner bundles, bring your own if needed
- Development Mode: Controlled via export conditions, not global
__DEV__
- ESM Support: Proper
exports
field for better module resolution - Source Maps: Fixed and improved for better debugging
Deprecations & Removals
Removed Packages/Exports
- React render prop components (
@apollo/client/react/components
) - Higher-order components (
@apollo/client/react/hoc
) @apollo/client/react/parser
@apollo/client/utilities/globals
Upgrade Path
- Update to Apollo Client 3.14 first for deprecation warnings
- Install peer dependencies:
npm install rxjs
- Run the codemod to automate import and type updates
- Update ApolloClient initialization (explicit
HttpLink
,LocalState
if needed) - Review error handling - update to use new error classes
- Test thoroughly - especially SSR, error handling, and local state
Resources
Acknowledgments
Apollo Client 4.0 represents years of community feedback and contributions. Thank you to all our contributors, early adopters, and the entire GraphQL community for making this release possible.
#12644 #12673 If #12686 #12539 #12586 #12384 #12398 #12379 If you are using #12396 #12809 #12809 #12712 #12222 #12787 #12450 #12614 #12376 #12644 NOTE: If you use a testing utility to mock requests in your test, you may experience different behavior than production if your testing utility responds as #12600 As a result of this change, utilities or types exported from #12513 #12384 #12463 As a result of this change, #12478 #12735 #12673 #12367 #12690 #12556 #12776 #12788 #12224 #12407 #12476 #12457 #12840 #12712 #12808 #12384 #12825 #12595 #12718 #12470 Previously, a Now, the #12559 #12559 #12735 #12576 #12533 #12485 #12556 #12663 #12809 #12398 These errors were mutally exclusive, meaning both GraphQL errors are now encapsulated in a Network errors are no longer wrapped and are instead passed through directly.
Protocol errors are now encapsulated in a These were unused by the client and have no replacement. Any non-GraphQL or non-protocol errors are now passed through unwrapped.
If the link sends a string error, Apollo Client will wrap this in an If the link chain sends any other object type as an error, Apollo Client will wrap this in an #12809 #12450 #12774 NOTE: This change does not affect the equivalent #12705 This means #12475 #12384 #12649 To migrate, use the following guide to replace your type with the right set of states (all types listed below are changed the same way):
The following types are affected. Provide the allowed All #12850 #12809 #12802 #12211 #12690 #12559 #12262 #12673 This could look like this:
This feature can be disabled by passing #12742 #12536 #12465 #12675 #12398 #12712 #12463 #12367 #12614 #12475 #12367 To change options, rerender the component with new options. These options will take effect with the next query execution.
#12384 #12681 Options that now trigger a #12787 #12837 If you're using GraphQL Codegen to generate masked types, opt into the GraphQL Codegen masked types using declaration merging on the #12824 #12398 #12837 As a result, the #12304 When #12731 We now ship a React-Compiler compiled version of the React hooks in This entry point contains everything that #12446 #12649 #12396 #12367 If you need access to the additional properties such as #12531 #12793 #12714 #12614 #12576 #12526 #12700 You can override this type globally - this example shows how to override it #12367 If you prefer the behavior from 3.x, rerender the component with #12475 #12254 #12633 Please keep in mind that #12513 #12430 #12685 #12385 In modern bundlers, this should automatically be handled by the bundler loading If neither the Unlike Apollo Client 3 though, if This switch to explicilty requiring #12644 #12476 #12499 #12367 #12333 This will affect you in a handful of places:
#12644 #12742 This change means the #12639 The following APIs were removed. To migrate, update usages of the following APIs as such:
#12614 #12367 #12684 #12704 The #12823 #12223 #12300 The following is a list of exports available in The following is a list of exports available in #12525 #12588 #12647 That means that This changes the previous meaning of #12809 #12428 #12590 #12678 #12556 #12637 #12478 #12384 This change is mostly transparent, however transforming values on observables, common in link implementations, differs in RxJS vs For a full list of operators and comprehensive documentation on the capabilities of RxJS, check out the documentation.
#12442 #12809 If you have a custom link that overrides the #12329 We have reworked the way Apollo Client is packaged.
#12566 #12397 #12384 #12388 If you are running in an environment without #12367 #12631 #12639 #12591 #12367 If you use #12823 #12809 #12556 #12367 This change means the execute function returned from #12614 To migrate, pull any request context from #12712 #12704 #12614 #12532 If you would like to restore the old behavior, use a global default delay of #12442 #12304 If you use The client now expects #12433 This removes support for In the WhatWG Fetch specification, At this point in time, this is natively supported in browsers, If you are using an older #12484 This affects the following APIs:
#12512 #12614 #12430 #12359 See #12352 for more context on this change.
#12384 #12476 #12367 You can now safely rerender #12809 #12464 #12384 #12476 When an error terminates the downstream connection, a #12841 #12809 #12715 Please migrate these function calls to class creations:
#12530 To replicate the old behavior of with with #12556 #12647 #12809 As a result of this change, #12589 If using If creating a client without the #12304 If you use The client now expects #12451 #12562 #12211
Major Changes
fe2f005
Thanks @jerelmiller! - Replace the result
property on ServerError
with bodyText
. bodyText
is set to the raw string body. HttpLink
and BatchHttpLink
no longer try and parse the response body as JSON when a ServerError
is thrown.
cee90ab
Thanks @phryneas! - The includeExtensions
option of HttpLink
and BatchHttpLink
now defaults
to true
.
includeExtensions
is true
, but extensions
is not set or empty, extensions
will not be included in outgoing requests.
dc4b1d0
Thanks @jerelmiller! - A @defer
query that has not yet finished streaming is now considered loading and thus the loading
flag will be true
until the response has completed. A new NetworkStatus.streaming
value has been introduced and will be set as the networkStatus
while the response is streaming.
dd0d6d6
Thanks @jerelmiller! - onError
link now uses a single error
property to report the error that caused the link callback to be called. This will be an instance of CombinedGraphQLErrors
in the event GraphQL errors were emitted from the terminating link, CombinedProtocolErrors
if the terminating link emitted protocol errors, or the unwrapped error type if any other non-GraphQL error was thrown or emitted.
- const errorLink = onError(({ graphQLErrors, networkError, protocolErrors }) => {
- graphQLErrors.forEach(error => console.log(error.message));
+ const errorLink = onError(({ error }) => {
+ if (error.name === 'CombinedGraphQLErrors') {
+ error.errors.forEach(rawError => console.log(rawError.message));
+ }
});
605db8e
Thanks @jerelmiller! - Remove the typeDefs
option from ApolloClient
.
6aa6fd3
Thanks @jerelmiller! - Remove the asyncMap
utility function. Instead use one of the RxJS operators that creates Observables from promises, such as from
.
8cf5077
Thanks @jerelmiller! - Removes the isApolloError
utility function to check if the error object is an ApolloError
instance. Use instanceof
to check for more specific error types that replace ApolloError
.
ef892b4
Thanks @jerelmiller! - Removes the addTypename
option from InMemoryCache
and MockedProvider
. __typename
is now always added to the outgoing query document when using InMemoryCache
and cannot be disabled.
<MockedProvider />
with addTypename={false}
, ensure that your mocked responses include a __typename
field. This will ensure cache normalization kicks in and behaves more like production.
00f3d0a
Thanks @jerelmiller! - Remove the deprecated errors
property from useQuery
and useLazyQuery
. Read errors from the error
property instead.
e2a0be8
Thanks @jerelmiller! - operation.getContext
now returns a Readonly<OperationContext>
type.
e2a0be8
Thanks @jerelmiller! - The ApolloLink.Request
(i.e. GraphQLRequest
) passed to ApolloLink.execute
no longer accepts operationName
and operationType
options. These properties are derived from the query
and set on the returned ApolloLink.Operation
type.
bbb2b61
Thanks @jerelmiller! - An error is now thrown when trying to call fetchMore
on a cache-only
query.
d1a9054
Thanks @jerelmiller! - Drop support for React 16.
8ce31fa
Thanks @phryneas! - Remove DataProxy
namespace and interface.
876d070
Thanks @jerelmiller! - Remove TSerialized
generic argument to ApolloCache
. The ApolloCache
base cache abstraction now returns unknown
for cache.extract
which can be overridden by a cache subclass.
d2851e2
Thanks @jerelmiller! - The getCacheKey
function is no longer available from operation.getContext()
in the link chain. Use operation.client.cache.identify(obj)
in the link chain instead.
a0c996a
Thanks @jerelmiller! - Remove deprecated ignoreResults
option from useMutation
. If you don't want to synchronize component state with the mutation, use useApolloClient
to access your client instance and use client.mutate
directly.
fe2f005
Thanks @jerelmiller! - More strictly adhere to the GraphQL over HTTP spec. This change adds support for the application/graphql-response+json
media type and modifies the behavior of the application/json
media type.
content-type
using application/graphql-response+json
with a non-200 status code.
ServerError
when the server encodes content-type
using application/json
and returns a non-200 status code.
ServerError
when the server encodes using any other content-type
and returns a non-200 status code.
application/json
but your production server responds as application/graphql-response+json
. If a content-type
header is not set, the client interprets the response as application/json
.
34ff6aa
Thanks @jerelmiller! - Move most of the utilities in @apollo/client/utilities
to @apollo/client/utilities/internal
. Many of the utilities exported from the @apollo/client/utilities
endpoint were not considered stable.
@apollo/client/utilities
are now documented and considered stable and will not undergo breaking changes.
9c3207c
Thanks @phryneas! - Removed the @apollo/client/react/context
and @apollo/client/react/hooks
entry points. Please use @apollo/client/react
instead.
6aa6fd3
Thanks @jerelmiller! - Unusubscribing from ObservableQuery
while a request is in flight will no longer terminate the request by unsubscribing from the link observable.
3868df8
Thanks @jerelmiller! - ObservableQuery.setOptions
has been removed as it was an alias of reobserve
. Prefer using reobserve
directly instead.
const observable = client.watchQuery(options);
// Use reobserve to set new options and reevaluate the query
- observable.setOptions(newOptions);
+ observable.reobserve(newOptions);
reobserve
has been marked for public use and is no longer considered an internal API. The newNetworkStatus
argument has been removed to facilitate this change.
5ea6a45
Thanks @jerelmiller! - Remove variables
from the result returned from useSubscription
.
5159880
Thanks @jerelmiller! - Remove deprecated resultCacheMaxSize
option from InMemoryCache
options.
cee90ab
Thanks @phryneas! - The ApolloClient
constructor options name
and version
that are used to
configure the client awareness feature have moved onto a clientAwareness
key.
const client = new ApolloClient({
// ..
- name: "my-app",
- version: "1.0.0",
+ clientAwareness: {
+ name: "my-app",
+ version: "1.0.0",
+ },
});
e6af35e
Thanks @jerelmiller! - The previousData
property on useLazyQuery
will now change only when data
changes. Previously previousData
would change to the same value as data
while the query was loading.
5812759
Thanks @phryneas! - Aliasing any other field to __typename
is now forbidden.
c3fceda
Thanks @phryneas! - ObservableQuery
will now keep previous data
around when emitting a loading
state, unless query
or variables
changed.
Note that @exports
variables are not taken into account for this, so data
will stay around even if they change.
bce9b74
Thanks @jerelmiller! - Report masked fragments as complete even when a nested masked fragment contains partial data.
4179446
Thanks @phryneas! - TVariables
now always extends OperationVariables
in all interfaces.
51e6c0f
Thanks @jerelmiller! - Remove deprecated partialRefetch
option.
8b1390b
Thanks @jerelmiller! - Calling refetch
with new variables will now set the networkStatus
to refetch
instead of setVariables
.
6afff60
Thanks @jerelmiller! - Subscriptions now emit a SubscribeResult
instead of a FetchResult
. As a result, the errors
field has been removed in favor of error
.
32e85ea
Thanks @jerelmiller! - Network errors triggered by queries now adhere to the errorPolicy
. This means that GraphQL errors and network errors now behave the same way. Previously promise-based APIs, such as client.query
, would reject the promise with the network error even if errorPolicy
was set to ignore
. The promise is now resolved with the error
property set to the network error instead.
83e132a
Thanks @phryneas! - If you use an incremental delivery handler, you now have to explicitly opt into adding the chunk types to the ApolloLink.Result
type.
import { Defer20220824Handler } from "@apollo/client/incremental";
declare module "@apollo/client" {
export interface TypeOverrides extends Defer20220824Handler.TypeOverrides {}
}
bbb2b61
Thanks @jerelmiller! - cache-only
queries are no longer refetched when calling client.reFetchObservableQueries
when includeStandby
is true
.
8e31a23
Thanks @phryneas! - HTTP Multipart handling will now throw an error if the connection closed before the final boundary has been received.
Data after the final boundary will be ignored.
6aa6fd3
Thanks @jerelmiller! - Remove the iterateObserversSafely
utility function.
292b949
Thanks @jerelmiller! - The serializeFetchParameter
helper is no longer exported and JSON.stringify
is used directly. As such, the ClientParseError
type has also been removed in favor of throwing any JSON serialize errors directly.
60bb49c
Thanks @jerelmiller! - Remove the @apollo/client/testing/experimental
test utilities. Use GraphQL Testing Library instead.
ecfc02a
Thanks @jerelmiller! - Version bump only to release latest as rc
.
d32902f
Thanks @phryneas! - ssrMode
, ssrForceFetchDelay
and disableNetworkFetches
have been reworked:
ObservableQuery
created by client.query
or client.watchQuery
while one of those were active would permanently be changed from a fetchPolicy
of "network-only"
or "cache-and-network"
to "cache-first"
, and stay that way
even long after disableNetworkFetches
would have been deactivated.
ObservableQuery
will keep their original fetchPolicy
, but queries
made during disableNetworkFetches
will just apply the fetchPolicy
replacement
at request time, just for that one request.
ApolloClient.disableNetworkFetches
has been renamed to ApolloClient.prioritizeCacheValues
to better reflect this behaviour.
49ace0e
Thanks @jerelmiller! - ObservableQuery.variables
can now be reset back to empty when calling reobserve
with variables: undefined
. Previously the variables
key would be ignored so variables
would remain unchanged.
49ace0e
Thanks @jerelmiller! - never
is no longer supported as a valid TVariables
generic argument for APIs that require variables
as part of its type. Use Record<string, never>
instead.
5159880
Thanks @jerelmiller! - Remove deprecated connectToDevtools
option from ApolloClientOptions
. Use devtools.enabled
instead.
a92ff78
Thanks @jerelmiller! - The cache
and forceFetch
properties are no longer available on context when calling operation.getContext()
. cache
can be accessed through the operation
with operation.client.cache
instead. forceFetch
has been replaced with queryDeduplication
which specifies whether queryDeduplication
was enabled for the request or not.
73221d8
Thanks @jerelmiller! - Remove the onError
and setOnError
methods from ApolloLink
. onError
was only used by MockLink
to rewrite errors if setOnError
was used.
d338303
Thanks @jerelmiller! - Throw an error for queries and mutations if the link chain completes without emitting a value.
c3fceda
Thanks @phryneas! - Removed getLastResult
, getLastError
and resetLastResults
from ObservableQuery
01512f2
Thanks @jerelmiller! - Unsubscribing from an ObservableQuery
before a value has been emitted will remove the query from the tracked list of queries and will no longer be eligible for query deduplication.
e2a0be8
Thanks @jerelmiller! - operation.operationType
is now a non-null OperationTypeNode
. It is now safe to compare this value without having to check for undefined
.
8cf5077
Thanks @jerelmiller! - Apollo Client no longer wraps errors in ApolloError
. ApolloError
has been replaced with separate error classes depending on the cause of the error. As such, APIs that return an error
property have been updated to use the generic Error
type. Use instanceof
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 the errors
field by the GraphQL server
networkError
- Any non-GraphQL error that caused the query to fail
protocolErrors
- Transport-level errors that occur during multipart HTTP subscriptions
clientErrors
- A space to define custom errors. Mostly unused.
networkError
and graphqlErrors
were never set simultaneously. The following replaces each of these fields from ApolloError
.
graphqlErrors
CombinedGraphQLErrors
instance. You can access the raw GraphQL errors via the errors
property.
import { CombinedGraphQLErrors } from "@apollo/client";
// ...
const { error } = useQuery(query);
if (error && error instanceof CombinedGraphQLErrors) {
console.log(error.errors);
}
networkError
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
CombinedProtocolErrors
instance. You can access the raw protocol errors via the errors
property.
import { CombinedProtocolErrors } from "@apollo/client";
// ...
const { error } = useSubscription(subscription);
if (error && error instanceof CombinedProtocolErrors) {
console.log(error.errors);
}
clientErrors
Strings as errors
Error
instance. This ensures error
properties are guaranteed to be of type Error
.
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
UnknownError
instance with the cause
set to the original object. This ensures error
properties are guaranteed to be of type Error
.
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.
e2a0be8
Thanks @jerelmiller! - operation.operationName
is now set as string | undefined
where undefined
represents an anonymous query. Previously operationName
would return an empty string as the operationName
for anonymous queries.
876d070
Thanks @jerelmiller! - Remove the TCacheShape
generic argument to ApolloClient
. client.extract()
now returns unknown
by default. You will either need to type-cast this to the expected serialized shape, or use the cache.extract()
directly from the subclass to get more specific types.
511b4f3
Thanks @jerelmiller! - Apply document transforms before reading data from the cache for client.readQuery
, client.readFragment
, client.watchFragment
, useFragment
, and useSuspenseFragment
.
cache.*
APIs. To read data from the cache without first running document transforms, run cache.readQuery
, cache.readFragment
, etc.
a60f411
Thanks @jerelmiller! - cache-only
queries will now initialize with loading: false
and networkStatus: NetworkStatus.ready
when there is no data in the cache.
useQuery
will no longer render a short initial loading state before rendering loading: false
and ObservableQuery.getCurrentResult()
will now return loading: false
immediately.
3de63eb
Thanks @jerelmiller! - Unify error behavior on mutations for GraphQL errors and network errors by ensuring network errors are subject to the errorPolicy
. Network errors created when using an errorPolicy
of all
will now resolve the promise and be returned on the error
property of the result, or stripped away when the errorPolicy
is none
.
6aa6fd3
Thanks @jerelmiller! - Remove fromError
utility function. Use throwError
instead.
0be92ad
Thanks @jerelmiller! - The TData
generic provided to types that return a dataState
property is now modified by the given DataState
generic instead of passing a modified TData
type. For example, a QueryRef
that could return partial data was defined as QueryRef<DeepPartial<TData>, TVariables>
. Now TData
should be provided unmodified and a set of allowed states should be given instead: QueryRef<TData, TVariables, 'complete' | 'streaming' | 'partial'>
.
- QueryRef<TData, TVariables>
// `QueryRef`'s default is 'complete' | 'streaming' so this can also be left alone if you prefer
// All other types affected by this change default to all states
+ QueryRef<TData, TVariables>
+ QueryRef<TData, TVariables, 'complete' | 'streaming'>
- QueryRef<TData | undefined, TVariables>
+ QueryRef<TData, TVariables, 'complete' | 'streaming' | 'empty'>
- QueryRef<DeepPartial<TData>, TVariables>
+ QueryRef<TData, TVariables, 'complete' | 'streaming' | 'partial'>
- QueryRef<DeepPartial<TData> | undefined, TVariables>
+ QueryRef<TData, TVariables, 'complete' | 'streaming' | 'partial' | 'empty'>
dataState
values to the TDataState
generic:
ApolloQueryResult
QueryRef
PreloadedQueryRef
useLazyQuery.Result
useQuery.Result
useReadQuery.Result
useSuspenseQuery.Result
*QueryRef
types default to complete | streaming
states while the rest of the types default to 'complete' | 'streaming' | 'partial' | 'empty'
states. You shouldn't need to provide the states unless you need to either allow for partial data/empty values (*QueryRef
) or a restricted set of states.
268cd80
Thanks @phryneas! - Introduce a versioning policy.
e2a0be8
Thanks @jerelmiller! - The concat
, from
, and split
functions on ApollLink
no longer support a plain request handler function. Please wrap the request handler with new ApolloLink
.
const link = new ApolloLink(/* ... */);
link.concat(
- (operation, forward) => forward(operation),
+ new ApolloLink((operation, forward) => forward(operation)),
);
e2b51b3
Thanks @jerelmiller! - Disallow the mutation
option for the mutate
function returned from useMutation
.
c2736db
Thanks @jerelmiller! - Remove the deprecated graphql
, withQuery
, withMutation
, withSubscription
, and withApollo
hoc components. Use the provided React hooks instead.
5812759
Thanks @phryneas! - Aliasing a field to an alias beginning with __ac_
is now forbidden - this namespace is now reserved for internal use.
49ace0e
Thanks @jerelmiller! - When passing a variables
key with the value undefined
, the value will be replaced by the default value in the query, if it is provided, rather than leave it as undefined
.
// given this query
const query = gql`
query PaginatedQuery($limit: Int! = 10, $offset: Int) {
list(limit: $limit, offset: $offset) {
id
}
}
`;
const observable = client.query({
query,
variables: { limit: 5, offset: 0 },
});
console.log(observable.variables); // => { limit: 5, offset: 0 }
observable.reobserve({ variables: { limit: undefined, offset: 10 } });
// limit is now `10`. This would previously be `undefined`
console.log(observable.variables); // => { limit: 10, offset: 10 }
10ef733
Thanks @jerelmiller! - Remove itAsync
test utility.
cee90ab
Thanks @phryneas! - Adds enhanced client awareness to the client.
HttpLink
and BatchHttpLink
will now per default send information about the
client library you are using in extensions
.
{
"query": "query GetUser($id: ID!) { user(id: $id) { __typename id name } }",
"variables": {
"id": 5
},
"extensions": {
"clientLibrary": {
"name": "@apollo/client",
"version": "4.0.0"
}
}
}
enhancedClientAwareness: { transport: false }
to your
ApolloClient
, HttpLink
or BatchHttpLink
constructor options.
575bf3e
Thanks @jerelmiller! - The new SetContextLink
flips the prevContext
and operation
arguments in the callback. The setContext
function has remained unchanged.
- new SetContextLink((operation, prevContext) => {
+ new SetContextLink((prevContext, operation) => {
// ...
})
e14205a
Thanks @jerelmiller! - An initial loading state is now emitted from ObservableQuery
when subscribing if notifyOnNetworkStatusChange
is set to true
.
a132163
Thanks @jerelmiller! - Flatten out React hook types. As a result, the base types have been removed. Prefer using the hook types instead. Removed types include:
BaseMutationOptions
BaseQueryOptions
BaseSubscriptionOptions
ObservableQueryFields
MutationSharedOptions
QueryFunctionOptions
8f1d974
Thanks @phryneas! - ObservableQuery
no longer has a queryId
property.
ApolloClient.getObservableQueries
no longer returns a Map<string, ObservableQuery>
, but a Set<ObservableQuery>
.
8cf5077
Thanks @jerelmiller! - Updates the ServerError
and ServerParseError
types to be proper Error
subclasses. Perviously these were plain Error
intances with additional properties added at runtime. All properties are retained, but instanceof
checks now work correctly.
import { ServerError, ServerParseError } from "@apollo/client";
if (error instanceof ServerError) {
// ...
}
if (error instanceof ServerParseError) {
// ...
}
bbb2b61
Thanks @jerelmiller! - cache-only
queries are now excluded from client.refetchQueries
in all situations. cache-only
queries affected by updateCache
are also excluded from refetchQueries
when onQueryUpdated
is not provided.
3868df8
Thanks @jerelmiller! - useQuery
no longer returns reobserve
as part of its result. It was possible to use reobserve
to set new options on the underlying ObservableQuery
instance which differed from the options passed to the hook. This could result in unexpected results. Instead prefer to rerender the hook with new options.
e6af35e
Thanks @jerelmiller! - useLazyQuery
no longer supports SSR environments and will now throw if the execute
function is called in SSR. If you need to run a query in an SSR environment, use useQuery
instead.
d2851e2
Thanks @jerelmiller! - Removes the resolvers
option from ApolloClient
. Local resolvers have instead been moved to the new LocalState
instance which is assigned to the localState
option in ApolloClient
. To migrate, move the resolvers
values into a LocalState
instance and assign that instance to localState
.
new ApolloClient({
- resolvers: { /* ... */ }
+ localState: new LocalState({
+ resolvers: { /* ... */ }
+ }),
});
3de63eb
Thanks @jerelmiller! - client.mutate
now returns a MutateResult
instead of FetchResult
. As a result, the errors
property has been removed in favor of error
which is set if either a network error occured or GraphQL errors are returned from the server.
useMutation
now also returns a MutateResult
instead of a FetchResult
.
e6af35e
Thanks @jerelmiller! - The execute function returned from useLazyQuery
now only supports the context
and variables
options. This means that passing options supported by the hook no longer override the hook value.
6aa6fd3
Thanks @jerelmiller! - ObservableQuery
will no longer terminate on errors and will instead emit a next
value with an error
property. This ensures that ObservableQuery
instances can continue to receive updates after errors are returned in requests without the need to resubscribe to the observable.
b181f98
Thanks @jerelmiller! - Changing most options when rerendering useQuery
will no longer trigger a reobserve
which may cause network fetches. Instead, the changed options will be applied to the next cache update or fetch.
reobserve
when changed between renders are:
query
variables
skip
fetchPolicy
to or from standby
8ce31fa
Thanks @phryneas! - Generic arguments for Cache.ReadOptions
were flipped from TVariables, TData
to TData, TVariables
.
7c49fdc
Thanks @jerelmiller! - You must now opt in to use GraphQL Codegen data masking types when using Apollo Client's data masking feature. By default, Apollo Client now uses an identity type to apply to masked/unmasked types.
TypeOverides
interface.
import { GraphQLCodegenDataMasking } from "@apollo/client/masking";
declare module "@apollo/client" {
export interface TypeOverrides
extends GraphQLCodegenDataMasking.TypeOverrides {}
}
0506f12
Thanks @jerelmiller! - Ensure the error
argument for the delay
and attempts
functions on RetryLink
are an ErrorLike
.
8cf5077
Thanks @jerelmiller! - Removes the throwServerError
utility function. Now that ServerError
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 });
7c49fdc
Thanks @jerelmiller! - The types mode for data masking has been removed. Adding a types mode to the DataMasking
interface has no effect. Remove the mode
key in the module where you declare the DataMasking
type for the @apollo/client
module.
Masked
and MaskedDocumentNode
types have also been removed since these have no effect when types are preserved.
86469a2
Thanks @jerelmiller! - The Cache.DiffResult<T>
type is now a union type with better type safety for both complete and partial results. Checking diff.complete
will now narrow the type of result
depending on whether the value is true
or false
.
true
, diff.result
will be a non-null value equal to the T
generic type. When false
, diff.result
now reports result
as DeepPartial<T> | null
indicating that fields in the result may be missing (DeepPartial<T>
) or empty entirely (null
).
0198870
Thanks @phryneas! - Ship React Compiler compiled React hooks in @apollo/client/react/compiled
.
@apollo/client/react/compiled
.
@apollo/client/react
does,
so you can use it as a drop-in replacement in your whole application
if you choose to use the compiled hooks.
ab920d2
Thanks @jerelmiller! - Removes the defaultOptions
option from useQuery
. Use options directly or use the global ApolloClient
defaultOptions
.
0be92ad
Thanks @jerelmiller! - Remove the deprecated QueryReference
type. Please use QueryRef
instead.
00f3d0a
Thanks @jerelmiller! - Remove the errors
property from the results emitted from ObservableQuery
or returned from client.query
. Read errors from the error
property instead.
e6af35e
Thanks @jerelmiller! - The result resolved from the promise returned from the execute function in useLazyQuery
is now an ApolloQueryResult
type and no longer includes all the fields returned from the useLazyQuery
hook tuple.
called
, refetch
, etc. not included in ApolloQueryResult
, read them from the hook instead.
7784b46
Thanks @jerelmiller! - Mocked responses passed to MockLink
now accept a callback for the request.variables
option. This is used to determine if the mock should be matched for a set of request variables. With this change, the variableMatcher
option has been removed in favor of passing a callback to variables
. Update by moving the callback function from variableMatcher
to request.variables
.
new MockLink([
{
request: {
query,
+ variables: (requestVariables) => true
},
- variableMatcher: (requestVariables) => true
}
]);
24e98a1
Thanks @phryneas! - ApolloConsumer
has been removed - please use useApolloClient
instead.
0e39469
Thanks @phryneas! - Rework option handling for fetchMore
.
query
option was specified, no options would be inherited
from the underlying ObservableQuery
.
Now, even if query
is specified, all unspecified options except for variables
will be inherited from the underlying ObservableQuery
.
query
is not specified, variables
will still be shallowly merged with the variables
of the underlying ObservableQuery
. If a query
option is specified, the variables
passed to fetchMore
are used instead.
errorPolicy
of fetchMore
will now always default to "none"
instead of inherited from the ObservableQuery
options. This can prevent accidental cache writes of partial data for a paginated query. To opt into receive partial data that may be written to the cache, pass an errorPolicy
to fetchMore
to override the default.
d2851e2
Thanks @jerelmiller! - Remove local resolvers APIs from ApolloClient
in favor of localState
. Methods removed are:
addResolvers
getResolvers
setResolvers
setLocalStateFragmentMatcher
a92ff78
Thanks @jerelmiller! - ApolloLink.execute
now requires a third argument which provides the client
that initiated the request to the link chain. If you use execute
directly, add a third argument with a client
property:
ApolloLink.execute(link, operation, { client });
// or if you import the `execute` function directly:
execute(link, operation, { client });
391af1d
Thanks @phryneas! - The @apollo/client
and @apollo/client/core
entry points are now equal.
In the next major, the @apollo/client/core
entry point will be removed.
Please change imports over from @apollo/client/core
to @apollo/client
.
8e96e08
Thanks @phryneas! - Added a new Streaming
type that will mark data
in results while dataState
is "streaming"
.
Streaming<TData>
defaults to TData
, but can be overwritten in userland to
integrate with different codegen dialects.
with DeepPartial<TData>
:
import { HKT, DeepPartial } from "@apollo/client/utilities";
type StreamingOverride<TData> = DeepPartial<TData>;
interface StreamingOverrideHKT extends HKT {
return: StreamingOverride<this["arg1"]>;
}
declare module "@apollo/client" {
export interface TypeOverrides {
Streaming: StreamingOverrideHKT;
}
}
e6af35e
Thanks @jerelmiller! - useLazyQuery
will no longer rerender with the loading state when calling the execute function the first time unless the notifyOnNetworkStatusChange
option is set to true
(which is the new default).
notifyOnNetworkStatusChange
set to false
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);
}
// ...
}
3de63eb
Thanks @jerelmiller! - Mutations no longer report errors if the GraphQL result from the server contains an empty array of errors.
0028ac0
Thanks @jerelmiller! - Changes the default Accept
header to application/graphql-response+json
.
9bfb51f
Thanks @phryneas! - If the execute
function of useLazyQuery
is executed, previously started queries
from the same useLazyQuery
usage will be rejected with an AbortError
unless
.retain()
is called on the promise returned by previous execute
calls.
useLazyQuery
is primarily meant as a means to synchronize
your component to the status of a query and that it's purpose it not to make a
series of network calls.
If you plan on making a series of network calls without the need to synchronize
the result with your component, consider using ApolloClient.query
instead.
9c3207c
Thanks @phryneas! - Removed the @apollo/client/react/parser
entry point. There is no replacement.
2ff66d0
Thanks @jerelmiller! - ObservableQuery.setVariables
will now resolve with the last emitted result instead of undefined
when either the variables match the current variables or there are no subscribers to the query.
3b74800
Thanks @jerelmiller! - Remove the check and warning for cache.fragmentMatches
when applying data masking. cache.fragmentMatches
is a required API and data masking may crash when cache.fragmentMatches
does not exist.
cad5117
Thanks @phryneas! - Apollo Client now defaults to production mode, not development mode, if the
environment cannot be determined.
the bundler with the development
export condition.
production
nor the development
export condition are
used by the bundler/runtime, Apollo Client will fall back to globalThis.__DEV__
to determine if it should run in production or development mode.
globalThis.__DEV__
is not set to true
,
Apollo Client will now default to production
, not to development
, behaviour.
true
also resolves a situation where
an HTML element with id="__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.
fe2f005
Thanks @jerelmiller! - Change the default Accept
header to application/graphql-response+json,application/json;q=0.9
.
6afff60
Thanks @jerelmiller! - Unify error behavior on subscriptions for GraphQL errors and network errors by ensuring network errors are subject to the errorPolicy
. Network errors that terminate the connection will now be emitted on the error
property passed to the next
callback followed by a call to the complete
callback.
ce35ea2
Thanks @phryneas! - Enable React compiler for hooks in ESM builds.
e6af35e
Thanks @jerelmiller! - The reobserve
option is no longer available in the result returned from useLazyQuery
. This was considered an internal API and should not be used directly.
3e4beaa
Thanks @jerelmiller! - Fix type of data
property on ApolloQueryResult
. Previously this field was non-optional, non-null TData
, however at runtime this value could be set to undefined
. This field is now reported as TData | undefined
.
data
property emitted from the result passed to the next
callback from client.watchQuery
ApolloQueryResult
type such as observableQuery.refetch
, observableQuery.fetchMore
, etc.
fe2f005
Thanks @jerelmiller! - HttpLink
and BatchHttpLink
no longer emit a next
notification with the JSON-parsed response body when a well-formed GraphQL response is returned and a ServerError
is thrown.
575bf3e
Thanks @jerelmiller! - The operation
argument to the callback passed to SetContextLink
is now of type SetContextLink.SetContextOperation
which is an Operation
without the getContext
or setContext
functions. Previously the type of operation
was GraphQLRequest
which had access to a context
property. The context
property was always undefined
and could result in bugs when using it instead of the prevContext
argument.
operation
argument now contains an accessible client
property.
1bdf489
Thanks @jerelmiller! - Move internal testing utilities in @apollo/client/testing
to @apollo/client/testing/internal
and remove deprecated testing utilities. Some of the testing utilities exported from the @apollo/client/testing
endpoint were not considered stable. As a result of this change, testing utilities or types exported from @apollo/client/testing
are now considered stable and will not undergo breaking changes.
createMockClient
- const client = createMockClient(data, query, variables);
+ const client = new ApolloClient({
+ cache: new InMemoryCache(),
+ link: new MockLink([
+ {
+ request: { query, variables },
+ result: { data },
+ }
+ ]),
+ });
mockObservableLink
- const link = mockObservableLink();
+ const link = new MockSubscriptionLink();
mockSingleLink
- const link = mockSingleLink({
- request: { query, variables },
- result: { data },
- });
+ const link = new MockLink([
+ {
+ request: { query, variables },
+ result: { data },
+ }
+ ]);
d2851e2
Thanks @jerelmiller! - Third-party caches must now implement the fragmentMatches
API. Additionally fragmentMatches
must be able to handle both InlineFragmentNode
and FragmentDefinitionNode
nodes.
class MyCache extends ApolloCache {
// This is now required
public fragmentMatches(
fragment: InlineFragmentNode | FragmentDefinitionNode,
typename: string
): boolean {
return; // ... logic to determine if typename matches fragment
}
}
e6af35e
Thanks @jerelmiller! - The promise returned when calling the execute function from useLazyQuery
will now reject when using an errorPolicy
of none
when GraphQL errors are returned from the result.
e697431
Thanks @jerelmiller! - Remove context
from useLazyQuery
hook options. If used, context
must now be provided to the execute
function. context
will reset to {}
if not provided as an option to execute
.
45dba43
Thanks @jerelmiller! - The ErrorResponse
object passed to the disable
and retry
callback options provided to createPersistedQueryLink
no longer provides separate graphQLErrors
and networkError
properties and instead have been combined to a single error
property of type ErrorLike
.
// The following also applies to the `retry` function since it has the same signature
createPersistedQueryLink({
- disable: ({ graphQLErrors, networkError }) => {
+ disable: ({ error }) => {
- if (graphQLErrors) {
+ if (CombinedGraphQLErrors.is(error)) {
// ... handle GraphQL errors
}
- if (networkError) {
+ if (error) {
// ... handle link errors
}
// optionally check for a specific kind of error
- if (networkError) {
+ if (ServerError.is(error)) {
// ... handle a server error
}
});
response
property has also been renamed to result
.
createPersistedQueryLink({
- disable: ({ response }) => {
+ disable: ({ result }) => {
// ... handle GraphQL errors
}
}
});
19e315e
Thanks @jerelmiller! - Move all 1st party link types into a namespace.
69c1cb6
Thanks @jerelmiller! - Remove subscribeAndCount
testing utility from @apollo/client/testing
.
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.
@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
@apollo/client/testing
that should now import from @apollo/client/testing/react
:
MockedProvider
8785186
Thanks @jerelmiller! - Throw an error when a client-only query is used in a mocked response passed to MockLink
.
eed825a
Thanks @jerelmiller! - Remove TContext
generic argument from all types that use it. TContext
is replaced with DefaultContext
which can be modified using declaration merging.
a70fac6
Thanks @phryneas! - ObservableQuery
s will now only be registered with the ApolloClient
while they
have subscribers.
ApolloClient.getObservableQueries
and ApolloClient.refetchQueries
will only be able to return/refetch queries that have at least one subscriber.
active
and inactive
queries:
inactive
queries are queries with a subscriber that are skipped from a
React hook or have a fetchPolicy
of standby
active
queries are queries with at least one subscriber that are not skipped or in standby
.
ObservableQuery
s without subscribers but with an active ongoing network request
(e.g. caused by calling reobserve
) will be handled as if they had a subscriber
for the duration of the query.
e2a0be8
Thanks @jerelmiller! - transformOperation
and validateOperation
have been removed and are no longer exported from @apollo/client/link/utils
. These utilities have been merged into the implementation of createOperation
. As a result, createOperation
now returns a well-formed Operation
object. Previously createOperation
relied on an external call to transformOperation
to provide a well-formed Operation
type. If you use createOperation
directly, remove the calls to transformOperation
and validateOperation
and pass the request directly.
abed922
Thanks @jerelmiller! - Removes the urql
multipart subscriptions utilities. Use the native multipart subscriptions support in urql
instead.
a005e82
Thanks @jerelmiller! - Drop graphql
v15 as a valid peer dependency.
91a876b
Thanks @jerelmiller! - queryRef
s created by preloadQuery
no longer have a .toPromise()
function. Instead preloadQuery
now has a toPromise
function that accepts a queryRef and will resolve when the underlying promise has been resolved.
const queryRef = preloadQuery(query, options);
- await queryRef.toPromise();
+ await preloadQuery.toPromise(queryRef);
c3fceda
Thanks @phryneas! - Reworked the logic for then a loading state is triggered. If the link chain responds synchronously, a loading state will be omitted, otherwise it will be triggered.
If local resolvers are used, the time window for "sync vs async" starts as soon as @exports
variables are resolved.
d2a60d4
Thanks @phryneas! - useQuery
: only advance previousData
if data
actually changed
5ea6a45
Thanks @jerelmiller! - Remove deprecated onSubscriptionData
and onSubscriptionComplete
callbacks from useSubscription
. Use onData
and onComplete
instead.
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 install rxjs
in addition to @apollo/client
.
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 a map
operator instead.
import { map } from "rxjs";
const link new ApolloLink((operation, forward) => {
return forward(operation).pipe(
map((result) => performTransform(result))
);
});
c5ead08
Thanks @jerelmiller! - Remove the deprecated canonizeResults
option. It was prone to memory leaks. As such, some results that were referentially equal when canonizeResults
option was set to true
no longer retain the same object identity.
e2a0be8
Thanks @jerelmiller! - The request handler provided to ApolloLink
must now return an Observable
. null
is no longer supported as a valid return value. If you rely on null
so that ApolloLink
provides an empty observable, use the EMPTY
observable from RxJS instead:
import { ApolloLink } from "@apollo/client";
+ import { EMPTY } from "rxjs";
const link = new ApolloLink((operation, forward) => {
- return null;
+ return EMPTY;
});
request
method, remove null
from the return signature:
class MyCustomLink extends ApolloLink {
request(
operation: ApolloLink.Operation,
forward: ApolloLink.ForwardFunction,
- ): Observable<ApolloLink.Result> | null {
+ ): Observable<ApolloLink.Result> {
// implementation
}
}
61febe4
Thanks @phryneas! - Rework package publish format (#12329, #12382)
"since 2023, node >= 20, not dead"
)
package.json
files, e.g. cache/core/package.json
and react/package.json
. While these helped with older build tools, modern build tooling uses the exports
field in the root package.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.
exports
field to package.json
to expose entry points
globalThis.__DEV__
, Apollo Client now primarily relies on the development
and production
exports conditions. It falls back to globalThis.__DEV__
if the bundler doesn't know these, though.
ce4b488
Thanks @jerelmiller! - Don't broadcastQueries
when a query is torn down.
2545a54
Thanks @jerelmiller! - Remove ObservableQuery.resetQueryStoreErrors
method. This method reset some internal state that was not consumed elsewhere in the client and resulted in a no-op.
6aa6fd3
Thanks @jerelmiller! - Remove fromPromise
utility function. Use from
instead.
0d825be
Thanks @jerelmiller! - Require environments that support WeakMap
, WeakSet
and symbols. Apollo Client would fallback to Map
and Set
if the weak versions were not available. This has been removed and expects that these features are available in the source environment.
WeakMap
, WeakSet
or symbols, you will need to find appropriate polyfills.
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, use useQuery
instead or move the call to a useEffect
.
b147cac
Thanks @phryneas! - ObservableQuery
will now return a loading: false
state for fetchPolicy
standby
, even before subscription
1bdf489
Thanks @jerelmiller! - Remove the @apollo/client/testing/core
entrypoint in favor of @apollo/client/testing
.
a7e7383
Thanks @jerelmiller! - Rename the @apollo/client/link/core
entrypoint to @apollo/client/link
.
e6af35e
Thanks @jerelmiller! - The defaultOptions
and initialFetchPolicy
options are no longer supported with useLazyQuery
.
defaultOptions
, pass those options directly to the hook instead. If you use initialFetchPolicy
, use fetchPolicy
instead.
19e315e
Thanks @jerelmiller! - The OperationBatcher
class is no longer exported from @apollo/client/link/batch
. It is an implementation detail of BatchLink
and should not be relied on directly.
e2a0be8
Thanks @jerelmiller! - createOperation
no longer accepts context
as the first argument. Instead make sure context
is set as the context
property on the request passed to createOperation
.
createOperation(
- startingContext,
- { query },
+ { query, context: startingContext },
{ client }
);
c3fceda
Thanks @phryneas! - Dropped the saveAsLastResult
argument from ObservableQuery.getCurrentResult
e6af35e
Thanks @jerelmiller! - useLazyQuery
no longer supports variables
in the hook options and therefore no longer performs variable merging. The execute function must now be called with variables
instead.
function MyComponent() {
const [execute] = useLazyQuery(query);
function runExecute() {
execute({ variables: { ... }});
}
}
useLazyQuery
is more type-safe. The execute function will require you to pass a variables
option if the query type includes required variables.
d2851e2
Thanks @jerelmiller! - The resolver function's context
argument (the 3rd argument) has changed to provide additional information without the possibility of name clashes. Previously the context
argument would spread request context and override the client
and cache
properties to give access to both inside of a resolver. The context
argument takes now takes the following shape:
{
// the request context. By default `TContextValue` is of type `DefaultContext`,
// but can be changed if a `context` function is provided.
requestContext: TContextValue,
// The client instance making the request
client: ApolloClient,
// Whether the resolver is run as a result of gathering exported variables
// or resolving the value as part of the result
phase: "exports" | "resolve"
}
requestContext
and the cache
from the client
property:
new LocalState({
resolvers: {
Query: {
- myResolver: (parent, args, { someValue, cache }) => {
+ myResolver: (parent, args, { requestContext, client }) => {
+ const someValue = requestContext.someValue;
+ const cache = client.cache;
}
}
}
});
bbb2b61
Thanks @jerelmiller! - cache-only
queries no longer poll when a pollInterval
is set. Instead a warning is now emitted that polling has no effect. If the fetchPolicy
is changed to cache-only
after polling is already active, polling is stopped.
45dba43
Thanks @jerelmiller! - The response
property in onError
link has been renamed to result
.
- onError(({ response }) => {
+ onError(({ result }) => {
// ...
});
d2851e2
Thanks @jerelmiller! - Apollo Client no longer ships with support for @client
fields out-of-the-box and now must be opt-in. To opt in to use @client
fields, pass an instantiated LocalState
instance to the localState
option. If a query contains @client
and local state hasn't been configured, an error will be thrown.
import { LocalState } from "@apollo/client/local-state";
new ApolloClient({
localState: new LocalState(),
});
ae0dcad
Thanks @jerelmiller! - Default the delay
for all mocked responses passed to MockLink
using realisticDelay
. This ensures your test handles loading states by default and is not reliant on a specific timing.
0
.
MockLink.defaultOptions = {
delay: 0,
};
c5ead08
Thanks @jerelmiller! - Remove resetResultIdentities
option from InMemoryCache.gc()
. This affected object canonization which has been removed.
86469a2
Thanks @jerelmiller! - ### Changes for users of InMemoryCache
cache.diff
now returns null
instead of an empty object ({}
) when returnPartialData
is true
and the result is empty.
cache.diff
directly with returnPartialData: true
, you will need to check for null
before accessing any other fields on the result
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
cache.diff
to return null
instead of an empty object when there is no data that can be fulfilled from the cache and returnPartialData
is true
. If your cache implementation returns an empty object, please update this to return null
.
b86e50b
Thanks @phryneas! - Remove workarounds for streaming with non-WhatWG response bodies to reduce bundle size.
fetch
implementations that return Node Streams, Async Iterators or Blob instances as Response.body
.
Response.body
is specified as a WhatWG ReadableStream.
node
and React Native (via react-native-fetch-api, see our setup instructions for React Native).
fetch
polyfill that deviates from the spec, this might not be compatible - for example, node-fetch returns a node Readable
instead of a ReadableStream
.
In those cases, please switch to a compatible alternative such as the node
-native fetch
, or undici
.
9a8b9ce
Thanks @jerelmiller! - Remove loading
, networkStatus
, and partial
properties on all promise-based query APIs. These properties were mostly static and were unnecessary since promise resolution guaranteed that the query was not longer loading.
client.query
client.refetchQueries
client.reFetchObservableQueries
client.resetStore
observableQuery.fetchMore
observableQuery.refetch
observableQuery.reobserve
observableQuery.setVariables
useLazyQuery
execute
function
e809b71
Thanks @jerelmiller! - notifyOnNetworkStatusChange
now defaults to true
. This means that loading states will be emitted (core API) or rendered (React) by default when calling refetch
, fetchMore
, etc. To maintain the old behavior, set notifyOnNetworkStatusChange
to false
in defaultOptions
.
new ApolloClient({
defaultOptions: {
watchQuery: {
// Use the v3 default
notifyOnNetworkStatusChange: false,
},
},
});
d2851e2
Thanks @jerelmiller! - Remove the fragmentMatcher
option from ApolloClient
. Custom fragment matchers used with local state are no longer supported. Fragment matching is now performed by the configured cache
via the cache.fragmentMatches
API.
2ff66d0
Thanks @jerelmiller! - Removes ObservableQuery.result()
method. If you use this method and need similar functionality, use the firstValueFrom
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));
ebb4d96
Thanks @jerelmiller! - Remove the onCompleted
and onError
callbacks from useQuery
and useLazyQuery
.
6aa6fd3
Thanks @jerelmiller! - Subscriptions are no longer eagerly started after calling client.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)
});
6afff60
Thanks @jerelmiller! - GraphQL errors or network errors emitted while using an errorPolicy
of ignore
in subscriptions will no longer emit a result if there is no data
emitted along with the error.
e6af35e
Thanks @jerelmiller! - useLazyQuery
will now only execute the query when the execute function is called. Previously useLazyQuery
would behave like useQuery
after the first call to the execute function which means changes to options might perform network requests.
useLazyQuery
with new options which will take effect the next time you manually trigger the query.
e2a0be8
Thanks @jerelmiller! - Remove the TVariables
generic argument on the GraphQLRequest
type.
0595f39
Thanks @jerelmiller! - Remove the called
property from useQuery
.
6aa6fd3
Thanks @jerelmiller! - Remove toPromise
utility function. Use firstValueFrom
instead.
6afff60
Thanks @jerelmiller! - Subscriptions no longer emit errors in the error
callback and instead provide errors on the error
property on the result passed to the next
callback. As a result, errors will no longer automatically terminate the connection allowing additional results to be emitted when the connection stays open.
next
event will be emitted with an error
property followed by a complete
event instead.
65b503f
Thanks @jerelmiller! - Remove the DataMasking
interface exported from @apollo/client
and @apollo/client/masking
.
e2a0be8
Thanks @jerelmiller! - The context object returned from operation.getContext()
is now frozen to prevent mutable changes to the object which could result in subtle bugs. This applies to the previousContext
object passed to the operation.setContext()
callback as well.
0be0b3f
Thanks @phryneas! - All links are now available as classes. The old creator functions have been deprecated.
import {
- setContext
+ SetContextLink
} from "@apollo/client/link/context"
-const link = setContext(...)
+const link = new SetContextLink(...)
import {
- createHttpLink
+ HttpLink
} from "@apollo/client/link/http"
-const link = createHttpLink(...)
+const link = new HttpLink(...)
import {
- createPersistedQueryLink
+ PersistedQueryLink
} from "@apollo/client/link/persisted-queries"
-const link = createPersistedQueryLink(...)
+const link = new PersistedQueryLink(...)
import {
- removeTypenameFromVariables
+ RemoveTypenameFromVariablesLink
} from "@apollo/client/link/remove-typename"
-const link = removeTypenameFromVariables(...)
+const link = new RemoveTypenameFromVariablesLink(...)
2973e2a
Thanks @jerelmiller! - Remove newData
option for mocked responses passed to MockLink
or the mocks
option on MockedProvider
. This option was undocumented and was nearly identical to using the result
option as a callback.
newData
, use result
as a callback and add the maxUsageCount
option with a value set to Number.POSITIVE_INFINITY
.
MockLink
new MockLink([
{
request: { query, variables },
- newData: (variables) => ({ data: { greeting: "Hello " + variables.greeting } }),
+ result: (variables) => ({ data: { greeting: "Hello " + variables.greeting } }),
+ maxUsageCount: Number.POSITIVE_INFINITY,
}
])
MockedProvider
<MockedProvider
mocks={[
{
request: { query, variables },
- newData: (variables) => ({ data: { greeting: "Hello " + variables.greeting } }),
+ result: (variables) => ({ data: { greeting: "Hello " + variables.greeting } }),
+ maxUsageCount: Number.POSITIVE_INFINITY,
}
]}
/>
c3fceda
Thanks @phryneas! - A call to ObservableQuery.setVariables
with different variables or a ObservableQuery.refetch
call will always now guarantee that a value will be emitted from the observable, even if it is deep equal to the previous value.
a70fac6
Thanks @phryneas! - ApolloClient.stop()
now cleans up more agressively to prevent memory leaks:
ObservableQuery
instances by emitting a completed
event.
"QueryManager stopped while query was in flight"
.
e2a0be8
Thanks @jerelmiller! - The forward
function passed to the request handler is now always provided to request
and no longer optional. If you create custom links by subclassing ApolloLink
, the forward
function no longer needs to be optional:
class CustomLink extends ApolloLink {
request(
operation: ApolloLink.Operation,
// This no longer needs to be typed as optional
forward: ApolloLink.ForwardFunction
) {
// ...
}
}
ApolloLink
no longer detects terminating links by checking function arity on the request handler. This means using methods such as concat
on a terminating link no longer emit a warning. On the flip side, if the terminating link calls the forward
function, a warning is emitted and an observable that immediately completes is returned which will result in an error from Apollo Client.
15f5a1c
Thanks @jerelmiller! - Require the link
option when instantiating ApolloClient
. This removes the uri
, credentials
and headers
options from ApolloClient
in favor of passing an instantiated HttpLink
directly. To migrate:
uri
, credentials
, or headers
options
new ApolloClient({
// ...
- uri,
- credentials,
- headers,
+ link: new HttpLink({ uri, credentials, headers }),
// or if you prefer the function call approach:
+ link: createHttpLink({ uri, credentials, headers }),
});
link
option
new ApolloClient({
// ...
+ link: ApolloLink.empty()
});
86469a2
Thanks @jerelmiller! - ### Changes for users of InMemoryCache
cache.diff
no longer throws when returnPartialData
is set to false
without a complete result. Instead, cache.diff
will return null
when it is unable to read a full cache result.
cache.diff
directly with returnPartialData: false
, remove the try
/catch
block and replace with a check for null
.
Changes for third-party cache implementations
cache.diff
to return null
instead of throwing when the cache returns an incomplete result and returnPartialData
is false
. The internal try
/catch
blocks have been removed around cache.diff
. If your cache implementation throws for incomplete results, please update this to return null
.
77e1b13
Thanks @jerelmiller! - Default the TData
generic type to unknown
in all APIs that use a TData
generic argument such as useQuery
, client.query
, etc.
90bf0e6
Thanks @jerelmiller! - client.query
no longer supports a fetchPolicy
of standby
. standby
does not fetch and did not return data
. standby
is meant for watched queries where fetching should be on hold.
c2736db
Thanks @jerelmiller! - Remove the deprecated Query
, Mutation
, and Subscription
components. Use the provided React hooks instead.
Minor changes
For a comprehensive look at all the minor changes, see the changelog.
Patch changes
For a comprehensive look at all the patch changes, see the changelog.