Skip to content

Releases: apollographql/apollo-client

@apollo/[email protected]

23 Apr 08:23
47c63b9

Choose a tag to compare

Patch Changes

  • #13202 8a51ea6 Thanks @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.

@apollo/[email protected]

21 Apr 15:20
497b987

Choose a tag to compare

Pre-release

Minor Changes

  • #13132 f3ce805 Thanks @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:

    // 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:

    // 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:

    // 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 f3ce805 Thanks @phryneas! - Synchronize method and hook return types with defaultOptions.

    Prior to this change, the following code snippet would always apply:

    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:

    // 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:

    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):

    // 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.

@apollo/[email protected]

08 Apr 16:26
be4280a

Choose a tag to compare

Patch Changes

v3.14.1

12 Mar 21:02
ff90828

Choose a tag to compare

Patch Changes

  • #13168 6b84ec0 Thanks @jerelmiller! - Fix issue where muting a deprecation from one entrypoint would not mute the warning when checked in a different entrypoint. This caused some rogue deprecation warnings to appear in the console even though the warnings should have been muted.

  • #12970 f91fab5 Thanks @acemir! - Add a deprecation message for the variableMatcher option in MockLink.

  • #13168 6b84ec0 Thanks @jerelmiller! - Ensure deprecation warnings are properly silenced in React hooks when globally disabled.

@apollo/[email protected]

05 Mar 00:01
67bce6f

Choose a tag to compare

Pre-release

Patch Changes

@apollo/[email protected]

23 Feb 18:28
1f6decb

Choose a tag to compare

Patch Changes

  • #13128 6c0b8e4 Thanks @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.

@apollo/[email protected]

19 Feb 17:14
32f92e5

Choose a tag to compare

Patch Changes

  • #13155 3ba1583 Thanks @jerelmiller! - Fix an issue where useQuery would poll with pollInterval when skip was initialized to true.

  • #13135 fd42142 Thanks @jerelmiller! - Fix issue where client.query would apply options from defaultOptions.watchQuery.

@apollo/[email protected]

13 Feb 17:41
001df18

Choose a tag to compare

Pre-release

Minor Changes

  • #13130 dd12231 Thanks @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.

@apollo/[email protected]

05 Feb 18:18
1afb5c1

Choose a tag to compare

Patch Changes

  • #13124 578081f Thanks @Re-cool! - Ensure PersistedQueryLink merges http and fetchOptions context values instead of overwriting them.

@apollo/[email protected]

28 Jan 18:21
125862d

Choose a tag to compare

Patch Changes

  • #13111 bf46fe0 Thanks @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.