@apollo/client
Version:
A fully-featured caching GraphQL client.
1,054 lines (790 loc) • 776 kB
Markdown
# @apollo/client
## 4.3.0-alpha.1
### Patch Changes
- [#13268](https://github.com/apollographql/apollo-client/pull/13268) [`419e2b5`](https://github.com/apollographql/apollo-client/commit/419e2b5bfe573d1eb4c3a0ff7aa9084e6aaa2f37) Thanks [@DaleSeo](https://github.com/DaleSeo)! - Align the remaining cache generic constraints with `Cache.Implementation`. The deprecated React mutation types (`MutationHookOptions`, `MutationFunctionOptions`, `MutationTuple`) and the internal `InternalRefetchQueriesOptions` and `QueryInfo` types still constrained their cache type parameter to `ApolloCache`, so they now match the rest of the overridable cache API.
## 4.3.0-alpha.0
### Minor Changes
- [#13250](https://github.com/apollographql/apollo-client/pull/13250) [`bad7035`](https://github.com/apollographql/apollo-client/commit/bad7035565e15c18800080d9e0abf1d89b3d82fa) Thanks [@jerelmiller](https://github.com/jerelmiller)! - Add the ability to define the cache type for the client. `client.cache` currently returns `ApolloCache` as the cache type regardless of what cache you've provided to `ApolloClient`.
Declare the cache type using the `cache` property in the `TypeOverrides` interface to set the cache implementation used for the client.
```ts
// apollo.d.ts
import type { InMemoryCache } from "@apollo/client";
declare module "@apollo/client" {
export interface TypeOverrides {
cache: InMemoryCache;
}
}
```
Now anywhere `cache` is accessible, the type is the declared cache type:
```ts
client.cache;
// ^? InMemoryCache
client.mutate({
update: (cache) => {
// ^? InMemoryCache
},
});
```
> [!NOTE]
> Setting a cache type enforces that cache type in the `cache` option for the `ApolloClient` constructor.
## 4.2.3
### Patch Changes
- [#13254](https://github.com/apollographql/apollo-client/pull/13254) [`66e9dfc`](https://github.com/apollographql/apollo-client/commit/66e9dfcf7964345dac949ab4c6004460d224d1cf) Thanks [@jerelmiller](https://github.com/jerelmiller)! - Add support for `graphql` v17 as a valid peer dependency.
## 4.2.2
### Patch Changes
- [#13184](https://github.com/apollographql/apollo-client/pull/13184) [`c207b88`](https://github.com/apollographql/apollo-client/commit/c207b886026114943dc7f5c85e997a1938e74cfe) Thanks [@audrius-savickas](https://github.com/audrius-savickas)! - Preserve referential equality of masked data on refetch when the result is deeply equal to the previous result.
## 4.2.1
### Patch Changes
- [#13248](https://github.com/apollographql/apollo-client/pull/13248) [`062ffe3`](https://github.com/apollographql/apollo-client/commit/062ffe3ecfeda8630a4ad3ced40ce58193199b74) Thanks [@jerelmiller](https://github.com/jerelmiller)! - Fixes an issue where `useLazyQuery` would not apply a changed `pollInterval` between renders.
## 4.2.0
### Minor Changes
- [#13132](https://github.com/apollographql/apollo-client/pull/13132) [`f3ce805`](https://github.com/apollographql/apollo-client/commit/f3ce805425d10a9666218a8e109288a2d46dcab1) Thanks [@phryneas](https://github.com/phryneas)! - Introduce "classic" and "modern" method and hook signatures.
Apollo Client 4.2 introduces two signature styles for methods and hooks. All signatures previously present are now "classic" signatures, and a new set of "modern" signatures are added alongside them.
**Classic signatures** are the default and are identical to the signatures before Apollo Client 4.2, preserving backward compatibility. Classic signatures still work with manually specified TypeScript generics (e.g., `useSuspenseQuery<MyData>(...)`). However, manually specifying generics has been discouraged for a long time—instead, we recommend using `TypedDocumentNode` to automatically infer types, which provides more accurate results without any manual annotations.
**Modern signatures** automatically incorporate your declared `defaultOptions` into return types, providing more accurate types. Modern signatures infer types from the document node and do not support manually passing generic type arguments; TypeScript will produce a type error if you attempt to do so.
Methods and hooks automatically switch to modern signatures the moment any non-optional property is declared in `DeclareDefaultOptions`. The switch happens across all methods and hooks globally:
```ts
// apollo.d.ts
import "@apollo/client";
declare module "@apollo/client" {
namespace ApolloClient {
namespace DeclareDefaultOptions {
interface WatchQuery {
errorPolicy: "all"; // non-optional → modern signatures activated automatically
}
}
}
}
```
Users can also manually switch to modern signatures without declaring any `defaultOptions`, for example when wanting accurate type inference without relying on global `defaultOptions`:
```ts
// apollo.d.ts
import "@apollo/client";
declare module "@apollo/client" {
export interface TypeOverrides {
signatureStyle: "modern";
}
}
```
Users can do a global `DeclareDefaultOptions` type augmentation and then manually switch back to "classic" for migration purposes:
```ts
// apollo.d.ts
import "@apollo/client";
declare module "@apollo/client" {
export interface TypeOverrides {
signatureStyle: "classic";
}
}
```
Note that this is **not recommended for long-term use**. When combined with `DeclareDefaultOptions`, switching back to classic results in the same incorrect types as before Apollo Client 4.2—methods and hooks will not reflect the `defaultOptions` you've declared.
- [#13130](https://github.com/apollographql/apollo-client/pull/13130) [`dd12231`](https://github.com/apollographql/apollo-client/commit/dd122316028b55307de4a40335512307c8fa916a) Thanks [@jerelmiller](https://github.com/jerelmiller)! - Improve the accuracy of `client.query` return type to better detect the current `errorPolicy`. The `data` property is no longer nullable when the `errorPolicy` is `none`. This makes it possible to remove the `undefined` checks or optional chaining in most cases.
- [#13210](https://github.com/apollographql/apollo-client/pull/13210) [`1f9a428`](https://github.com/apollographql/apollo-client/commit/1f9a4287eb1eeef2cc08c81c92961f1cecd0dbca) Thanks [@jerelmiller](https://github.com/jerelmiller)! - Add support for automatic event-based refetching, such as window focus.
The `RefetchEventManager` class handles automatic refetches in response to events. Apollo Client provides built-in sources for window focus and network reconnect as `windowFocusSource` and `onlineSource`.
Event refetching is fully opt-in. Create and pass a `RefetchEventManager` instance to the `ApolloClient` constructor to activate the event listeners.
```ts
import {
ApolloClient,
InMemoryCache,
RefetchEventManager,
windowFocusSource,
onlineSource,
} from "@apollo/client";
const client = new ApolloClient({
link,
cache: new InMemoryCache(),
refetchEventManager: new RefetchEventManager({
sources: {
// Refetch when window is focused
windowFocus: windowFocusSource,
// Refetch when the user comes back online
online: onlineSource,
},
}),
});
```
By default, all active queries refetch when the events fire. Queries can opt out per-event or disable all event refetches:
```ts
// Skip refetch on window focus for this query, but keep `online`
useQuery(QUERY, {
refetchOn: { windowFocus: false },
});
// Disable all event-driven refetches for this query
useQuery(OTHER_QUERY, {
refetchOn: false,
});
// Enable every event for this query, regardless of defaultOptions
useQuery(LIVE_DASHBOARD, {
refetchOn: true,
});
// Dynamically enable or disable a refetch when the event fires
useQuery(LIVE_DASHBOARD, {
refetchOn: ({ source, payload }) => {
if (source === "windowFocus") {
// payload is the data associated with the event
return someCondition(payload);
}
return true;
},
});
// Dynamically enable or disable a refetch for a specific event
useQuery(LIVE_DASHBOARD, {
refetchOn: {
windowFocus: ({ payload }) => {
// payload is the data associated with the event
return someCondition(payload);
},
},
});
```
To enable per-query opt-in rather than opt-out, set `defaultOptions.watchQuery.refetchOn` to `false` and enable it per-query instead.
```ts
const client = new ApolloClient({
link,
cache,
refetchEventManager: new RefetchEventManager({
sources: { windowFocus: windowFocusSource },
}),
defaultOptions: {
watchQuery: { refetchOn: false },
},
});
// Only this query refetches on window focus
useQuery(DASHBOARD_QUERY, { refetchOn: { windowFocus: true } });
```
When `defaultOptions.watchQuery.refetchOn` and per-query `refetchOn` options are provided, the objects are merged together.
### Custom events
You can also add your own custom events that trigger refetches. Register your event name and payload type using TypeScript module augmentation, then provide a source function that returns an Observable. The source's emitted value becomes the event's `payload`.
```ts
import { Observable } from "@apollo/client";
import { filter } from "rxjs";
import { AppState, AppStateStatus, Platform } from "react-native";
declare module "@apollo/client" {
interface RefetchEvents {
reactNativeAppStatus: AppStateStatus;
}
}
const refetchEventManager = new RefetchEventManager({
sources: {
reactNativeAppStatus: () => {
return new Observable((observer) => {
const subscription = AppState.addEventListener("change", (status) => {
observer.next(status);
});
return () => subscription.remove();
}).pipe(
filter((status) => Platform.OS !== "web" && status === "active")
);
},
},
});
// Disable per-query by setting the event to false
useQuery(QUERY, { refetchOn: { reactNativeAppStatus: false } });
```
### Manually trigger an event refetch
Refetches can be triggered imperatively by calling `emit` with the event name and its payload (if any).
```ts
refetchEventManager.emit("reactNativeAppStatus", "active");
```
#### Sourceless events
A source that has no automatic detection logic but still wants imperative `emit` support can be declared as `true`. Type the event as `void` to omit the payload argument.
```ts
declare module "@apollo/client" {
interface RefetchEvents {
userTriggered: void;
}
}
const refetchEventManager = new RefetchEventManager({
sources: { userTriggered: true },
});
refetchEventManager.emit("userTriggered");
```
Note: Calling `emit` on an event without a registered source will log a warning and result in a no-op.
### Custom handlers
When an event fires, the default handler calls `client.refetchQueries({ include: "active" })` filtered by each query's `refetchOn` setting. You can override the handler for an event to add your own custom filtering. For example, to refetch all queries, including `standby` queries, define a handler for the event:
```ts
const refetchEventManager = new RefetchEventManager({
// ...
handlers: {
userTriggered: ({ client, source, payload, matchesRefetchOn }) => {
return client.refetchQueries({
include: "all",
onQueryUpdated: (observableQuery) => {
return matchesRefetchOn(observableQuery);
},
});
},
},
});
```
Handlers must return either a `RefetchQueriesResult` or `void`. Returning `void` skips refetching for the event.
- [#13232](https://github.com/apollographql/apollo-client/pull/13232) [`f1b541f`](https://github.com/apollographql/apollo-client/commit/f1b541fed4111028b6842727178288156582e669) Thanks [@jerelmiller](https://github.com/jerelmiller)! - Version bump to `rc`.
- [#13206](https://github.com/apollographql/apollo-client/pull/13206) [`08fccab`](https://github.com/apollographql/apollo-client/commit/08fccab68822e99c6edd539cb4162d1a3df4f4c9) Thanks [@jerelmiller](https://github.com/jerelmiller)! - Extend the `defaultOptions` type-safety work to `client.mutate` and `useMutation`.
The `errorPolicy` option now flows through to the result types for mutations in the same way it already does for queries:
- `ApolloClient.MutateResult<TData, TErrorPolicy>` maps `errorPolicy` to the concrete shape of `data` and `error`:
- `"none"` → `{ data: TData; error?: never }`
- `"all"` → `{ data: TData | undefined; error?: ErrorLike }`
- `"ignore"` → `{ data: TData | undefined; error?: never }`
- `client.mutate` and `useMutation` pick up the declared `defaultOptions.mutate.errorPolicy` and the explicit `errorPolicy` on each call to narrow return types accordingly.
- `useMutation.Result.error` is narrowed to `undefined` when `errorPolicy` is `"ignore"`, since `client.mutate` never resolves with an error in that case.
`DeclareDefaultOptions.Mutate` already accepted `errorPolicy`; the new behavior is that once you declare it, hook and method return types reflect it:
```ts
// apollo.d.ts
import "@apollo/client";
declare module "@apollo/client" {
namespace ApolloClient {
namespace DeclareDefaultOptions {
interface Mutate {
errorPolicy: "all";
}
}
}
}
```
```ts
const result = await client.mutate({ mutation: MUTATION });
result.data;
// ^? TData | undefined
result.error;
// ^? ErrorLike | undefined
```
Setting `errorPolicy` on an individual call overrides the default for that call's return type.
- [#13222](https://github.com/apollographql/apollo-client/pull/13222) [`b93c172`](https://github.com/apollographql/apollo-client/commit/b93c1723b4b7a9d1296ddd57035bc4fe39c8d971) Thanks [@jerelmiller](https://github.com/jerelmiller)! - Extend the `defaultOptions` type-safety work to `preloadQuery` (returned from `createQueryPreloader`). Defaults declared in `DeclareDefaultOptions.WatchQuery` now work with `preloadQuery` to ensure the `PreloadedQueryRef`'s data states are correctly set.
```ts
// apollo.d.ts
import "@apollo/client";
declare module "@apollo/client" {
namespace ApolloClient {
namespace DeclareDefaultOptions {
interface WatchQuery {
errorPolicy: "all";
}
}
}
}
```
```ts
const preloadQuery = createQueryPreloader(client);
const queryRef = preloadQuery(QUERY);
// ^? PreloadedQueryRef<TData, TVariables, "complete" | "streaming" | "empty">
```
- [#13132](https://github.com/apollographql/apollo-client/pull/13132) [`f3ce805`](https://github.com/apollographql/apollo-client/commit/f3ce805425d10a9666218a8e109288a2d46dcab1) Thanks [@phryneas](https://github.com/phryneas)! - Synchronize method and hook return types with `defaultOptions`.
Prior to this change, the following code snippet would always apply:
```ts
declare const MY_QUERY: TypedDocumentNode<TData, TVariables>;
const result1 = useSuspenseQuery(MY_QUERY);
result1.data;
// ^? TData
const result2 = useSuspenseQuery(MY_QUERY, { errorPolicy: "all" });
result2.data;
// ^? TData | undefined
```
While these types are generally correct, if you were to set `errorPolicy: 'all'` as a default option, the type of `result.data` for the first query would remain `TData` instead of changing to `TData | undefined` to match the runtime behavior.
We are now enforcing that certain `defaultOptions` types need to be registered globally. This means that if you want to use `errorPolicy: 'all'` as a default option for a query, you will need to register its type like this:
```ts
// apollo.d.ts
import "@apollo/client";
declare module "@apollo/client" {
namespace ApolloClient {
namespace DeclareDefaultOptions {
interface WatchQuery {
// possible global-registered values:
// * `errorPolicy`
// * `returnPartialData`
errorPolicy: "all";
}
interface Query {
// possible global-registered values:
// * `errorPolicy`
}
interface Mutate {
// possible global-registered values:
// * `errorPolicy`
}
}
}
}
```
Once this type declaration is in place, the type of `result.data` in the above example will correctly be changed to `TData | undefined`, reflecting the possibility that if an error occurs, `data` might be `undefined`. Manually specifying `useSuspenseQuery(MY_QUERY, { errorPolicy: "none" });` changes `result.data` to `TData` to reflect the local override.
This change means that you will need to declare your default options types in order to use `defaultOptions` with `ApolloClient`, otherwise you will see a TypeScript error.
Without the type declaration, the following (previously valid) code will now error:
```ts
new ApolloClient({
link: ApolloLink.empty(),
cache: new InMemoryCache(),
defaultOptions: {
watchQuery: {
// results in a type error:
// Type '"all"' is not assignable to type '"A default option for watchQuery.errorPolicy must be declared in ApolloClient.DeclareDefaultOptions before usage. See https://www.apollographql.com/docs/react/data/typescript#declaring-default-options-for-type-safety."'.
errorPolicy: "all",
},
},
});
```
If you are creating multiple instances of Apollo Client with conflicting default options and you cannot register a single `defaultOptions` value as a result, you can relax the constraints by declaring those options as union types covering all values used by all clients. The properties can be required (to enforce them in `defaultOptions`) or optional (if some constructor calls won't pass a value):
```ts
// apollo.d.ts
import "@apollo/client";
declare module "@apollo/client" {
export namespace ApolloClient {
export namespace DeclareDefaultOptions {
interface WatchQuery {
errorPolicy?: "none" | "all" | "ignore";
returnPartialData?: boolean;
}
interface Query {
errorPolicy?: "none" | "all" | "ignore";
}
interface Mutate {
errorPolicy?: "none" | "all" | "ignore";
}
}
}
}
```
With this declaration, the `ApolloClient` constructor accepts any of those values in `defaultOptions`. The tradeoff is that hook and method return types become more generic. For example, calling `useSuspenseQuery` without an explicit `errorPolicy` will return a result typed as if all error policies are possible, since TypeScript can't know which specific value your instance uses at runtime.
Note that making a property optional (`errorPolicy?:`) is equivalent to adding the TypeScript default value (`"none"`) to the union. So `errorPolicy?: "all" | "ignore"` has the same effect on return types as `errorPolicy: "none" | "all" | "ignore"`, because TypeScript assumes the option could also be absent (i.e., `"none"`).
You can also use a **partial union** that only lists the values you actually use. For example, if you only ever use `"all"` or `"ignore"`, declare `errorPolicy: "all" | "ignore"` (required) to keep the union narrow and avoid unused values broadening your signatures unnecessarily.
### Patch Changes
- [#13217](https://github.com/apollographql/apollo-client/pull/13217) [`790f987`](https://github.com/apollographql/apollo-client/commit/790f987ed65435159dd2c6df5fe2fa01587a179e) Thanks [@jerelmiller](https://github.com/jerelmiller)! - Fix the deprecation for the classic signatures for function overloads that rely on type inference from a `TypedDocumentNode`. The deprecation now only applies to classic signatures that provide explicit type arguments to encourage the use of `TypedDocumentNode`.
- [#13166](https://github.com/apollographql/apollo-client/pull/13166) [`0537d97`](https://github.com/apollographql/apollo-client/commit/0537d97161a51479141a182d869458912e1b8e1d) Thanks [@jerelmiller](https://github.com/jerelmiller)! - Release changes in 4.1.5 and 4.1.6.
- [#13215](https://github.com/apollographql/apollo-client/pull/13215) [`54c9eb7`](https://github.com/apollographql/apollo-client/commit/54c9eb7f95d3cd12dc5d12ec27090f1f23b0c471) Thanks [@jerelmiller](https://github.com/jerelmiller)! - Ensure the options object for the `useQuery`, `useSuspenseQuery`, and `useBackgroundQuery` hooks provide proper IntelliSense suggestions.
- [#13229](https://github.com/apollographql/apollo-client/pull/13229) [`9a7f65a`](https://github.com/apollographql/apollo-client/commit/9a7f65a0059433c83307ef2d8117dac67947d791) Thanks [@jerelmiller](https://github.com/jerelmiller)! - Fix `refetchOn` merging when `defaultOptions.watchQuery.refetchOn` is set to a non-object value (`false`, `true`, or a function) and the per-query `refetchOn` is an object. Previously the per-query object completely replaced the default so unspecified events fell back to "enabled" regardless of the default.
The `defaultOptions` value now applies to any event the per-query object does not explicitly configure:
- `false` - unspecified events stay disabled
- `true` - unspecified events refetch
- Callback function - the function is called for unspecified events to determine whether to refetch
```ts
const client = new ApolloClient({
// ...
defaultOptions: {
watchQuery: {
refetchOn: false,
},
},
});
// Only `windowFocus` refetches. Other events stay disabled per the default.
useQuery(QUERY, { refetchOn: { windowFocus: true } });
```
- [#13230](https://github.com/apollographql/apollo-client/pull/13230) [`b25b659`](https://github.com/apollographql/apollo-client/commit/b25b6593f5d968db505b127e7ff7f2bb2419d5ee) Thanks [@jerelmiller](https://github.com/jerelmiller)! - Add the ability to override the default event handler on `RefetchEventManager`. The default handler runs when no per-source handler is configured for an event. Provide a custom handler via the `defaultHandler` constructor option or the `setDefaultEventHandler` instance method.
```ts
new RefetchEventManager({
defaultHandler: ({ client, matchesRefetchOn }) => {
return client.refetchQueries({
include: "all",
onQueryUpdated: matchesRefetchOn,
});
},
});
```
## 4.2.0-rc.0
### Minor Changes
- [#13232](https://github.com/apollographql/apollo-client/pull/13232) [`f1b541f`](https://github.com/apollographql/apollo-client/commit/f1b541fed4111028b6842727178288156582e669) Thanks [@jerelmiller](https://github.com/jerelmiller)! - Version bump to `rc`.
## 4.2.0-alpha.8
### Patch Changes
- [#13229](https://github.com/apollographql/apollo-client/pull/13229) [`9a7f65a`](https://github.com/apollographql/apollo-client/commit/9a7f65a0059433c83307ef2d8117dac67947d791) Thanks [@jerelmiller](https://github.com/jerelmiller)! - Fix `refetchOn` merging when `defaultOptions.watchQuery.refetchOn` is set to a non-object value (`false`, `true`, or a function) and the per-query `refetchOn` is an object. Previously the per-query object completely replaced the default so unspecified events fell back to "enabled" regardless of the default.
The `defaultOptions` value now applies to any event the per-query object does not explicitly configure:
- `false` - unspecified events stay disabled
- `true` - unspecified events refetch
- Callback function - the function is called for unspecified events to determine whether to refetch
```ts
const client = new ApolloClient({
// ...
defaultOptions: {
watchQuery: {
refetchOn: false,
},
},
});
// Only `windowFocus` refetches. Other events stay disabled per the default.
useQuery(QUERY, { refetchOn: { windowFocus: true } });
```
- [#13230](https://github.com/apollographql/apollo-client/pull/13230) [`b25b659`](https://github.com/apollographql/apollo-client/commit/b25b6593f5d968db505b127e7ff7f2bb2419d5ee) Thanks [@jerelmiller](https://github.com/jerelmiller)! - Add the ability to override the default event handler on `RefetchEventManager`. The default handler runs when no per-source handler is configured for an event. Provide a custom handler via the `defaultHandler` constructor option or the `setDefaultEventHandler` instance method.
```ts
new RefetchEventManager({
defaultHandler: ({ client, matchesRefetchOn }) => {
return client.refetchQueries({
include: "all",
onQueryUpdated: matchesRefetchOn,
});
},
});
```
## 4.2.0-alpha.7
### Minor Changes
- [#13222](https://github.com/apollographql/apollo-client/pull/13222) [`b93c172`](https://github.com/apollographql/apollo-client/commit/b93c1723b4b7a9d1296ddd57035bc4fe39c8d971) Thanks [@jerelmiller](https://github.com/jerelmiller)! - Extend the `defaultOptions` type-safety work to `preloadQuery` (returned from `createQueryPreloader`). Defaults declared in `DeclareDefaultOptions.WatchQuery` now work with `preloadQuery` to ensure the `PreloadedQueryRef`'s data states are correctly set.
```ts
// apollo.d.ts
import "@apollo/client";
declare module "@apollo/client" {
namespace ApolloClient {
namespace DeclareDefaultOptions {
interface WatchQuery {
errorPolicy: "all";
}
}
}
}
```
```ts
const preloadQuery = createQueryPreloader(client);
const queryRef = preloadQuery(QUERY);
// ^? PreloadedQueryRef<TData, TVariables, "complete" | "streaming" | "empty">
```
## 4.2.0-alpha.6
### Patch Changes
- [#13217](https://github.com/apollographql/apollo-client/pull/13217) [`790f987`](https://github.com/apollographql/apollo-client/commit/790f987ed65435159dd2c6df5fe2fa01587a179e) Thanks [@jerelmiller](https://github.com/jerelmiller)! - Fix the deprecation for the classic signatures for function overloads that rely on type inference from a `TypedDocumentNode`. The deprecation now only applies to classic signatures that provide explicit type arguments to encourage the use of `TypedDocumentNode`.
## 4.2.0-alpha.5
### Patch Changes
- [#13215](https://github.com/apollographql/apollo-client/pull/13215) [`54c9eb7`](https://github.com/apollographql/apollo-client/commit/54c9eb7f95d3cd12dc5d12ec27090f1f23b0c471) Thanks [@jerelmiller](https://github.com/jerelmiller)! - Ensure the options object for the `useQuery`, `useSuspenseQuery`, and `useBackgroundQuery` hooks provide proper IntelliSense suggestions.
## 4.2.0-alpha.4
### Minor Changes
- [#13210](https://github.com/apollographql/apollo-client/pull/13210) [`1f9a428`](https://github.com/apollographql/apollo-client/commit/1f9a4287eb1eeef2cc08c81c92961f1cecd0dbca) Thanks [@jerelmiller](https://github.com/jerelmiller)! - Add support for automatic event-based refetching, such as window focus.
The `RefetchEventManager` class handles automatic refetches in response to events. Apollo Client provides built-in sources for window focus and network reconnect as `windowFocusSource` and `onlineSource`.
Event refetching is fully opt-in. Create and pass a `RefetchEventManager` instance to the `ApolloClient` constructor to activate the event listeners.
```ts
import {
ApolloClient,
InMemoryCache,
RefetchEventManager,
windowFocusSource,
onlineSource,
} from "@apollo/client";
const client = new ApolloClient({
link,
cache: new InMemoryCache(),
refetchEventManager: new RefetchEventManager({
sources: {
// Refetch when window is focused
windowFocus: windowFocusSource,
// Refetch when the user comes back online
online: onlineSource,
},
}),
});
```
By default, all active queries refetch when the events fire. Queries can opt out per-event or disable all event refetches:
```ts
// Skip refetch on window focus for this query, but keep `online`
useQuery(QUERY, {
refetchOn: { windowFocus: false },
});
// Disable all event-driven refetches for this query
useQuery(OTHER_QUERY, {
refetchOn: false,
});
// Enable every event for this query, regardless of defaultOptions
useQuery(LIVE_DASHBOARD, {
refetchOn: true,
});
// Dynamically enable or disable a refetch when the event fires
useQuery(LIVE_DASHBOARD, {
refetchOn: ({ source, payload }) => {
if (source === "windowFocus") {
// payload is the data associated with the event
return someCondition(payload);
}
return true;
},
});
// Dynamically enable or disable a refetch for a specific event
useQuery(LIVE_DASHBOARD, {
refetchOn: {
windowFocus: ({ payload }) => {
// payload is the data associated with the event
return someCondition(payload);
},
},
});
```
To enable per-query opt-in rather than opt-out, set `defaultOptions.watchQuery.refetchOn` to `false` and enable it per-query instead.
```ts
const client = new ApolloClient({
link,
cache,
refetchEventManager: new RefetchEventManager({
sources: { windowFocus: windowFocusSource },
}),
defaultOptions: {
watchQuery: { refetchOn: false },
},
});
// Only this query refetches on window focus
useQuery(DASHBOARD_QUERY, { refetchOn: { windowFocus: true } });
```
When `defaultOptions.watchQuery.refetchOn` and per-query `refetchOn` options are provided, the objects are merged together.
### Custom events
You can also add your own custom events that trigger refetches. Register your event name and payload type using TypeScript module augmentation, then provide a source function that returns an Observable. The source's emitted value becomes the event's `payload`.
```ts
import { Observable } from "@apollo/client";
import { filter } from "rxjs";
import { AppState, AppStateStatus, Platform } from "react-native";
declare module "@apollo/client" {
interface RefetchEvents {
reactNativeAppStatus: AppStateStatus;
}
}
const refetchEventManager = new RefetchEventManager({
sources: {
reactNativeAppStatus: () => {
return new Observable((observer) => {
const subscription = AppState.addEventListener("change", (status) => {
observer.next(status);
});
return () => subscription.remove();
}).pipe(
filter((status) => Platform.OS !== "web" && status === "active")
);
},
},
});
// Disable per-query by setting the event to false
useQuery(QUERY, { refetchOn: { reactNativeAppStatus: false } });
```
### Manually trigger an event refetch
Refetches can be triggered imperatively by calling `emit` with the event name and its payload (if any).
```ts
refetchEventManager.emit("reactNativeAppStatus", "active");
```
#### Sourceless events
A source that has no automatic detection logic but still wants imperative `emit` support can be declared as `true`. Type the event as `void` to omit the payload argument.
```ts
declare module "@apollo/client" {
interface RefetchEvents {
userTriggered: void;
}
}
const refetchEventManager = new RefetchEventManager({
sources: { userTriggered: true },
});
refetchEventManager.emit("userTriggered");
```
Note: Calling `emit` on an event without a registered source will log a warning and result in a no-op.
### Custom handlers
When an event fires, the default handler calls `client.refetchQueries({ include: "active" })` filtered by each query's `refetchOn` setting. You can override the handler for an event to add your own custom filtering. For example, to refetch all queries, including `standby` queries, define a handler for the event:
```ts
const refetchEventManager = new RefetchEventManager({
// ...
handlers: {
userTriggered: ({ client, source, payload, matchesRefetchOn }) => {
return client.refetchQueries({
include: "all",
onQueryUpdated: (observableQuery) => {
return matchesRefetchOn(observableQuery);
},
});
},
},
});
```
Handlers must return either a `RefetchQueriesResult` or `void`. Returning `void` skips refetching for the event.
## 4.2.0-alpha.3
### Minor Changes
- [#13206](https://github.com/apollographql/apollo-client/pull/13206) [`08fccab`](https://github.com/apollographql/apollo-client/commit/08fccab68822e99c6edd539cb4162d1a3df4f4c9) Thanks [@jerelmiller](https://github.com/jerelmiller)! - Extend the `defaultOptions` type-safety work to `client.mutate` and `useMutation`.
The `errorPolicy` option now flows through to the result types for mutations in the same way it already does for queries:
- `ApolloClient.MutateResult<TData, TErrorPolicy>` maps `errorPolicy` to the concrete shape of `data` and `error`:
- `"none"` → `{ data: TData; error?: never }`
- `"all"` → `{ data: TData | undefined; error?: ErrorLike }`
- `"ignore"` → `{ data: TData | undefined; error?: never }`
- `client.mutate` and `useMutation` pick up the declared `defaultOptions.mutate.errorPolicy` and the explicit `errorPolicy` on each call to narrow return types accordingly.
- `useMutation.Result.error` is narrowed to `undefined` when `errorPolicy` is `"ignore"`, since `client.mutate` never resolves with an error in that case.
`DeclareDefaultOptions.Mutate` already accepted `errorPolicy`; the new behavior is that once you declare it, hook and method return types reflect it:
```ts
// apollo.d.ts
import "@apollo/client";
declare module "@apollo/client" {
namespace ApolloClient {
namespace DeclareDefaultOptions {
interface Mutate {
errorPolicy: "all";
}
}
}
}
```
```ts
const result = await client.mutate({ mutation: MUTATION });
result.data;
// ^? TData | undefined
result.error;
// ^? ErrorLike | undefined
```
Setting `errorPolicy` on an individual call overrides the default for that call's return type.
## 4.2.0-alpha.2
### Minor Changes
- [#13132](https://github.com/apollographql/apollo-client/pull/13132) [`f3ce805`](https://github.com/apollographql/apollo-client/commit/f3ce805425d10a9666218a8e109288a2d46dcab1) Thanks [@phryneas](https://github.com/phryneas)! - Introduce "classic" and "modern" method and hook signatures.
Apollo Client 4.2 introduces two signature styles for methods and hooks. All signatures previously present are now "classic" signatures, and a new set of "modern" signatures are added alongside them.
**Classic signatures** are the default and are identical to the signatures before Apollo Client 4.2, preserving backward compatibility. Classic signatures still work with manually specified TypeScript generics (e.g., `useSuspenseQuery<MyData>(...)`). However, manually specifying generics has been discouraged for a long time—instead, we recommend using `TypedDocumentNode` to automatically infer types, which provides more accurate results without any manual annotations.
**Modern signatures** automatically incorporate your declared `defaultOptions` into return types, providing more accurate types. Modern signatures infer types from the document node and do not support manually passing generic type arguments; TypeScript will produce a type error if you attempt to do so.
Methods and hooks automatically switch to modern signatures the moment any non-optional property is declared in `DeclareDefaultOptions`. The switch happens across all methods and hooks globally:
```ts
// apollo.d.ts
import "@apollo/client";
declare module "@apollo/client" {
namespace ApolloClient {
namespace DeclareDefaultOptions {
interface WatchQuery {
errorPolicy: "all"; // non-optional → modern signatures activated automatically
}
}
}
}
```
Users can also manually switch to modern signatures without declaring any `defaultOptions`, for example when wanting accurate type inference without relying on global `defaultOptions`:
```ts
// apollo.d.ts
import "@apollo/client";
declare module "@apollo/client" {
export interface TypeOverrides {
signatureStyle: "modern";
}
}
```
Users can do a global `DeclareDefaultOptions` type augmentation and then manually switch back to "classic" for migration purposes:
```ts
// apollo.d.ts
import "@apollo/client";
declare module "@apollo/client" {
export interface TypeOverrides {
signatureStyle: "classic";
}
}
```
Note that this is **not recommended for long-term use**. When combined with `DeclareDefaultOptions`, switching back to classic results in the same incorrect types as before Apollo Client 4.2—methods and hooks will not reflect the `defaultOptions` you've declared.
- [#13132](https://github.com/apollographql/apollo-client/pull/13132) [`f3ce805`](https://github.com/apollographql/apollo-client/commit/f3ce805425d10a9666218a8e109288a2d46dcab1) Thanks [@phryneas](https://github.com/phryneas)! - Synchronize method and hook return types with `defaultOptions`.
Prior to this change, the following code snippet would always apply:
```ts
declare const MY_QUERY: TypedDocumentNode<TData, TVariables>;
const result1 = useSuspenseQuery(MY_QUERY);
result1.data;
// ^? TData
const result2 = useSuspenseQuery(MY_QUERY, { errorPolicy: "all" });
result2.data;
// ^? TData | undefined
```
While these types are generally correct, if you were to set `errorPolicy: 'all'` as a default option, the type of `result.data` for the first query would remain `TData` instead of changing to `TData | undefined` to match the runtime behavior.
We are now enforcing that certain `defaultOptions` types need to be registered globally. This means that if you want to use `errorPolicy: 'all'` as a default option for a query, you will need to register its type like this:
```ts
// apollo.d.ts
import "@apollo/client";
declare module "@apollo/client" {
namespace ApolloClient {
namespace DeclareDefaultOptions {
interface WatchQuery {
// possible global-registered values:
// * `errorPolicy`
// * `returnPartialData`
errorPolicy: "all";
}
interface Query {
// possible global-registered values:
// * `errorPolicy`
}
interface Mutate {
// possible global-registered values:
// * `errorPolicy`
}
}
}
}
```
Once this type declaration is in place, the type of `result.data` in the above example will correctly be changed to `TData | undefined`, reflecting the possibility that if an error occurs, `data` might be `undefined`. Manually specifying `useSuspenseQuery(MY_QUERY, { errorPolicy: "none" });` changes `result.data` to `TData` to reflect the local override.
This change means that you will need to declare your default options types in order to use `defaultOptions` with `ApolloClient`, otherwise you will see a TypeScript error.
Without the type declaration, the following (previously valid) code will now error:
```ts
new ApolloClient({
link: ApolloLink.empty(),
cache: new InMemoryCache(),
defaultOptions: {
watchQuery: {
// results in a type error:
// Type '"all"' is not assignable to type '"A default option for watchQuery.errorPolicy must be declared in ApolloClient.DeclareDefaultOptions before usage. See https://www.apollographql.com/docs/react/data/typescript#declaring-default-options-for-type-safety."'.
errorPolicy: "all",
},
},
});
```
If you are creating multiple instances of Apollo Client with conflicting default options and you cannot register a single `defaultOptions` value as a result, you can relax the constraints by declaring those options as union types covering all values used by all clients. The properties can be required (to enforce them in `defaultOptions`) or optional (if some constructor calls won't pass a value):
```ts
// apollo.d.ts
import "@apollo/client";
declare module "@apollo/client" {
export namespace ApolloClient {
export namespace DeclareDefaultOptions {
interface WatchQuery {
errorPolicy?: "none" | "all" | "ignore";
returnPartialData?: boolean;
}
interface Query {
errorPolicy?: "none" | "all" | "ignore";
}
interface Mutate {
errorPolicy?: "none" | "all" | "ignore";
}
}
}
}
```
With this declaration, the `ApolloClient` constructor accepts any of those values in `defaultOptions`. The tradeoff is that hook and method return types become more generic. For example, calling `useSuspenseQuery` without an explicit `errorPolicy` will return a result typed as if all error policies are possible, since TypeScript can't know which specific value your instance uses at runtime.
Note that making a property optional (`errorPolicy?:`) is equivalent to adding the TypeScript default value (`"none"`) to the union. So `errorPolicy?: "all" | "ignore"` has the same effect on return types as `errorPolicy: "none" | "all" | "ignore"`, because TypeScript assumes the option could also be absent (i.e., `"none"`).
You can also use a **partial union** that only lists the values you actually use. For example, if you only ever use `"all"` or `"ignore"`, declare `errorPolicy: "all" | "ignore"` (required) to keep the union narrow and avoid unused values broadening your signatures unnecessarily.
## 4.2.0-alpha.1
### Patch Changes
- [#13166](https://github.com/apollographql/apollo-client/pull/13166) [`0537d97`](https://github.com/apollographql/apollo-client/commit/0537d97161a51479141a182d869458912e1b8e1d) Thanks [@jerelmiller](https://github.com/jerelmiller)! - Release changes in 4.1.5 and 4.1.6.
## 4.2.0-alpha.0
### Minor Changes
- [#13130](https://github.com/apollographql/apollo-client/pull/13130) [`dd12231`](https://github.com/apollographql/apollo-client/commit/dd122316028b55307de4a40335512307c8fa916a) Thanks [@jerelmiller](https://github.com/jerelmiller)! - Improve the accuracy of `client.query` return type to better detect the current `errorPolicy`. The `data` property is no longer nullable when the `errorPolicy` is `none`. This makes it possible to remove the `undefined` checks or optional chaining in most cases.
## 4.1.9
### Patch Changes
- [#13203](https://github.com/apollographql/apollo-client/pull/13203) [`099954b`](https://github.com/apollographql/apollo-client/commit/099954b9905c0f80b57563eb64157386f4493e84) Thanks [@copilot-swe-agent](https://github.com/apps/copilot-swe-agent)! - Remove the `workspaces` field from the published `package.json` in `dist` to avoid Yarn v1 warnings about workspaces requiring private packages.
## 4.1.8
### Patch Changes
- [#13202](https://github.com/apollographql/apollo-client/pull/13202) [`8a51ea6`](https://github.com/apollographql/apollo-client/commit/8a51ea636600dbe4b48477d32f30469b7d36b152) Thanks [@phryneas](https://github.com/phryneas)! - Ship agent skill for usage with @tanstack/intent — the skill is now bundled in the npm package under `skills/apollo-client/` and discoverable by `intent list`.
For more context, see the [TanStack Intent QuickStart](https://tanstack.com/intent/latest/docs/getting-started/quick-start-consumers).
## 4.1.7
### Patch Changes
- [#13187](https://github.com/apollographql/apollo-client/pull/13187) [`bb3fd9b`](https://github.com/apollographql/apollo-client/commit/bb3fd9b3d40a2505add673a6ee89d85b8b4c8984) Thanks [@jerelmiller](https://github.com/jerelmiller)! - Fix RxJS interop issue with the observable returned by `WebSocketLink`.
## 4.1.6
### Patch Changes
- [#13128](https://github.com/apollographql/apollo-client/pull/13128) [`6c0b8e4`](https://github.com/apollographql/apollo-client/commit/6c0b8e4301609b62ed599340589c978e4f51f020) Thanks [@pavelivanov](https://github.com/pavelivanov)! - Fix `useQuery` hydration mismatch when `ssr: false` and `skip: true` are used together
When both options were combined, the server would return `loading: false` (because `useSSRQuery` checks `skip` first), but the client's `getServerSnapshot` was returning `ssrDisabledResult` with `loading: true`, causing a hydration mismatch.
## 4.1.5
### Patch Changes
- [#13155](https://github.com/apollographql/apollo-client/pull/13155) [`3ba1583`](https://github.com/apollographql/apollo-client/commit/3ba1583f93c40343501acd9d598ce506537d1c9b) Thanks [@jerelmiller](https://github.com/jerelmiller)! - Fix an issue where `useQuery` would poll with `pollInterval` when `skip` was initialized to `true`.
- [#13135](https://github.com/apollographql/apollo-client/pull/13135) [`fd42142`](https://github.com/apollographql/apollo-client/commit/fd42142495d24859a9bc7145a85bc8f8d857ec88) Thanks [@jerelmiller](https://github.com/jerelmiller)! - Fix issue where `client.query` would apply options from `defaultOptions.watchQuery`.
## 4.1.4
### Patch Changes
- [#13124](https://github.com/apollographql/apollo-client/pull/13124) [`578081f`](https://github.com/apollographql/apollo-client/commit/578081f2da7f2f54c0dd2711ee4a97530a5c38fc) Thanks [@Re-cool](https://github.com/Re-cool)! - Ensure `PersistedQueryLink` merges `http` and `fetchOptions` context values instead of overwriting them.
## 4.1.3
### Patch Changes
- [#13111](https://github.com/apollographql/apollo-client/pull/13111) [`bf46fe0`](https://github.com/apollographql/apollo-client/commit/bf46fe019b316ea8a87a05981a89fac5411260b4) Thanks [@RogerHYang](https://github.com/RogerHYang)! - Fix `createFetchMultipartSubscription` to support cancellation via `AbortController`
Previously, calling `dispose()` or `unsubscribe()` on a subscription created by `createFetchMultipartSubscription` had no effect - the underlying fetch request would continue running until completion. This was because no `AbortController` was created or passed to `fetch()`, and no cleanup function was returned from the Observable.
## 4.1.2
### Patch Changes
- [#13105](https://github.com/apollographql/apollo-client/pull/13105) [`8b62263`](https://github.com/apollographql/apollo-client/commit/8b62263e19b3442e20fea822de62074cf4f5cb22) Thanks [@phryneas](https://github.com/phryneas)! - `ssrMode`, `ssrForceFetchDelay` or `prioritizeCacheValues` should not override `fetchPolicy: 'cache-only'`, `fetchPolicy: 'no-cache'`, `fetchPolicy: 'standby'`, `skip: true`, or `skipToken` when reading the initial value of an `ObservableQuery`.
- [#13105](https://github.com/apollographql/apollo-client/pull/13105) [`8b62263`](https://github.com/apollographql/apollo-client/commit/8b62263e19b3442e20fea822de62074cf4f5cb22) Thanks [@phryneas](https://github.com/phryneas)! - Fix `skipToken` in `useQuery` with `prerenderStatic` and related SSR functions.
- [#13105](https://github.com/apollographql/apollo-client/pull/13105) [`8b62263`](https://github.com/apollographql/apollo-client/commit/8b62263e19b3442e20fea822de62074cf4f5cb22) Thanks [@phryneas](https://github.com/phryneas)! - Avoid fetches with `fetchPolicy: no-cache` in `useQuery` with `prerenderStatic` and related SSR functions.
## 4.1.1
### Patch Changes
- [#13103](https://github.com/apollographql/apollo-client/pull/13103) [`dee7dcf`](https://github.com/apollographql/apollo-client/commit/dee7dcff4d4baa26d623d1ecace60be88c684c1a) Thanks [@jerelmiller](https://github.com/jerelmiller)! - Ensure `@client` fields that are children of aliased server fields are resolved correctly.
## 4.1.0
### Minor Changes
- [#13043](https://github.com/apollographql/apollo-client/pull/13043) [`65e66ca`](https://github.com/apollographql/apollo-client/commit/65e66cafb6828b63d14b64877bbad47af95f66e4) Thanks [@jerelmiller](https://github.com/jerelmiller)! - Support `headers` transport for enhanced client awareness.
- [#12927](https://github.com/apollographql/apollo-client/pull/12927) [`785e223`](https://github.com/apollographql/apollo-client/commit/785e2232b4f7d9e561611cd4f45b8fdd1e44319e) Thanks [@jerelmiller](https://github.com/jerelmiller)! - You can now provide a callback function as the `context` option on the `mutate` function returned by `useMutation`. The callback function is called with the value of the `context` option provided to the `useMutation` hook. This is useful if you'd like to merge the context object provided to the `useMutation` hook with a value provided to the `mutate` function.
```ts
function MyComponent() {
const [mutate, result] = useMutation(MUTATION, {
context: { foo: true },
});
async function runMutation() {
await mutate({
// sends context as { foo: true, bar: true }
context: (hookContext) => ({ ...hookContext, bar: true }),
});
}
// ...
}
```
- [#12923](https://github.com/apollographql/apollo-client/pull/12923) [`94ea3e3`](https://github.com/apollographql/apollo-client/commit/94ea3e32c82dd767b62a6907be6c3891864633af) Thanks [@jerelmiller](https://github.com/jerelmiller)! - Fix an issue where deferred payloads that returned arrays with fewer items than the original cached array would retain items from the cached array. This change includes `@stream` arrays where stream arrays replace the cached arrays.
- [#12927](https://github.com/apollographql/apollo-client/pull/12927) [`96b531f`](https://github.com/apollographql/apollo-client/commit/96b531f6b57a158aa2c57da976c6dd22c1a7f4d5) Thanks [@jerelmiller](https://github.com/jerelmiller)! - Don't set the fallback value of a `@client` field to `null` when a `read` function is defined. Instead the `read` function will be called with an `existing` value of `undefined` to allow default arguments to be used to set the returned value.
When a `read` function is not defined nor is there a defined resolver for the field, warn and set the value to `null` only in that instance.
- [#12927](https: