releases.shpreview
Apollo GraphQL/Apollo Client

Apollo Client

$npx -y @buildinternet/releases show apollo-client
Mon
Wed
Fri
AprMayJunJulAugSepOctNovDecJanFebMarApr
Less
More
Releases15Avg5/moVersions@apollo/client@4.1.0-rc.0 → @apollo/client@4.1.7
Apr 23, 2025

Major Changes

  • #12576 a92ff78 Thanks @jerelmiller! - The cache and forceFetch properties are no longer available on context when calling operation.getContext(). cache can be accessed through the operation with operation.client.cache instead. forceFetch has been replaced with queryDeduplication which specifies whether queryDeduplication was enabled for the request or not.

  • #12576 a92ff78 Thanks @jerelmiller! - ApolloLink.execute now requires a third argument which provides the client that initiated the request to the link chain. If you use execute directly, add a third argument with a client property:

    ApolloLink.execute(link, operation, { client });
    
    // or if you import the `execute` function directly:
    execute(link, operation, { client });
  • #12566 ce4b488 Thanks @jerelmiller! - Don't broadcastQueries when a query is torn down.

Minor Changes

  • #12576 a92ff78 Thanks @jerelmiller! - Provide an extension to define types for context passed to the link chain. To define your own types, use declaration merging to add properties to the DefaultContext type.

    // @apollo-client.d.ts
    // This import is necessary to ensure all Apollo Client imports
    // are still available to the rest of the application.
    import "@apollo/client";
    
    declare module "@apollo/client" {
      interface DefaultContext extends Record<string, any> {
        myProperty: string;
      }
    }

    Links that provide context options can be used with this type to add those context types to DefaultContext. For example, to add context options from HttpLink, add the following code:

    import { HttpLink } from "@apollo/client";
    
    declare module "@apollo/client" {
      interface DefaultContext extends HttpLink.ContextOptions {
        myProperty: string;
      }
    }

    At this time, the following built-in links support context options:

    • HttpLink.ContextOptions
    • BatchHttpLink.ContextOptions
  • #12576 a92ff78 Thanks @jerelmiller! - Add a client property to the operation passed to the link chain. This client is set as the client making the request to the link chain.

Patch Changes

  • #12574 0098ec9 Thanks @jerelmiller! - Export gql from the @apollo/client/react entrypoint.

  • #12572 3dc50e6 Thanks @jerelmiller! - Adjust useMutation types to better handle required variables. When required variables are missing, TypeScript will now complain if they are not provided either to the hook or the returned mutate function. Providing required variables to useMutation will make them optional in the returned mutate function.

Apr 17, 2025

Major Changes

  • #12559 49ace0e Thanks @jerelmiller! - ObservableQuery.variables can now be reset back to empty when calling reobserve with variables: undefined. Previously the variables key would be ignored so variables would remain unchanged.

  • #12559 49ace0e Thanks @jerelmiller! - never is no longer supported as a valid TVariables generic argument for APIs that require variables as part of its type. Use Record<string, never> instead.

  • #12559 49ace0e Thanks @jerelmiller! - When passing a variables key with the value undefined, the value will be replaced by the default value in the query, if it is provided, rather than leave it as undefined.

    // given this query
    const query = gql`
      query PaginatedQuery($limit: Int! = 10, $offset: Int) {
        list(limit: $limit, offset: $offset) {
          id
        }
      }
    `;
    
    const observable = client.query({
      query,
      variables: { limit: 5, offset: 0 },
    });
    console.log(observable.variables); // => { limit: 5, offset: 0 }
    
    observable.reobserve({ variables: { limit: undefined, offset: 10 } });
    // limit is now `10`. This would previously be `undefined`
    console.log(observable.variables); // => { limit: 10, offset: 10 }
  • #12562 90bf0e6 Thanks @jerelmiller! - client.query no longer supports a fetchPolicy of standby. standby does not fetch and did not return data. standby is meant for watched queries where fetching should be on hold.

Minor Changes

  • #12557 51d26ae Thanks @jerelmiller! - Add ability to specify message formatter for CombinedGraphQLErrors and CombinedProtocolErrors. To provide your own message formatter, override the static formatMessage property on these classes.

    CombinedGraphQLErrors.formatMessage = (
      errors,
      { result, defaultFormatMessage }
    ) => {
      return "Some formatted message";
    };
    
    CombinedProtocolErrors.formatMessage = (errors, { defaultFormatMessage }) => {
      return "Some formatted message";
    };
  • #12546 5dffbbe Thanks @jerelmiller! - Add a static is method to error types defined by Apollo Client. is makes it simpler to determine whether an error is a specific type, which can be helpful in cases where you'd like to narrow the error type in order to use specific properties from that error.

    This change applies to the following error types:

    • CombinedGraphQLErrors
    • CombinedProtocolErrors
    • ServerError
    • ServerParseError
    • UnconventionalError

    Example

    import { CombinedGraphQLErrors } from "@apollo/client";
    
    if (CombinedGraphQLErrors.is(error)) {
      console.log(error.message);
      error.errors.forEach((graphQLError) => console.log(graphQLError.message));
    }
  • #12561 99d72bf Thanks @jerelmiller! - Add the ability to detect if an error was an error was emitted from the link chain. This is useful if your application throws custom errors in other areas of the application and you'd like to differentiate them from errors emitted by the link chain itself.

    To detect if an error was emitted from the link chain, use LinkError.is.

    import { LinkError } from "@apollo/client";
    
    client.query({ query }).catch((error) => {
      if (LinkError.is(error)) {
        // This error originated from the link chain
      }
    });

Patch Changes

  • #12559 49ace0e Thanks @jerelmiller! - The variables option used with various APIs are now enforced more consistently across the client when TVariables contains required variables. If required variables are not provided, TypeScript will now complain that it requires a variables option.

    This change affects the following APIs:

    • client.query
    • client.mutate
    • client.subscribe
    • client.watchQuery
    • useBackgroundQuery
    • useQuery
    • useSubscription
    • useSuspenseQuery
  • #12559 49ace0e Thanks @jerelmiller! - Fix type of variables returned from useLazyQuery. When called is false, variables is now Partial<TVariables> instead of TVariables.

  • #12562 90bf0e6 Thanks @jerelmiller! - client.query no longer supports notifyOnNetworkStatusChange in options. An error will be thrown if this option is set. The effects of this option were not observable by client.query since client.query emits a single result.

  • #12557 51d26ae Thanks @jerelmiller! - Update format of the error message for CombinedGraphQLErrors and CombinedProtocolErrors to be more like v3.x.

    console.log(error.message);
    - `The GraphQL server returned with errors:
    - - Email not found
    - - Username already in use`
    + `Email not found
    + Username already in use`
    
  • #12559 49ace0e Thanks @jerelmiller! - ObservableQuery.variables has been updated to return TVariables rather than TVariables | undefined. This is more consistent with the runtime value where an empty object ({}) will be returned when the variables option is not provided.

Patch Changes

Apr 11, 2025

Major Changes

  • #12536 e14205a Thanks @jerelmiller! - An initial loading state is now emitted from ObservableQuery when subscribing if notifyOnNetworkStatusChange is set to true.

  • #12512 e809b71 Thanks @jerelmiller! - notifyOnNetworkStatusChange now defaults to true. This means that loading states will be emitted (core API) or rendered (React) by default when calling refetch, fetchMore, etc. To maintain the old behavior, set notifyOnNetworkStatusChange to false in defaultOptions.

    new ApolloClient({
      defaultOptions: {
        watchQuery: {
          // Use the v3 default
          notifyOnNetworkStatusChange: false,
        },
      },
    });

Patch Changes

  • #12536 e14205a Thanks @jerelmiller! - The returned networkStatus in useLazyQuery is now set to setVariables when calling the useLazyQuery execute function for the first time with variables.

  • #12536 e14205a Thanks @jerelmiller! - Ensure ObservableQuery stops polling if switching to a standby fetchPolicy. When switching back to a non-standby fetchPolicy, polling will resume.

  • #12536 e14205a Thanks @jerelmiller! - Ensure a loading state is emitted when calling the execute function after changing clients in useLazyQuery.

  • #12542 afb4fce Thanks @jerelmiller! - Ensure useLazyQuery does not return a partial property which is not specified by the result type.

Apr 10, 2025

Major Changes

  • #12539 dd0d6d6 Thanks @jerelmiller! - onError link now uses a single error property to report the error that caused the link callback to be called. This will be an instance of CombinedGraphQLErrors in the event GraphQL errors were emitted from the terminating link, CombinedProtocolErrors if the terminating link emitted protocol errors, or the unwrapped error type if any other non-GraphQL error was thrown or emitted.

    - const errorLink = onError(({ graphQLErrors, networkError, protocolErrors }) => {
    -   graphQLErrors.forEach(error => console.log(error.message));
    + const errorLink = onError(({ error }) => {
    +   if (error.name === 'CombinedGraphQLErrors') {
    +     error.errors.forEach(rawError => console.log(rawError.message));
    +   }
    });
    
  • #12533 73221d8 Thanks @jerelmiller! - Remove the onError and setOnError methods from ApolloLink. onError was only used by MockLink to rewrite errors if setOnError was used.

  • #12531 7784b46 Thanks @jerelmiller! - Mocked responses passed to MockLink now accept a callback for the request.variables option. This is used to determine if the mock should be matched for a set of request variables. With this change, the variableMatcher option has been removed in favor of passing a callback to variables. Update by moving the callback function from variableMatcher to request.variables.

    new MockLink([
      {
        request: {
          query,
    +     variables: (requestVariables) => true
        },
    -   variableMatcher: (requestVariables) => true
      }
    ]);
    
  • #12526 391af1d Thanks @phryneas! - The @apollo/client and @apollo/client/core entry points are now equal. In the next major, the @apollo/client/core entry point will be removed. Please change imports over from @apollo/client/core to @apollo/client.

  • #12525 8785186 Thanks @jerelmiller! - Throw an error when a client-only query is used in a mocked response passed to MockLink.

  • #12532 ae0dcad Thanks @jerelmiller! - Default the delay for all mocked responses passed to MockLink using realisticDelay. This ensures your test handles loading states by default and is not reliant on a specific timing.

    If you would like to restore the old behavior, use a global default delay of 0.

    MockLink.defaultOptions = {
      delay: 0,
    };
  • #12530 2973e2a Thanks @jerelmiller! - Remove newData option for mocked responses passed to MockLink or the mocks option on MockedProvider. This option was undocumented and was nearly identical to using the result option as a callback.

    To replicate the old behavior of newData, use result as a callback and add the maxUsageCount option with a value set to Number.POSITIVE_INFINITY.

    with MockLink

    new MockLink([
      {
        request: { query, variables },
    -   newData: (variables) => ({ data: { greeting: "Hello " + variables.greeting } }),
    +   result: (variables) => ({ data: { greeting: "Hello " + variables.greeting } }),
    +   maxUsageCount: Number.POSITIVE_INFINITY,
      }
    ])
    

    with MockedProvider

    <MockedProvider
      mocks={[
        {
          request: { query, variables },
    -     newData: (variables) => ({ data: { greeting: "Hello " + variables.greeting } }),
    +     result: (variables) => ({ data: { greeting: "Hello " + variables.greeting } }),
    +     maxUsageCount: Number.POSITIVE_INFINITY,
        }
      ]}
    />
    

Minor Changes

  • #12532 ae0dcad Thanks @jerelmiller! - Allow mocked responses passed to MockLink to accept a callback for the delay option. The delay callback will be given the current operation which can be used to determine what delay should be used for the mock.

  • #12532 ae0dcad Thanks @jerelmiller! - Introduce a new realisticDelay helper function for use with the delay callback for mocked responses used with MockLink. realisticDelay will generate a random value between 20 and 50ms to provide an experience closer to unpredictable network latency. realisticDelay can be configured with a min and max to set different thresholds if the defaults are not sufficient.

    import { realisticDelay } from "@apollo/client/testing";
    
    new MockLink([
      {
        request: { query },
        result: { data: { greeting: "Hello" } },
        delay: realisticDelay(),
      },
      {
        request: { query },
        result: { data: { greeting: "Hello" } },
        delay: realisticDelay({ min: 10, max: 100 }),
      },
    ]);
  • #12532 ae0dcad Thanks @jerelmiller! - Add ability to specify a default delay for all mocked responses passed to MockLink. This delay can be configured globally (all instances of MockLink will use the global defaults), or per-instance (all mocks in a single instance will use the defaults). A delay defined on a single mock will supercede all default delays. Per-instance defaults supercede global defaults.

    Global defaults

    MockLink.defaultOptions = {
      // Use a default delay of 20ms for all mocks in all instances without a specified delay
      delay: 20,
    
      // altenatively use a callback which will be executed for each mock
      delay: () => getRandomNumber(),
    
      // or use the built-in `realisticDelay`. This is the default
      delay: realisticDelay(),
    };

    Per-instance defaults

    new MockLink(
      [
        // Use the default delay
        {
          request: { query },
          result: { data: { greeting: "Hello" } },
        },
        {
          request: { query },
          result: { data: { greeting: "Hello" } },
          // Override the default for this mock
          delay: 10,
        },
      ],
      {
        defaultOptions: {
          // Use a default delay of 20ms for all mocks without a specified delay
          delay: 20,
    
          // altenatively use a callback which will be executed for each mock
          delay: () => getRandomNumber(),
    
          // or use the built-in `realisticDelay`. This is the default
          delay: realisticDelay(),
        },
      }
    );

Patch Changes

  • #12540 0098932 Thanks @phryneas! - Refactor: Move notification scheduling logic from QueryInfo to ObservableQuery

  • #12540 0098932 Thanks @phryneas! - Refactored cache emit logic for ObservableQuery. This should be an invisible change.

Apr 4, 2025

Patch Changes

  • #12285 cdc55ff Thanks @phryneas! - keep ObservableQuery created by useQuery non-active before it is first subscribed
Apr 3, 2025

Major Changes

  • #12513 9c3207c Thanks @phryneas! - Removed the @apollo/client/react/context and @apollo/client/react/hooks entry points. Please use @apollo/client/react instead.

  • #12513 9c3207c Thanks @phryneas! - Removed the @apollo/client/react/parser entry point. There is no replacement.

Patch Changes

  • #12513 9c3207c Thanks @phryneas! - Removed the parser cache. The functionality has been replaced in a way that doesn't need caching.
Apr 1, 2025

Major Changes

  • #12485 d338303 Thanks @jerelmiller! - Throw an error for queries and mutations if the link chain completes without emitting a value.

  • #12484 9a8b9ce Thanks @jerelmiller! - Remove loading, networkStatus, and partial properties on all promise-based query APIs. These properties were mostly static and were unnecessary since promise resolution guaranteed that the query was not longer loading.

    This affects the following APIs:

    • client.query
    • client.refetchQueries
    • client.reFetchObservableQueries
    • client.resetStore
    • observableQuery.fetchMore
    • observableQuery.refetch
    • observableQuery.reobserve
    • observableQuery.setVariables
    • The useLazyQuery execute function

Minor Changes

  • #12497 ff2cbe1 Thanks @jerelmiller! - Add a data property to CombinedGraphQLErrors that captures any partial data returned by the GraphQL response when errors are also returned.

  • #12488 c98b633 Thanks @phryneas! - Add a new method for static SSR of React components, prerenderStatic. The old methods, getDataFromTree, getMarkupFromTree and renderToStringWithData have been deprecated in favor of prerenderStatic.

    If used with React 19 and the prerender or prerenderToNodeStream apis from react-dom/static, this method can now be used to SSR-prerender suspense-enabled hook APIs.

Mar 31, 2025

Major Changes

  • #12478 5ea6a45 Thanks @jerelmiller! - Remove variables from the result returned from useSubscription.

  • #12476 6afff60 Thanks @jerelmiller! - Subscriptions now emit a SubscribeResult instead of a FetchResult. As a result, the errors field has been removed in favor of error.

  • #12475 3de63eb Thanks @jerelmiller! - Unify error behavior on mutations for GraphQL errors and network errors by ensuring network errors are subject to the errorPolicy. Network errors created when using an errorPolicy of all will now resolve the promise and be returned on the error property of the result, or stripped away when the errorPolicy is none.

  • #12475 3de63eb Thanks @jerelmiller! - client.mutate now returns a MutateResult instead of FetchResult. As a result, the errors property has been removed in favor of error which is set if either a network error occured or GraphQL errors are returned from the server.

    useMutation now also returns a MutateResult instead of a FetchResult.

  • #12475 3de63eb Thanks @jerelmiller! - Mutations no longer report errors if the GraphQL result from the server contains an empty array of errors.

  • #12476 6afff60 Thanks @jerelmiller! - Unify error behavior on subscriptions for GraphQL errors and network errors by ensuring network errors are subject to the errorPolicy. Network errors that terminate the connection will now be emitted on the error property passed to the next callback followed by a call to the complete callback.

  • #12478 5ea6a45 Thanks @jerelmiller! - Remove deprecated onSubscriptionData and onSubscriptionComplete callbacks from useSubscription. Use onData and onComplete instead.

  • #12476 6afff60 Thanks @jerelmiller! - GraphQL errors or network errors emitted while using an errorPolicy of ignore in subscriptions will no longer emit a result if there is no data emitted along with the error.

  • #12476 6afff60 Thanks @jerelmiller! - Subscriptions no longer emit errors in the error callback and instead provide errors on the error property on the result passed to the next callback. As a result, errors will no longer automatically terminate the connection allowing additional results to be emitted when the connection stays open.

    When an error terminates the downstream connection, a next event will be emitted with an error property followed by a complete event instead.

Minor Changes

Patch Changes

  • #12487 b695e5e Thanks @phryneas! - useQuery with ssr: false - previously, skip had a higher priortity than ssr: false while ssr: false had a higher priority than fetchPolicy: "standby" (which is roughly equivalent to skip).

    This priority has been adjusted so now both skip and fetchPolicy: "standby" have a higher priority than ssr: false and will return loading: false, while ssr: false will only come after those and will return loading: true if those are not set.

  • #12475 3de63eb Thanks @jerelmiller! - Fix an issue where passing onError to useMutation would resolve the promise returned by the mutate function instead of rejecting when using an errorPolicy of none.

  • #12475 3de63eb Thanks @jerelmiller! - Fix an issue where additional response properties were returned on the result returned from client.mutate, such as @defer payload fields. These properties are now stripped out to correspond to the TypeScript type.

Mar 24, 2025

Major Changes

  • #12463 3868df8 Thanks @jerelmiller! - ObservableQuery.setOptions has been removed as it was an alias of reobserve. Prefer using reobserve directly instead.

    const observable = client.watchQuery(options);
    
    // Use reobserve to set new options and reevaluate the query
    - observable.setOptions(newOptions);
    + observable.reobserve(newOptions);
    

    As a result of this change, reobserve has been marked for public use and is no longer considered an internal API. The newNetworkStatus argument has been removed to facilitate this change.

  • #12470 d32902f Thanks @phryneas! - ssrMode, ssrForceFetchDelay and disableNetworkFetches have been reworked:

    Previously, a ObservableQuery created by client.query or client.watchQuery while one of those were active would permanently be changed from a fetchPolicy of "network-only" or "cache-and-network" to "cache-first", and stay that way even long after disableNetworkFetches would have been deactivated.

    Now, the ObservableQuery will keep their original fetchPolicy, but queries made during disableNetworkFetches will just apply the fetchPolicy replacement at request time, just for that one request.

    ApolloClient.disableNetworkFetches has been renamed to ApolloClient.prioritizeCacheValues to better reflect this behaviour.

  • #12465 a132163 Thanks @jerelmiller! - Flatten out React hook types. As a result, the base types have been removed. Prefer using the hook types instead. Removed types include:

    • BaseMutationOptions
    • BaseQueryOptions
    • BaseSubscriptionOptions
    • ObservableQueryFields
    • MutationSharedOptions
    • QueryFunctionOptions
  • #12463 3868df8 Thanks @jerelmiller! - useQuery no longer returns reobserve as part of its result. It was possible to use reobserve to set new options on the underlying ObservableQuery instance which differed from the options passed to the hook. This could result in unexpected results. Instead prefer to rerender the hook with new options.

Patch Changes

  • #12465 a132163 Thanks @jerelmiller! - Rename all React hook result types and options. These types have all moved under a namespace that matches the hook name. For example, useQuery exports useQuery.Options and useQuery.Result types. As such, the old hook types have been deprecated and will be removed in v5.
Mar 20, 2025

Major Changes

  • #12457 32e85ea Thanks @jerelmiller! - Network errors triggered by queries now adhere to the errorPolicy. This means that GraphQL errors and network errors now behave the same way. Previously promise-based APIs, such as client.query, would reject the promise with the network error even if errorPolicy was set to ignore. The promise is now resolved with the error property set to the network error instead.

  • #12464 0595f39 Thanks @jerelmiller! - Remove the called property from useQuery.

Patch Changes

  • #12461 12c8d06 Thanks @jerelmiller! - Fix an issue where a cache-first query would return the result for previous variables when a cache update is issued after simultaneously changing variables and skipping the query.
Mar 19, 2025

Major Changes

  • #12450 876d070 Thanks @jerelmiller! - Remove TSerialized generic argument to ApolloCache. The ApolloCache base cache abstraction now returns unknown for cache.extract which can be overridden by a cache subclass.

  • #12450 876d070 Thanks @jerelmiller! - Remove the TCacheShape generic argument to ApolloClient. client.extract() now returns unknown by default. You will either need to type-cast this to the expected serialized shape, or use the cache.extract() directly from the subclass to get more specific types.

  • #12446 ab920d2 Thanks @jerelmiller! - Removes the defaultOptions option from useQuery. Use options directly or use the global ApolloClient defaultOptions.

  • #12442 c5ead08 Thanks @jerelmiller! - Remove the deprecated canonizeResults option. It was prone to memory leaks. As such, some results that were referentially equal when canonizeResults option was set to true no longer retain the same object identity.

  • #12442 c5ead08 Thanks @jerelmiller! - Remove resetResultIdentities option from InMemoryCache.gc(). This affected object canonization which has been removed.

  • #12451 77e1b13 Thanks @jerelmiller! - Default the TData generic type to unknown in all APIs that use a TData generic argument such as useQuery, client.query, etc.

Patch Changes

  • #12451 77e1b13 Thanks @jerelmiller! - Default TVariables generic type to OperationVariables instead of any throughout the client in areas that did not yet have the default as such.

  • #12454 925548a Thanks @phryneas! - Fix up the 4.0 CommonJS build

Mar 14, 2025

Major Changes

  • #12433 b86e50b Thanks @phryneas! - Remove workarounds for streaming with non-WhatWG response bodies to reduce bundle size.

    This removes support for fetch implementations that return Node Streams, Async Iterators or Blob instances as Response.body.

    In the WhatWG Fetch specification, Response.body is specified as a WhatWG ReadableStream.

    At this point in time, this is natively supported in browsers, node and React Native (via react-native-fetch-api, see our setup instructions for React Native).

    If you are using an older fetch polyfill that deviates from the spec, this might not be compatible - for example, node-fetch returns a node Readable instead of a ReadableStream. In those cases, please switch to a compatible alternative such as the node-native fetch, or undici.

Minor Changes

  • #12438 5089516 Thanks @phryneas! - Drop rehackt dependency. We can now directly import from react without causing build errors in RSC.

  • #12437 4779dc7 Thanks @phryneas! - Remove polyfills for Object.freeze,seal and preventExtensions in React Native

    These polyfills were only necessary until React Native 0.59, which patched the problem on the React Native side.

    With React Native 0.61, the Map function was completely replaced with a native implementation that never had the problems we guarded against.

  • #12438 5089516 Thanks @phryneas! - Add react-server entry point with stubs for normal exports.

Mar 13, 2025

Major Changes

  • #12384 6aa6fd3 Thanks @jerelmiller! - Remove the asyncMap utility function. Instead use one of the RxJS operators that creates Observables from promises, such as from.

  • #12398 8cf5077 Thanks @jerelmiller! - Removes the isApolloError utility function to check if the error object is an ApolloError instance. Use instanceof to check for more specific error types that replace ApolloError.

  • #12379 ef892b4 Thanks @jerelmiller! - Removes the addTypename option from InMemoryCache and MockedProvider. __typename is now always added to the outgoing query document when using InMemoryCache and cannot be disabled.

    If you are using <MockedProvider /> with addTypename={false}, ensure that your mocked responses include a __typename field. This will ensure cache normalization kicks in and behaves more like production.

  • #12396 00f3d0a Thanks @jerelmiller! - Remove the deprecated errors property from useQuery and useLazyQuery. Read errors from the error property instead.

  • #12222 d1a9054 Thanks @jerelmiller! - Drop support for React 16.

  • #12376 a0c996a Thanks @jerelmiller! - Remove deprecated ignoreResults option from useMutation. If you don't want to synchronize component state with the mutation, use useApolloClient to access your client instance and use client.mutate directly.

  • #12384 6aa6fd3 Thanks @jerelmiller! - Unusubscribing from ObservableQuery while a request is in flight will no longer terminate the request by unsubscribing from the link observable.

  • #12367 e6af35e Thanks @jerelmiller! - The previousData property on useLazyQuery will now change only when data changes. Previously previousData would change to the same value as data while the query was loading.

  • #12224 51e6c0f Thanks @jerelmiller! - Remove deprecated partialRefetch option.

  • #12407 8b1390b Thanks @jerelmiller! - Calling refetch with new variables will now set the networkStatus to refetch instead of setVariables.

  • #12384 6aa6fd3 Thanks @jerelmiller! - Remove the iterateObserversSafely utility function.

  • #12398 8cf5077 Thanks @jerelmiller! - Apollo Client no longer wraps errors in ApolloError. ApolloError has been replaced with separate error classes depending on the cause of the error. As such, APIs that return an error property have been updated to use the generic Error type. Use instanceof to check for more specific error types.

    Migration guide

    ApolloError encapsulated 4 main error properties. The type of error would determine which property was set:

    • graphqlErrors - Errors returned from the errors field by the GraphQL server
    • networkError - Any non-GraphQL error that caused the query to fail
    • protocolErrors - Transport-level errors that occur during multipart HTTP subscriptions
    • clientErrors - A space to define custom errors. Mostly unused.

    These errors were mutally exclusive, meaning both networkError and graphqlErrors were never set simultaneously. The following replaces each of these fields from ApolloError.

    graphqlErrors

    GraphQL errors are now encapsulated in a CombinedGraphQLErrors instance. You can access the raw GraphQL errors via the errors property.

    import { CombinedGraphQLErrors } from "@apollo/client";
    
    // ...
    
    const { error } = useQuery(query);
    
    if (error && error instanceof CombinedGraphQLErrors) {
      console.log(error.errors);
    }

    networkError

    Network errors are no longer wrapped and are instead passed through directly.

    const client = new ApolloClient({
      link: new ApolloLink(() => {
        return new Observable((observer) => {
          observer.error(new Error("Test error"));
        });
      }),
    });
    
    // ...
    
    const { error } = useQuery(query);
    
    // error is `new Error('Test error')`;

    protocolErrors

    Protocol errors are now encapsulated in a CombinedProtocolErrors instance. You can access the raw protocol errors via the errors property.

    import { CombinedProtocolErrors } from "@apollo/client";
    
    // ...
    
    const { error } = useSubscription(subscription);
    
    if (error && error instanceof CombinedProtocolErrors) {
      console.log(error.errors);
    }

    clientErrors

    These were unused by the client and have no replacement. Any non-GraphQL or non-protocol errors are now passed through unwrapped.

    Strings as errors

    If the link sends a string error, Apollo Client will wrap this in an Error instance. This ensures error properties are guaranteed to be of type Error.

    const client = new ApolloClient({
      link: new ApolloLink(() => {
        return new Observable((observer) => {
          // Oops we sent a string instead of wrapping it in an `Error`
          observer.error("Test error");
        });
      }),
    });
    
    // ...
    
    const { error } = useQuery(query);
    
    // The error string is wrapped and returned as `new Error('Test error')`;

    Non-error types

    If the link chain sends any other object type as an error, Apollo Client will wrap this in an UnknownError instance with the cause set to the original object. This ensures error properties are guaranteed to be of type Error.

    const client = new ApolloClient({
      link: new ApolloLink(() => {
        return new Observable((observer) => {
          observer.error({ message: "Not a proper error type" });
        });
      }),
    });
    
    // ...
    
    const { error } = useQuery(query);
    
    // error is an `UnknownError` instance. error.cause returns the original object.
  • #12384 6aa6fd3 Thanks @jerelmiller! - Remove fromError utility function. Use throwError instead.

  • #12211 c2736db Thanks @jerelmiller! - Remove the deprecated graphql, withQuery, withMutation, withSubscription, and withApollo hoc components. Use the provided React hooks instead.

  • #12262 10ef733 Thanks @jerelmiller! - Remove itAsync test utility.

  • #12398 8cf5077 Thanks @jerelmiller! - Updates the ServerError and ServerParseError types to be proper Error subclasses. Perviously these were plain Error intances with additional properties added at runtime. All properties are retained, but instanceof checks now work correctly.

    import { ServerError, ServerParseError } from "@apollo/client";
    
    if (error instanceof ServerError) {
      // ...
    }
    
    if (error instanceof ServerParseError) {
      // ...
    }
  • #12367 e6af35e Thanks @jerelmiller! - useLazyQuery no longer supports SSR environments and will now throw if the execute function is called in SSR. If you need to run a query in an SSR environment, use useQuery instead.

  • #12367 e6af35e Thanks @jerelmiller! - The execute function returned from useLazyQuery now only supports the context and variables options. This means that passing options supported by the hook no longer override the hook value.

    To change options, rerender the component with new options. These options will take effect with the next query execution.

  • #12384 6aa6fd3 Thanks @jerelmiller! - ObservableQuery will no longer terminate on errors and will instead emit a next value with an error property. This ensures that ObservableQuery instances can continue to receive updates after errors are returned in requests without the need to resubscribe to the observable.

  • #12398 8cf5077 Thanks @jerelmiller! - Removes the throwServerError utility function. Now that ServerError is an Error subclass, you can throw these errors directly:

    import { ServerError } from "@apollo/client";
    
    // instead of
    throwServerError(response, result, "error message");
    
    // Use
    throw new ServerError("error message", { response, result });
  • #12304 86469a2 Thanks @jerelmiller! - The Cache.DiffResult<T> type is now a union type with better type safety for both complete and partial results. Checking diff.complete will now narrow the type of result depending on whether the value is true or false.

    When true, diff.result will be a non-null value equal to the T generic type. When false, diff.result now reports result as DeepPartial<T> | null indicating that fields in the result may be missing (DeepPartial<T>) or empty entirely (null).

  • #12396 00f3d0a Thanks @jerelmiller! - Remove the errors property from the results emitted from ObservableQuery or returned from client.query. Read errors from the error property instead.

  • #12367 e6af35e Thanks @jerelmiller! - The result resolved from the promise returned from the execute function in useLazyQuery is now an ApolloQueryResult type and no longer includes all the fields returned from the useLazyQuery hook tuple.

    If you need access to the additional properties such as called, refetch, etc. not included in ApolloQueryResult, read them from the hook instead.

  • #12367 e6af35e Thanks @jerelmiller! - useLazyQuery will no longer rerender with the loading state when calling the execute function the first time unless the notifyOnNetworkStatusChange option is set to true (which is the new default).

    If you prefer the behavior from 3.x, rerender the component with notifyOnNetworkStatusChange set to false after the execute function is called the first time.

    function MyComponent() {
      const [notifyOnNetworkStatusChange, setNotifyOnNetworkStatusChange] =
        useState(true);
      const [execute] = useLazyQuery(query, { notifyOnNetworkStatusChange });
    
      async function runExecute() {
        await execute();
    
        // Set to false after the initial fetch to stop receiving notifications
        // about changes to the loading states.
        setNotifyOnNetworkStatusChange(false);
      }
    
      // ...
    }
  • #12254 0028ac0 Thanks @jerelmiller! - Changes the default Accept header to application/graphql-response+json.

  • #12430 2ff66d0 Thanks @jerelmiller! - ObservableQuery.setVariables will now resolve with the last emitted result instead of undefined when either the variables match the current variables or there are no subscribers to the query.

  • #12385 cad5117 Thanks @phryneas! - Apollo Client now defaults to production mode, not development mode, if the environment cannot be determined.

    In modern bundlers, this should automatically be handled by the bundler loading the bundler with the development export condition.

    If neither the production nor the development export condition are used by the bundler/runtime, Apollo Client will fall back to globalThis.__DEV__ to determine if it should run in production or development mode.

    Unlike Apollo Client 3 though, if globalThis.__DEV__ is not set to true, Apollo Client will now default to production, not to development, behaviour.

    This switch to explicilty requiring true also resolves a situation where an HTML element with id="__DEV__" would create a global __DEV__ variable with a referent to the DOM element, which in the past was picked up as "truthy" and would have triggered development mode.

  • #12367 e6af35e Thanks @jerelmiller! - The reobserve option is no longer available in the result returned from useLazyQuery. This was considered an internal API and should not be used directly.

  • #12333 3e4beaa Thanks @jerelmiller! - Fix type of data property on ApolloQueryResult. Previously this field was non-optional, non-null TData, however at runtime this value could be set to undefined. This field is now reported as TData | undefined.

    This will affect you in a handful of places:

    • The data property emitted from the result passed to the next callback from client.watchQuery
    • Fetch-based APIs that return an ApolloQueryResult type such as observableQuery.refetch, observableQuery.fetchMore, etc.
  • #12367 e6af35e Thanks @jerelmiller! - The promise returned when calling the execute function from useLazyQuery will now reject when using an errorPolicy of none when GraphQL errors are returned from the result.

  • #12223 69c1cb6 Thanks @jerelmiller! - Remove subscribeAndCount testing utility from @apollo/client/testing.

  • #12300 4d581e4 Thanks @jerelmiller! - Moves all React-related exports to the @apollo/client/react entrypoint and out of the main @apollo/client entrypoint. This prevents the need to install React in order to use the core client.

    The following is a list of exports available in @apollo/client that should now import from @apollo/client/react.

    • ApolloConsumer
    • ApolloProvider
    • createQueryPreloader
    • getApolloContext
    • skipToken
    • useApolloClient
    • useBackgroundQuery
    • useFragment
    • useLazyQuery
    • useLoadableQuery
    • useMutation
    • useQuery
    • useQueryRefHandlers
    • useReactiveVar
    • useReadQuery
    • useSubscription
    • useSuspenseQuery

    The following is a list of exports available in @apollo/client/testing that should now import from @apollo/client/testing/react:

    • MockedProvider
  • #12428 abed922 Thanks @jerelmiller! - Removes the urql multipart subscriptions utilities. Use the native multipart subscriptions support in urql instead.

  • #12384 6aa6fd3 Thanks @jerelmiller! - Switch to RxJS as the observable implementation. rxjs is now a peer dependency of Apollo Client which means you will now need to install rxjs in addition to @apollo/client.

    This change is mostly transparent, however transforming values on observables, common in link implementations, differs in RxJS vs zen-observable. For example, you could modify values in the link chain emitted from a downstream link by using the .map function. In RxJS, this is done with the .pipe function and passing a map operator instead.

    import { map } from "rxjs";
    
    const link new ApolloLink((operation, forward) => {
      return forward(operation).pipe(
        map((result) => performTransform(result))
      );
    });

    For a full list of operators and comprehensive documentation on the capabilities of RxJS, check out the documentation.

  • #12329 61febe4 Thanks @phryneas! - Rework package publish format (#12329, #12382)

    We have reworked the way Apollo Client is packaged.

    • shipping ESM and CJS
    • fixing up source maps
    • the build targets a modern runtime environment (browserslist query: "since 2023, node >= 20, not dead")
    • removed the "proxy directory" package.json files, e.g. cache/core/package.json and react/package.json. While these helped with older build tools, modern build tooling uses the exports field in the root package.json instead and the presence of these files can confuse modern build tooling. If your build tooling still relies on those, please update your imports to import from e.g. @apollo/client/cache/core/index.js instead of @apollo/client/cache/core - but generally, this should not be necessary.
    • added an exports field to package.json to expose entry points
    • instead of globalThis.__DEV__, Apollo Client now primarily relies on the development and production exports conditions. It falls back to globalThis.__DEV__ if the bundler doesn't know these, though.
  • #12397 2545a54 Thanks @jerelmiller! - Remove ObservableQuery.resetQueryStoreErrors method. This method reset some internal state that was not consumed elsewhere in the client and resulted in a no-op.

  • #12384 6aa6fd3 Thanks @jerelmiller! - Remove fromPromise utility function. Use from instead.

  • #12388 0d825be Thanks @jerelmiller! - Require environments that support WeakMap, WeakSet and symbols. Apollo Client would fallback to Map and Set if the weak versions were not available. This has been removed and expects that these features are available in the source environment.

    If you are running in an environment without WeakMap, WeakSet or symbols, you will need to find appropriate polyfills.

  • #12367 e6af35e Thanks @jerelmiller! - useLazyQuery no longer supports calling the execute function in render and will now throw. If you need to execute the query immediately, use useQuery instead or move the call to a useEffect.

  • #12367 e6af35e Thanks @jerelmiller! - The defaultOptions and initialFetchPolicy options are no longer supported with useLazyQuery.

    If you use defaultOptions, pass those options directly to the hook instead. If you use initialFetchPolicy, use fetchPolicy instead.

  • #12367 e6af35e Thanks @jerelmiller! - useLazyQuery no longer supports variables in the hook options and therefore no longer performs variable merging. The execute function must now be called with variables instead.

    function MyComponent() {
      const [execute] = useLazyQuery(query);
    
      function runExecute() {
        execute({ variables: { ... }});
      }
    }

    This change means the execute function returned from useLazyQuery is more type-safe. The execute function will require you to pass a variables option if the query type includes required variables.

  • #12304 86469a2 Thanks @jerelmiller! - ### Changes for users of InMemoryCache

    cache.diff now returns null instead of an empty object ({}) when returnPartialData is true and the result is empty.

    If you use cache.diff directly with returnPartialData: true, you will need to check for null before accessing any other fields on the result property. A non-null value indicates that at least one field was present in the cache for the given query document.

    Changes for third-party cache implementations

    The client now expects cache.diff to return null instead of an empty object when there is no data that can be fulfilled from the cache and returnPartialData is true. If your cache implementation returns an empty object, please update this to return null.

  • #12430 2ff66d0 Thanks @jerelmiller! - Removes ObservableQuery.result() method. If you use this method and need similar functionality, use the firstValueFrom helper in RxJS.

    import { firstValueFrom, from } from "rxjs";
    
    // The `from` is necessary to turn `observableQuery` into an RxJS observable
    const result = await firstValueFrom(from(observableQuery));
  • #12359 ebb4d96 Thanks @jerelmiller! - Remove the onCompleted and onError callbacks from useQuery and useLazyQuery.

    See #12352 for more context on this change.

  • #12384 6aa6fd3 Thanks @jerelmiller! - Subscriptions are no longer eagerly started after calling client.subscribe. To kick off the subscription, you will now need to subscribe to the returned observable.

    // Subscriptions are no longer started when calling subscribe on its own.
    const subscriptionObservable = client.subscribe(...);
    
    // Instead, subscribe to the returned observable to kick off the subscription.
    subscriptionObservable.subscribe({
      next: (value) => console.log(value)
    });
  • #12367 e6af35e Thanks @jerelmiller! - useLazyQuery will now only execute the query when the execute function is called. Previously useLazyQuery would behave like useQuery after the first call to the execute function which means changes to options might perform network requests.

    You can now safely rerender useLazyQuery with new options which will take effect the next time you manually trigger the query.

  • #12384 6aa6fd3 Thanks @jerelmiller! - Remove toPromise utility function. Use firstValueFrom instead.

  • #12304 86469a2 Thanks @jerelmiller! - ### Changes for users of InMemoryCache

    cache.diff no longer throws when returnPartialData is set to false without a complete result. Instead, cache.diff will return null when it is unable to read a full cache result.

    If you use cache.diff directly with returnPartialData: false, remove the try/catch block and replace with a check for null.

    Changes for third-party cache implementations

    The client now expects cache.diff to return null instead of throwing when the cache returns an incomplete result and returnPartialData is false. The internal try/catch blocks have been removed around cache.diff. If your cache implementation throws for incomplete results, please update this to return null.

  • #12211 c2736db Thanks @jerelmiller! - Remove the deprecated Query, Mutation, and Subscription components. Use the provided React hooks instead.

Minor Changes

  • #12385 cad5117 Thanks @phryneas! - Apollo Client is no longer using ts-invariant, but ships with a modified variant of it.

    The existing export setLogVerbosity from @apollo/client is still available and now points to this new integration. In most cases, you should be using this export. It will no longer adjust the verbosity of ts-invariant and as such no longer influence other packages relying on ts-invariant.

    The new entry point @apollo/client/utilities/invariant now exports invariant, InvariantError and setVerbosity. (Note that these tools are mostly meant to be used by Apollo Client and libraries directly based on Apollo Client like the @apollo/client-integration-* packages.)

  • #12333 3e4beaa Thanks @jerelmiller! - Deprecate the partial flag on ApolloQueryResult and make it a non-optional property. Previously partial was only set conditionally if the result emitted was partial. This value is now available with all results that return an ApolloQueryResult.

Patch Changes

  • #12291 ae5d06a Thanks @phryneas! - Remove deprecated resetApolloContext export

  • #12402 903c3ef Thanks @jerelmiller! - Use an an empty object ({}) rather than an object with null prototype (Object.create(null)) in all areas that instantiate objects.

  • #12385 cad5117 Thanks @phryneas! - * dropped the deprecated DEV export from @apollo/client/utilities and @apollo/client/utilities/globals

    • moved the __DEV__ export from @apollo/client/utilities/globals to @apollo/client/utilities/environment
    • moved the invariant, newInvariantError and InvariantError exports from @apollo/client/utilities/globals to @apollo/client/utilities/invariant
  • #12432 c7c2f61 Thanks @phryneas! - ObservableQuery: implement the rxjs InteropObservable interface to ensure from(observableQuery) stays possible

  • #12385 cad5117 Thanks @phryneas! - @apollo/client, @apollo/client/core and @apollo/client/cache no longer export an empty Cache runtime object. This is meant to be a type-only namespace.

  • #12384 6aa6fd3 Thanks @jerelmiller! - Don't emit a partial cache result from cache-only queries when returnPartialData is false.

Mar 10, 2025

Patch Changes

  • #12420 fee9368 Thanks @jorenbroekema! - Use import star from rehackt to prevent issues with importing named exports from external CJS modules.
Mar 7, 2025

Patch Changes

  • #12362 f6d387c Thanks @jerelmiller! - Fixes an issue where calling observableQuery.getCurrentResult() when the errorPolicy was set to all would return the networkStatus as NetworkStatus.ready when there were errors returned in the result. This has been corrected to report NetworkStatus.error.

    This bug also affected the useQuery and useLazyQuery hooks and may affect you if you check for networkStatus in your component.

Mar 6, 2025

Patch Changes

  • #12409 6aa2f3e Thanks @phryneas! - To mitigate problems when Apollo Client ends up more than once in the bundle, some unique symbols were converted into Symbol.for calls.

  • #12392 644bb26 Thanks @Joja81! - Fixes an issue where the DeepOmit type would turn optional properties into required properties. This should only affect you if you were using the omitDeep or stripTypename utilities exported by Apollo Client.

  • #12404 4332b88 Thanks @jerelmiller! - Show NaN rather than converting to null in debug messages from MockLink for unmatched variables values.

Feb 14, 2025

Patch Changes

Latest
@apollo/client@4.1.7
Tracking Since
Feb 14, 2025
Last checked Apr 20, 2026