@apollo/client
Version:
A fully-featured caching GraphQL client.
947 lines (707 loc) • 801 kB
Markdown
# @apollo/client
## 4.3.0-alpha.5
### Minor Changes
- [#13390](https://github.com/apollographql/apollo-client/pull/13390) [`90e338c`](https://github.com/apollographql/apollo-client/commit/90e338c3cd5ec1be23852cc5ef0ca6078a98f548) Thanks [@jerelmiller](https://github.com/jerelmiller)! - Fix issue where sibling `@defer` fragments were pruned incorrectly when at least one of the `@defer` fragments wasn't delivered.
As a result of this change, a `label` argument is now added to all outgoing `@defer` directives when using the `GraphQL17Alpha9Handler` in order to disambiguate the `@defer` fragments from each other.
- [#13393](https://github.com/apollographql/apollo-client/pull/13393) [`434d25f`](https://github.com/apollographql/apollo-client/commit/434d25facdcb214cac4ee33abbe1f1fecd05637b) Thanks [@jerelmiller](https://github.com/jerelmiller)! - Change when `@defer` fragments and `@stream` fields are pruned for `cache-first` and `cache-and-network` fetch policies to better match the network when the initial value contained a partial result:
- `cache-first`: prune undelivered `@defer` fragments or `@stream` items when the result is fetched from the network due to a partial result
- `cache-and-network`: prune undelivered `@defer` fragments or `@stream` items if the initial cache value was partial. If the first value emitted from the cache is complete, the results will not be pruned.
This makes the emitted results more predictable by following what the network has delivered and avoids some ambiguity in other edge cases.
For example, with a `cache-first` fetch policy where all `@defer` fields are written to the cache, but a non-deferred field is partial, the values emitted from the client previously looked like the following:
```graphql
query {
user {
id
name
... @defer {
email
}
}
}
```
```ts
// data written to the cache is missing name
{ user: { id: 1, email: "user.cache@example.com" }}
// 1. empty because the result is partial
{ data: undefined, dataState: "empty", ... }
// 2. returns all data because the cache contains a value for email
{ data: { user: 1, name: "User", email: "user.cache@example.com" }, dataState: "complete" }
// 3. email updated from the server
{ data: { user: 1, name: "User", email: "user.network@example.com" }, dataState: "complete" }
```
Here the result is confusing because the initial value returned from the query was `undefined`, yet a complete result was returned after the initial chunk from the network returned (which did not contain `email`).
The cache values are now pruned if the network hasn't delivered them yet:
```ts
// 1. empty because the result is partial
{ data: undefined, dataState: "empty" }
// 2. email hasn't been delivered by the network so it gets pruned
{ data: { user: 1, name: "User" }, dataState: "streaming" }
// 3. full result returned after the network streams the email field
{ data: { user: 1, name: "User", email: "user.network@example.com" }, dataState: "complete" }
```
This is especially helpful in situations where `@defer` boundaries that are never delivered due to errors prevent an awkward situation where the client would otherwise have to choose whether to serve the stale cache result from the cache, or prune the undelivered fragment on the final chunk.
### Patch Changes
- [#13381](https://github.com/apollographql/apollo-client/pull/13381) [`9c73762`](https://github.com/apollographql/apollo-client/commit/9c73762b8e8f1d16885140df847b8222f16a39f4) Thanks [@jerelmiller](https://github.com/jerelmiller)! - Fix an issue where a `network-only` query leaked partial cache data for `@defer` fragments that were not delivered by the network due to an error that bubbled to the `@defer` fragment boundary.
- [#13390](https://github.com/apollographql/apollo-client/pull/13390) [`90e338c`](https://github.com/apollographql/apollo-client/commit/90e338c3cd5ec1be23852cc5ef0ca6078a98f548) Thanks [@jerelmiller](https://github.com/jerelmiller)! - Fix an issue where a sibling non-deferred fragment might be accidentally pruned when the `@defer` fragment hadn't been delivered.
- [#13403](https://github.com/apollographql/apollo-client/pull/13403) [`aaff7a8`](https://github.com/apollographql/apollo-client/commit/aaff7a8a833c261e0514488c493a18d51e07a983) Thanks [@jerelmiller](https://github.com/jerelmiller)! - Fix issue where the wrong `dataState` was returned when there was nothing written to the cache and a `@defer` fragment was marked pending.
- [#13381](https://github.com/apollographql/apollo-client/pull/13381) [`9c73762`](https://github.com/apollographql/apollo-client/commit/9c73762b8e8f1d16885140df847b8222f16a39f4) Thanks [@jerelmiller](https://github.com/jerelmiller)! - Fix an issue where a `@defer` query reported the `dataState` as `complete` instead of `streaming` when an error occurs on a deferred field that bubbled to the defer boundary.
- [#13373](https://github.com/apollographql/apollo-client/pull/13373) [`2551937`](https://github.com/apollographql/apollo-client/commit/25519374c7137dee7b1ddd4a70b28288936fcef0) Thanks [@jerelmiller](https://github.com/jerelmiller)! - Fix an issue where a cache write in the middle of polling would remain as the query value if future poll requests returned deep equal results to previous polling results.
- [#13403](https://github.com/apollographql/apollo-client/pull/13403) [`aaff7a8`](https://github.com/apollographql/apollo-client/commit/aaff7a8a833c261e0514488c493a18d51e07a983) Thanks [@jerelmiller](https://github.com/jerelmiller)! - Fix issue where setting `returnPartialData: true` might report the wrong `dataState` when partial data was written to the cache and `@defer` fragments were pending.
- [#13381](https://github.com/apollographql/apollo-client/pull/13381) [`9c73762`](https://github.com/apollographql/apollo-client/commit/9c73762b8e8f1d16885140df847b8222f16a39f4) Thanks [@jerelmiller](https://github.com/jerelmiller)! - Fix an invariant error thrown when a `@defer` boundary received a payload after it had already been marked complete.
## 4.3.0-alpha.4
### Patch Changes
- [#13347](https://github.com/apollographql/apollo-client/pull/13347) [`7d543d6`](https://github.com/apollographql/apollo-client/commit/7d543d6416688ed113295a69e73b706c097a0d31) Thanks [@jerelmiller](https://github.com/jerelmiller)! - Fix an issue where `network-only` incremental queries could cause cache data to leak into the emitted result when a `@defer` or `@stream` boundary already had complete data in the cache. Cache data inside pending `@defer` objects and `@stream` arrays are now pruned so that only completed `@defer` or `@stream` boundaries are returned.
NOTE: This change only applies to `InMemoryCache` when using `GraphQL17Alpha9Handler`.
- [#13329](https://github.com/apollographql/apollo-client/pull/13329) [`1d581d2`](https://github.com/apollographql/apollo-client/commit/1d581d282fe223e4bf39ea5e7a3cbc44fdbf32b5) Thanks [@AmariahAK](https://github.com/AmariahAK)! - Cache diffs for incomplete queries no longer pay the cost of building a full `MissingFieldError` when the `missing` property is not accessed. The error object is now only constructed when the `missing` property is accessed the first time. This improves performance by avoiding a V8 stack capture when `missing` is ignored entirely.
As an additional small performance improvement, `JSON.stringify` is no longer used in the error message on objects whose cache ID is known. `JSON.stringify` is only used for non-normalized objects.
- [#13347](https://github.com/apollographql/apollo-client/pull/13347) [`7d543d6`](https://github.com/apollographql/apollo-client/commit/7d543d6416688ed113295a69e73b706c097a0d31) Thanks [@jerelmiller](https://github.com/jerelmiller)! - Fix an issue where partial cache data could leak into intermediate incremental results. This could cause runtime crashes if you relied on the presence of values to determine whether the `@defer` data had streamed in or not.
## 4.3.0-alpha.3
### Minor Changes
- [#13324](https://github.com/apollographql/apollo-client/pull/13324) [`0abd8de`](https://github.com/apollographql/apollo-client/commit/0abd8de53a408c6b5925b2a909acde5179eaac46) Thanks [@jerelmiller](https://github.com/jerelmiller)! - Fix the accuracy of `dataState` in complex incremental streaming scenarios, especially when combined with `returnPartialData: true`.
Prior to this change, all intermediate chunks used for both `@defer` and `@stream` directives returned a `dataState` of `streaming`, regardless of whether the actual data shape fit the definition of the `streaming` data state. The `streaming` data state represents an incomplete incremental response where the only holes in the data occur at `@defer` boundaries.
Let's use the following example of where the previous `dataState` fell down when combined with `returnPartialData`.
```gql
query GreetingQuery {
greeting {
message
... @defer {
recipient {
name
email
}
}
}
}
```
1. Scenario 1: partial data inside a `@defer` boundary written to the cache
Let's say the cache contained the following partial data:
```ts
{
greeting: {
__typename: "Greeting",
recipient: {
__typename: "Person",
name: "John Doe",
},
},
};
```
After the first chunk arrives from the server, the data looks like the following:
```ts
{
greeting: {
__typename: "Greeting",
message: "Hello, John",
recipient: {
__typename: "Person",
name: "John Doe",
},
},
};
```
This data is not `complete` because `recipient.email` is missing. This data is also not `streaming` because the data requirements in the `@defer` boundary are partially fulfilled due to the existence of `recipient`. This could lead to runtime crashes on `recipient.email` if you use the existence of `recipient` to detect whether data in the `@defer` boundary has streamed in or not. This change now accurately reports this as `partial` to ensure the field is marked as a partial field in `recipient`.
2. Scenario 2: partial data written to the cache that fulfills the data requirements of the `@defer` boundary
Let's say the cache contained the following partial data:
```ts
{
greeting: {
__typename: "Greeting",
recipient: {
__typename: "Person",
name: "John Doe",
email: "john@example.com",
},
},
};
```
After the first chunk arrives from the server, the data looks like the following:
```ts
{
greeting: {
__typename: "Greeting",
message: "Hello, John",
recipient: {
__typename: "Person",
name: "John Doe",
email: "john@example.com",
},
},
};
```
In this case, the combination of the first chunk and the partial data in the cache now fulfills the data requirements of the query. Even though the server is still streaming data (`NetworkStatus.streaming`), we can report this as `dataState: "complete"` since it is safe to access data on all fields.
This change also means `@stream` queries by definition fulfill the data requirements of the query after the first chunk arrives since `@stream` operates on lists and contains no data holes. `@stream` queries now accurately report `dataState` as `complete` or `partial`, depending on whether the list mixes partial data with streamed list items.
As a result of this change, some cases where you'd previously see `dataState` reported as `"streaming"` are now reported as `partial` or `complete`.
If you use `dataState` to determine whether an incremental request is still in-flight, please use `networkStatus` instead to check for `NetworkStatus.streaming`. `dataState` is type narrowing feature and not intended to report the network status.
### Patch Changes
- [#13324](https://github.com/apollographql/apollo-client/pull/13324) [`0abd8de`](https://github.com/apollographql/apollo-client/commit/0abd8de53a408c6b5925b2a909acde5179eaac46) Thanks [@jerelmiller](https://github.com/jerelmiller)! - Fix an issue where field `read` functions were not applied to intermediate results while streaming `@defer` responses. `cache.diff` ran the `read` functions, but the transformed values were only applied to the emitted result when the updated cache result was considered complete. Intermediate chunks whose only holes were at `@defer` boundaries now correctly return the result of field `read` functions.
```ts
new InMemoryCache({
typePolicies: {
Greeting: {
fields: {
message: {
read: (message) => message.toUpperCase(),
},
},
},
},
});
// query GreetingQuery {
// greeting {
// message
// ... @defer {
// recipient { name }
// }
// }
// }
// First chunk previously returned:
// { greeting: { message: "Hello world" } }
//
// Now correctly returns while still streaming:
// { greeting: { message: "HELLO WORLD" } }
```
- [#13324](https://github.com/apollographql/apollo-client/pull/13324) [`0abd8de`](https://github.com/apollographql/apollo-client/commit/0abd8de53a408c6b5925b2a909acde5179eaac46) Thanks [@jerelmiller](https://github.com/jerelmiller)! - Fix an issue with `@stream` queries when using `returnPartialData: true` where the streamed list was truncated after the first incremental chunk when the list contained partial cache data. The list is no longer truncated and partial list items are now retained as incremental chunks arrive. The `dataState` is now reported as `partial` until the server has streamed enough of the list so that each list item fully satisfies the query.
This change also updates `@stream` queries so that they reported with `dataState: "complete` instead of `"streaming"` since it is safe to access all fields in the response.
## 4.3.0-alpha.2
### Minor Changes
- [#13274](https://github.com/apollographql/apollo-client/pull/13274) [`7b10078`](https://github.com/apollographql/apollo-client/commit/7b10078f4bcd8d82890ca438bf7355677fe2f841) Thanks [@jerelmiller](https://github.com/jerelmiller)! - Adds `Scalar.fromGraphQLScalarType` helper to create a `Scalar` instance from an existing graphql.js `GraphQLScalarType`.
```ts
import { GraphQLScalarType } from "graphql";
import { Scalar } from "@apollo/client";
const dateTimeScalarType = new GraphQLScalarType<Date, string>({
// ...
});
const dateTimeScalar = Scalar.fromGraphQLScalarType(dateTimeScalarType, {
is: (value) => value instanceof Date,
});
```
- [#13252](https://github.com/apollographql/apollo-client/pull/13252) [`ed86234`](https://github.com/apollographql/apollo-client/commit/ed8623485683c38982c87278d1381412ef39a9db) Thanks [@jerelmiller](https://github.com/jerelmiller)! - Adds the plumbing and types implementation for declaring custom scalars and configuring custom scalars in `InMemoryCache`.
You can declare custom scalar types with declaration merging on the `ApolloCache.Scalars` interface:
```ts
// apollo.d.ts
import "@apollo/client";
declare module "@apollo/client" {
namespace ApolloCache {
interface Scalars {
Date: { serialized: string; parsed: Date };
}
}
}
```
This enables the `scalars` option in `InMemoryCache`:
```ts
import { Scalar } from "@apollo/client";
const cache = new InMemoryCache({
scalars: {
Date: new Scalar({
parse: (dateString) => new Date(dateString),
serialize: (date) => date.toISOString(),
is: (value) => value instanceof Date,
}),
},
});
```
- [#13259](https://github.com/apollographql/apollo-client/pull/13259) [`ccaf686`](https://github.com/apollographql/apollo-client/commit/ccaf6867be15e413f08594b54b3516003e28c108) Thanks [@jerelmiller](https://github.com/jerelmiller)! - Adds a `scalar` option to `InMemoryCache` field policies that tells the cache which scalar to use when parsing or serializing the field value.
```ts
import { Scalar } from "@apollo/client";
new InMemoryCache({
scalars: {
DateTime: new Scalar({
parse: (dateString) => new Date(dateString),
serialize: (date) => date.toISOString(),
}),
},
typePolicies: {
Event: {
fields: {
startTime: {
// Parse this field using the DateTime scalar
scalar: "DateTime",
},
},
},
},
});
```
This scalar definition is now used to properly parse or serialize the field value for cache reads and writes as well as `cache.extract()` and `cache.restore()`.
- [#13273](https://github.com/apollographql/apollo-client/pull/13273) [`0886de1`](https://github.com/apollographql/apollo-client/commit/0886de19ed67ca24bbcc075dcf5a94ba01589902) Thanks [@jerelmiller](https://github.com/jerelmiller)! - Automatically serialize variables that include custom scalar values. This includes cache reads and writes as well as requests to the network.
For more complex input objects, a new `inputObjects` option is available to `InMemoryCache` that specifies where nested scalar fields are found.
```ts
const cache = new InMemoryCache({
scalars: {
DateTime: new Scalar({
parse: (value) => new Date(value),
serialize: (value) => value.toISOString(),
is: (value) => value instanceof Date,
}),
},
inputObjects: {
EventFilter: {
fields: {
date: "DateTime",
},
},
},
});
const client = new ApolloClient({ cache, link });
await client.query({
query: gql`
query Event($filter: EventFilter!) {
event(filter: $filter) {
name
}
}
`,
variables: {
filter: {
date: new Date("2026-01-01T00:00:00.000Z"),
},
},
});
// The link receives:
// { filter: { date: "2026-01-01T00:00:00.000Z" } }
```
- [#13252](https://github.com/apollographql/apollo-client/pull/13252) [`ed86234`](https://github.com/apollographql/apollo-client/commit/ed8623485683c38982c87278d1381412ef39a9db) Thanks [@jerelmiller](https://github.com/jerelmiller)! - Adds the `getScalar` abstract method to `ApolloCache` that cache subclasses override to provide scalar behavior to Apollo Client. Defaults to unconditionally return `undefined` if not specified.
## 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.11
### Patch Changes
- [#13398](https://github.com/apollographql/apollo-client/pull/13398) [`3dd3e9a`](https://github.com/apollographql/apollo-client/commit/3dd3e9a6f195ea5dd27a973f153a0538ccdffd20) Thanks [@phryneas](https://github.com/phryneas)! - Fix type signature of some `DocumentationTypes` to fix their display in our documentation.
- [#13392](https://github.com/apollographql/apollo-client/pull/13392) [`d4f0771`](https://github.com/apollographql/apollo-client/commit/d4f0771976766b54e0a95a3d51b708b64eb3cfe0) Thanks [@jerelmiller](https://github.com/jerelmiller)! - Add a development-only warning when a network result is written to the cache but reading the query back from the cache returns a partial result. This usually points at a `merge` or `read` function that did not repair missing fields in the cache, which prevents Apollo Client from applying the cache result to the data received by the network.
## 4.2.10
### Patch Changes
- [#13385](https://github.com/apollographql/apollo-client/pull/13385) [`bfb674e`](https://github.com/apollographql/apollo-client/commit/bfb674ec0ac9cd296b7ea2992d9deebf68edaea9) Thanks [@jerelmiller](https://github.com/jerelmiller)! - Fix accidental widening of the `client.mutate` return type when `optimisticResponse` was present.
- [#13382](https://github.com/apollographql/apollo-client/pull/13382) [`365373e`](https://github.com/apollographql/apollo-client/commit/365373e0a0b647e89183245c3904d58499aa63e1) Thanks [@jerelmiller](https://github.com/jerelmiller)! - Fix result types widened when a query's variables had constant types (e.g. `TypedDocumentNode<Data, { type: "main" }>`). This caused options such as `returnPartialData` or `errorPolicy` to be reported as their widened types (e.g. `boolean`, `ErrorPolicy`) instead of the value that was passed which returned the wrong `data` and `dataState` types.
- [#13382](https://github.com/apollographql/apollo-client/pull/13382) [`365373e`](https://github.com/apollographql/apollo-client/commit/365373e0a0b647e89183245c3904d58499aa63e1) Thanks [@jerelmiller](https://github.com/jerelmiller)! - Fix issue where unknown options were permitted by TypeScript when passed alongside a valid option to APIs with modern signatures.
- [#13383](https://github.com/apollographql/apollo-client/pull/13383) [`5840f50`](https://github.com/apollographql/apollo-client/commit/5840f5014d0a6bb9b963801657cd68b0a1aba6c8) Thanks [@jerelmiller](https://github.com/jerelmiller)! - Update the return type of `refetch`, `fetchMore` and `useLazyQuery`'s `execute` function on the provided `errorPolicy`. Previously these APIs all used the default type which typed `data` as `TData | undefined` and `error` as `ErrorLike | undefined`.
## 4.2.9
### Patch Changes
- [#13364](https://github.com/apollographql/apollo-client/pull/13364) [`2f383e7`](https://github.com/apollographql/apollo-client/commit/2f383e7e484ceaec72df205b52abf8430cc59891) Thanks [@atharv-sys32](https://github.com/atharv-sys32)! - Fix a bug where GraphQL variable default values were not applied during cache reads when variables with defaults were explicitly set to `undefined`. This caused `@include`/`@skip` directives to throw "Invalid variable referenced" errors when the variable was passed as `undefined` instead of being omitted entirely.
- [#13367](https://github.com/apollographql/apollo-client/pull/13367) [`2b39cc8`](https://github.com/apollographql/apollo-client/commit/2b39cc8b2e0a6b0a6c1dfc2f64fa2940c59b23bd) Thanks [@jerelmiller](https://github.com/jerelmiller)! - Fix an issue where some `@export` queries would not react to cache updates when the fields keyed by exported variables were updated.
## 4.2.8
### Patch Changes
- [#13349](https://github.com/apollographql/apollo-client/pull/13349) [`501a33b`](https://github.com/apollographql/apollo-client/commit/501a33bba831828da0398c994662582054272743) Thanks [@jerelmiller](https://github.com/jerelmiller)! - Prevent the `setTimeout` in `connectToDevtools` that shows the devtools suggestion from firing when the user agent does not match Chrome or Firefox. This check was previously done inside the `setTimeout` which meant the timer was scheduled for environments where we'd never show the message anyways. For test environments, this could cause flaky tests when that `setTimeout` outlived the tests and ran after any virtual DOM was torn down and removed.
## 4.2.7
### Patch Changes
- [#13320](https://github.com/apollographql/apollo-client/pull/13320) [`538c906`](https://github.com/apollographql/apollo-client/commit/538c906143c18dcc4fd9c29427413451fcd72c22) Thanks [@jerelmiller](https://github.com/jerelmiller)! - Cleanup some unused internals. Please file an issue if you notice anything change.
## 4.2.6
### Patch Changes
- [#13315](https://github.com/apollographql/apollo-client/pull/13315) [`a406cc9`](https://github.com/apollographql/apollo-client/commit/a406cc9669246972a8f067462422aec716b6213b) Thanks [@fallintoplace](https://github.com/fallintoplace)! - Prevent relay multipart subscriptions from issuing a fetch request after serializing the request body fails.
- [#13307](https://github.com/apollographql/apollo-client/pull/13307) [`abd0781`](https://github.com/apollographql/apollo-client/commit/abd07814bbe80d9307458a450dd28addf1d38ef1) Thanks [@wolfie](https://github.com/wolfie)! - Speed up cache writes by avoiding a full AST `visit` of every written field to detect `@stream`. The check now runs only when the result carries stream info, and only inspects the field node's own directives. As a result, fields that merely contain `@stream` on a nested field are no longer treated as streamed themselves and now overwrite existing lists like regular fields instead of merging chunk-wise.
## 4.2.5
### Patch Changes
- [#13302](https://github.com/apollographql/apollo-client/pull/13302) [`bb75dd3`](https://github.com/apollographql/apollo-client/commit/bb75dd3a42bab21a0ff14c4482a5cb99a61843eb) Thanks [@tpict](https://github.com/tpict)! - Export `KeyArgsFunction` and `RelayFieldPolicy` types from public entrypoints.
## 4.2.4
### Patch Changes
- [#13281](https://github.com/apollographql/apollo-client/pull/13281) [`e4df809`](https://github.com/apollographql/apollo-client/commit/e4df809e87a1d2b72728df53327f531f65411ed3) Thanks [@jerelmiller](https://github.com/jerelmiller)! - Fixes an issue where `client.readFragment` and `client.readQuery` ignored the `optimistic` option when passed in the options object.
## 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 `createQueryPreloade