Skip to content

Releases: apollographql/apollo-client

@apollo/[email protected]

13 Jun 17:47
ddf196a

Choose a tag to compare

Pre-release

Major Changes

  • #12673 cee90ab Thanks @phryneas! - The includeExtensions option of HttpLink and BatchHttpLink now defaults
    to true.

    If includeExtensions is true, but extensions is not set or empty, extensions
    will not be included in outgoing requests.

  • #12673 cee90ab Thanks @phryneas! - The ApolloClient constructor options name and version that are used to
    configure the client awareness feature have moved onto a clientAwareness key.

    const client = new ApolloClient({
      // ..
    -  name: "my-app",
    -  version: "1.0.0",
    +  clientAwareness: {
    +    name: "my-app",
    +    version: "1.0.0",
    +  },
    });
  • #12690 5812759 Thanks @phryneas! - Aliasing any other field to __typename is now forbidden.

  • #12690 5812759 Thanks @phryneas! - Aliasing a field to an alias beginning with __ac_ is now forbidden - this namespace is now reserved for internal use.

  • #12673 cee90ab Thanks @phryneas! - Adds enhanced client awareness to the client.

    HttpLink and BatchHttpLink will now per default send information about the
    client library you are using in extensions.

    This could look like this:

    {
      "query": "query GetUser($id: ID!) { user(id: $id) { __typename id name } }",
      "variables": {
        "id": 5
      },
      "extensions": {
        "clientLibrary": {
          "name": "@apollo/client",
          "version": "4.0.0"
        }
      }
    }

    This feature can be disabled by passing enhancedClientAwareness: { transport: false } to your
    ApolloClient, HttpLink or BatchHttpLink constructor options.

Minor Changes

  • #12698 be77d1a Thanks @phryneas! - Adjusted the accept header for multipart requests according to the new GraphQL over HTTP spec with these changes:

    -multipart/mixed;boundary=graphql;subscriptionSpec=1.0,application/json
    +multipart/mixed;boundary=graphql;subscriptionSpec=1.0,application/graphql-response+json,application/json;q=0.9
    -multipart/mixed;deferSpec=20220824,application/json
    +multipart/mixed;deferSpec=20220824,application/graphql-response+json,application/json;q=0.9
  • #12673 cee90ab Thanks @phryneas! - Add the new ClientAwarenessLink.

    This link is already included in HttpLink and BatchHttpLink to enable the
    "client awareness" and "enhanced client awareness" features, but you can also use
    ClientAwarenessLink directly in your link chain to combine it with other
    terminating links.

    If you want to save the bundle size that ClientAwarenessLink adds to HttpLink
    and BatchHttpLink, you can use BaseHttpLink or BaseBatchHttpLink instead.
    These links come without the ClientAwarenessLink included.

    For example:

    import {
      ApolloClient,
    -  HttpLink,
    } from "@apollo/client";
    +import { BaseHttpLink } from "@apollo/client/link/http";
    
    const client = new ApolloClient({
    -  link: new HttpLink({
    +  link: new BaseHttpLink({
        uri,
      }),
      cache: new InMemoryCache(),
    });
  • #12698 be77d1a Thanks @phryneas! - Adds an accept option to HttpOptions that allows to add additional Accept headers to be merged in without overriding user-specified or default accept headers.

Patch Changes

  • #12673 cee90ab Thanks @phryneas! - Fixed a bug in PersistedQueryLink where the persistedQuery extension would still be sent after a PersistedQueryNotSupported if includeExtensions was enabled on HttpLink.

@apollo/[email protected]

10 Jun 18:03
7187540

Choose a tag to compare

Pre-release

Major Changes

  • #12686 dc4b1d0 Thanks @jerelmiller! - A @defer query that has not yet finished streaming is now considered loading and thus the loading flag will be true until the response has completed. A new NetworkStatus.streaming value has been introduced and will be set as the networkStatus while the response is streaming.

  • #12685 3b74800 Thanks @jerelmiller! - Remove the check and warning for cache.fragmentMatches when applying data masking. cache.fragmentMatches is a required API and data masking may crash when cache.fragmentMatches does not exist.

  • #12684 e697431 Thanks @jerelmiller! - Remove context from useLazyQuery hook options. If used, context must now be provided to the execute function. context will reset to {} if not provided as an option to execute.

@apollo/[email protected]

06 Jun 15:49
ad641e4

Choose a tag to compare

Pre-release

Major Changes

  • #12675 8f1d974 Thanks @phryneas! - ObservableQuery no longer has a queryId property.
    ApolloClient.getObservableQueries no longer returns a Map<string, ObservableQuery>, but a Set<ObservableQuery>.

  • #12647 a70fac6 Thanks @phryneas! - ObservableQuerys will now only be registered with the ApolloClient while they
    have subscribers.

    That means that ApolloClient.getObservableQueries and ApolloClient.refetchQueries
    will only be able to return/refetch queries that have at least one subscriber.

    This changes the previous meaning of active and inactive queries:

    • inactive queries are queries with a subscriber that are skipped from a
      React hook or have a fetchPolicy of standby
    • active queries are queries with at least one subscriber that are not skipped or in standby.

    ObservableQuerys without subscribers but with an active ongoing network request
    (e.g. caused by calling reobserve) will be handled as if they had a subscriber
    for the duration of the query.

  • #12678 91a876b Thanks @jerelmiller! - queryRefs created by preloadQuery no longer have a .toPromise() function. Instead preloadQuery now has a toPromise function that accepts a queryRef and will resolve when the underlying promise has been resolved.

    const queryRef = preloadQuery(query, options);
    
    - await queryRef.toPromise();
    + await preloadQuery.toPromise(queryRef);
  • #12647 a70fac6 Thanks @phryneas! - ApolloClient.stop() now cleans up more agressively to prevent memory leaks:

    • It will now unsubscribe all active ObservableQuery instances by emitting a completed event.
    • It will now reject all currently running queries with "QueryManager stopped while query was in flight".
    • It will remove all queryRefs from the suspense cache.

Minor Changes

  • #12647 a70fac6 Thanks @phryneas! - Added a new .stop function on ObservableQuery.
    Calling this method will unsubscribe all current subscribers by sending a complete event from the observable and tear down the ObservableQuery.

@apollo/[email protected]

05 Jun 17:13
ca2d63b

Choose a tag to compare

Pre-release

Major Changes

  • #12663 01512f2 Thanks @jerelmiller! - Unsubscribing from an ObservableQuery before a value has been emitted will remove the query from the tracked list of queries and will no longer be eligible for query deduplication.

Minor Changes

  • #12663 01512f2 Thanks @jerelmiller! - Subscriptions created by client.subscribe() can now be restarted. Restarting a subscription will terminate the connection with the link chain and recreate the request. Restarts also work across deduplicated subscriptions so calling restart on an observable who's request is deduplicated will restart the connection for each observable.

    const observable = client.subscribe({ query: subscription });
    
    // Restart the connection to the link
    observable.restart();
  • #12663 01512f2 Thanks @jerelmiller! - Deduplicating subscription operations is now supported. Previously it was possible to deduplicate a subscription only if the new subscription was created before a previously subscribed subscription emitted any values. As soon as a value was emitted from a subscription, new subscriptions would create new connections. Deduplication is now active for as long as a subscription connection is open (i.e. the source observable hasn't emitted a complete or error notification yet.)

    To disable deduplication and force a new connection, use the queryDeduplication option in context like you would a query operation.

    As a result of this change, calling the restart function returned from useSubscription will now restart the connection on deduplicated subscriptions.

@apollo/[email protected]

05 Jun 00:04
85dc598

Choose a tag to compare

Pre-release

Minor Changes

  • #12670 0a880ea Thanks @phryneas! - Provide a mechanism to override the DataMasking types.

    Up until now, our types Masked, MaskedDocumentNode, FragmentType, MaybeMasked and Unmasked would assume that you are stictly using the type output format of GraphQL Codegen.

    With this change, you can now modify the behaviour of those types if you use a different form of codegen that produces different types for your queries.

    A simple implementation that would override the Masked type to remove all fields starting with _ from a type would look like this:

    // your actual implementation of `Masked`
    type CustomMaskedImplementation<TData> = {
      [K in keyof TData as K extends `_${string}` ? never : K]: TData[K];
    };
    
    import { HKT } from "@apollo/client/utilities";
    // transform this type into a higher kinded type that can be evaulated at a later time
    interface CustomMaskedType extends HKT {
      arg1: unknown; // TData
      return: CustomMaskedImplementation<this["arg1"]>;
    }
    
    // create an "implementation interface" for the types you want to override
    export interface CustomDataMaskingImplementation {
      Masked: CustomMaskedType;
      // other possible keys: `MaskedDocumentNode`, `FragmentType`, `MaybeMasked` and `Unmasked`
    }

    then you would use that CustomDataMaskingImplementation interface in your project to extend the DataMasking interface exported by @apollo/client with it's functionality:

    declare module "@apollo/client" {
      export interface DataMasking extends CustomDataMaskingImplementation {}
    }

    After that, all internal usage of Masked in Apollo Client as well as all usage in your code base will use the new CustomMaskedType implementation.

    If you don't specify overrides, Apollo Client will still default to the GraphQL Codegen data masking implementation.
    The types for that are also explicitly exported as the GraphQLCodegenDataMasking namespace in @apollo/client/masking.

@apollo/[email protected]

03 Jun 15:43
3f58bf3

Choose a tag to compare

Pre-release

Major Changes

  • #12649 0be92ad Thanks @jerelmiller! - The TData generic provided to types that return a dataState property is now modified by the given DataState generic instead of passing a modified TData type. For example, a QueryRef that could return partial data was defined as QueryRef<DeepPartial<TData>, TVariables>. Now TData should be provided unmodified and a set of allowed states should be given instead: QueryRef<TData, TVariables, 'complete' | 'streaming' | 'partial'>.

    To migrate, use the following guide to replace your type with the right set of states (all types listed below are changed the same way):

    - QueryRef<TData, TVariables>
    // `QueryRef`'s default is 'complete' | 'streaming' so this can also be left alone if you prefer
    // All other types affected by this change default to all states
    + QueryRef<TData, TVariables>
    + QueryRef<TData, TVariables, 'complete' | 'streaming'>
    
    - QueryRef<TData | undefined, TVariables>
    + QueryRef<TData, TVariables, 'complete' | 'streaming' | 'empty'>
    
    - QueryRef<DeepPartial<TData>, TVariables>
    + QueryRef<TData, TVariables, 'complete' | 'streaming' | 'partial'>
    
    - QueryRef<DeepPartial<TData> | undefined, TVariables>
    + QueryRef<TData, TVariables, 'complete' | 'streaming' | 'partial' | 'empty'>

    The following types are affected. Provide the allowed dataState values to the TDataState generic:

    • ApolloQueryResult
    • QueryRef
    • PreloadedQueryRef
    • useLazyQuery.Result
    • useQuery.Result
    • useReadQuery.Result
    • useSuspenseQuery.Result

    All *QueryRef types default to complete | streaming states while the rest of the types default to 'complete' | 'streaming' | 'partial' | 'empty' states. You shouldn't need to provide the states unless you need to either allow for partial data/empty values (*QueryRef) or a restricted set of states.

  • #12649 0be92ad Thanks @jerelmiller! - Remove the deprecated QueryReference type. Please use QueryRef instead.

  • #12633 9bfb51f Thanks @phryneas! - If the execute function of useLazyQuery is executed, previously started queries
    from the same useLazyQuery usage will be rejected with an AbortError unless
    .retain() is called on the promise returned by previous execute calls.

    Please keep in mind that useLazyQuery is primarily meant as a means to synchronize
    your component to the status of a query and that it's purpose it not to make a
    series of network calls.
    If you plan on making a series of network calls without the need to synchronize
    the result with your component, consider using ApolloClient.query instead.

Minor Changes

  • #12633 9bfb51f Thanks @phryneas! - ObservableQuery.refetch and ObservableQuery.reobserve and the execute function of useLazyQuery now return a
    ResultPromise with an additional .retain method.
    If this method is called, the underlying network operation will be kept running even if the ObservableQuery itself does
    not require the result anymore, and the Promise will resolve with the final result instead of resolving with an intermediate
    result in the case of early cancellation.

  • #12649 0be92ad Thanks @jerelmiller! - Add a new dataState property that determines the completeness of the data property. dataState helps narrow the type of data. dataState is now emitted from ObservableQuery and returned from all React hooks that return a data property.

    The dataState values are:

    • empty: No data could be fulfilled from the cache or the result is incomplete. data is undefined.
    • partial: Some data could be fulfilled from the cache but data is incomplete. This is only possible when returnPartialData is true.
    • streaming: data is incomplete as a result of a deferred query and the result is still streaming in.
    • complete: data is a fully satisfied query result fulfilled either from the cache or network.

    Example:

    const { data, dataState } = useQuery<TData>(query);
    
    if (dataState === "empty") {
      expectTypeOf(data).toEqualTypeOf<undefined>();
    }
    
    if (dataState === "partial") {
      expectTypeOf(data).toEqualTypeOf<DeepPartial<TData>>();
    }
    
    if (dataState === "streaming") {
      expectTypeOf(data).toEqualTypeOf<TData>();
    }
    
    if (dataState === "complete") {
      expectTypeOf(data).toEqualTypeOf<TData>();
    }

@apollo/[email protected]

28 May 22:54

Choose a tag to compare

Pre-release

Major Changes

  • #12644 fe2f005 Thanks @jerelmiller! - Replace the result property on ServerError with bodyText. bodyText is set to the raw string body. HttpLink and BatchHttpLink no longer try and parse the response body as JSON when a ServerError is thrown.

  • #12644 fe2f005 Thanks @jerelmiller! - More strictly adhere to the GraphQL over HTTP spec. This change adds support for the application/graphql-response+json media type and modifies the behavior of the application/json media type.

    • The client will parse the response as a well-formed GraphQL response when the server encodes content-type using application/graphql-response+json with a non-200 status code.
    • The client will now throw a ServerError when the server encodes content-type using application/json and returns a non-200 status code.
    • The client will now throw a ServerError when the server encodes using any other content-type and returns a non-200 status code.

    NOTE: If you use a testing utility to mock requests in your test, you may experience different behavior than production if your testing utility responds as application/json but your production server responds as application/graphql-response+json. If a content-type header is not set, the client interprets the response as application/json.

  • #12644 fe2f005 Thanks @jerelmiller! - Change the default Accept header to application/graphql-response+json,application/json;q=0.9.

  • #12644 fe2f005 Thanks @jerelmiller! - HttpLink and BatchHttpLink no longer emit a next notification with the JSON-parsed response body when a well-formed GraphQL response is returned and a ServerError is thrown.

@apollo/[email protected]

28 May 22:54

Choose a tag to compare

Pre-release

Major Changes

  • #12617 ea633a1 Thanks @jerelmiller! - Introduce a new GraphQL Codegen plugin aimed at creating resolver types for LocalState. This plugin is similar to @graphql-codegen/typescript-resolvers but tailored to provide types that work with LocalState.

    To use the plugin, install @apollo/client-graphql-codegen and add the following to your codegen config:

    // codegen.ts
    
    const config: CodegenConfig = {
      // ...
      generates: {
        "./path/to/local/resolvers.ts": {
          schema: ["./path/to/localSchema.graphql"],
          plugins: ["typescript", "@apollo/client-graphql-codegen/local-state"],
          // ...
        },
      },
    };

    This will generate a Resolvers type in the generated file that can be used to provide type information to LocalState.

    import type { Resolvers } from "./path/to/resolvers-types.ts";
    
    const localState = new LocalState<Resolvers>({
      // ...
    });

    It is also recommended to add the following config:

    // codegen.ts
    import type { LocalStatePluginConfig } from "@apollo/client-graphql-codegen/local-state";
    
    const config: CodegenConfig = {
      // ...
      generates: {
        "./path/to/local/resolvers.ts": {
          config: {
            // Ensures you return a `__typename` for any `@client` fields that
            // return object or array types
            nonOptionalTypename: true,
    
            // Required if your localSchema extends existing schema types.
            baseTypesPath: "./relative/path/to/base/schema/types",
    
            // If you provide a `context` function to customize the context value,
            // provide the path or type here.
            contextType: "./path/to/contextValue#ContextValue",
          } satisfies LocalStatePluginConfig,
        },
      },
    };

    NOTE: It is recommended that the schema file passed to the schema option is your local schema, not your entire app schema in order to only generate resolver types for your local fields, otherwise the plugin will generate resolver types for your entire remote schema as well.

v4.0.0-alpha.15

23 May 22:30
1326540

Choose a tag to compare

v4.0.0-alpha.15 Pre-release
Pre-release

Major Changes

  • #12639 1bdf489 Thanks @jerelmiller! - Move internal testing utilities in @apollo/client/testing to @apollo/client/testing/internal and remove deprecated testing utilities. Some of the testing utilities exported from the @apollo/client/testing endpoint were not considered stable. As a result of this change, testing utilities or types exported from @apollo/client/testing are now considered stable and will not undergo breaking changes.

    The following APIs were removed. To migrate, update usages of the following APIs as such:

    createMockClient

    - const client = createMockClient(data, query, variables);
    + const client = new ApolloClient({
    +   cache: new InMemoryCache(),
    +   link: new MockLink([
    +     {
    +       request: { query, variables },
    +       result: { data },
    +     }
    +   ]),
    + });

    mockObservableLink

    - const link = mockObservableLink();
    + const link = new MockSubscriptionLink();

    mockSingleLink

    - const link = mockSingleLink({
    -   request: { query, variables },
    -   result: { data },
    - });
    + const link = new MockLink([
    +   {
    +     request: { query, variables },
    +     result: { data },
    +   }
    + ]);
  • #12637 d2a60d4 Thanks @phryneas! - useQuery: only advance previousData if data actually changed

  • #12631 b147cac Thanks @phryneas! - ObservableQuery will now return a loading: false state for fetchPolicy standby, even before subscription

  • #12639 1bdf489 Thanks @jerelmiller! - Remove the @apollo/client/testing/core entrypoint in favor of @apollo/client/testing.

Minor Changes

  • #12639 1bdf489 Thanks @jerelmiller! - Move MockLink types to MockLink namespace. This affects the MockedResponse, MockLinkOptions, and ResultFunction types. These types are still exported but are deprecated in favor of the namespace. To migrate, use the types on the MockLink namespace instead.

    import {
    - MockedResponse,
    - MockLinkOptions,
    - ResultFunction,
    + MockLink
    } from "@apollo/client/testing";
    
    - const mocks: MockedResponse = [];
    + const mocks: MockLink.MockedResponse = [];
    
    - const result: ResultFunction = () => {/* ... */ }
    + const result: MockLink.ResultFunction = () => {/* ... */ }
    
    - const options: MockLinkOptions = {}
    + const options: MockLink.Options = {}

Patch Changes

  • #12631 b147cac Thanks @phryneas! - When updating skip from false to true in useQuery, retain data if it is available rather than setting it to undefined.

  • #12631 b147cac Thanks @phryneas! - The error property is no longer present when skip is true in useQuery.

v4.0.0-alpha.14

21 May 17:12
12b26bf

Choose a tag to compare

v4.0.0-alpha.14 Pre-release
Pre-release

Major Changes

  • #12614 d2851e2 Thanks @jerelmiller! - The getCacheKey function is no longer available from operation.getContext() in the link chain. Use operation.client.cache.identify(obj) in the link chain instead.

  • #12556 c3fceda Thanks @phryneas! - ObservableQuery will now keep previous data around when emitting a loading state, unless query or variables changed.
    Note that @exports variables are not taken into account for this, so data will stay around even if they change.

  • #12556 c3fceda Thanks @phryneas! - Removed getLastResult, getLastError and resetLastResults from ObservableQuery

  • #12614 d2851e2 Thanks @jerelmiller! - Removes the resolvers option from ApolloClient. Local resolvers have instead been moved to the new LocalState instance which is assigned to the localState option in ApolloClient. To migrate, move the resolvers values into a LocalState instance and assign that instance to localState.

    new ApolloClient({
    - resolvers: { /* ... */ }
    + localState: new LocalState({
    +   resolvers: { /* ... */ }
    + }),
    });
  • #12614 d2851e2 Thanks @jerelmiller! - Remove local resolvers APIs from ApolloClient in favor of localState. Methods removed are:

    • addResolvers
    • getResolvers
    • setResolvers
    • setLocalStateFragmentMatcher
  • #12614 d2851e2 Thanks @jerelmiller! - Third-party caches must now implement the fragmentMatches API. Additionally fragmentMatches must be able to handle both InlineFragmentNode and FragmentDefinitionNode nodes.

    class MyCache extends ApolloCache {
      // This is now required
      public fragmentMatches(
        fragment: InlineFragmentNode | FragmentDefinitionNode,
        typename: string
      ): boolean {
        return; // ... logic to determine if typename matches fragment
      }
    }
  • #12556 c3fceda Thanks @phryneas! - Reworked the logic for then a loading state is triggered. If the link chain responds synchronously, a loading state will be omitted, otherwise it will be triggered.
    If local resolvers are used, the time window for "sync vs async" starts as soon as @exports variables are resolved.

  • #12556 c3fceda Thanks @phryneas! - Dropped the saveAsLastResult argument from ObservableQuery.getCurrentResult

  • #12614 d2851e2 Thanks @jerelmiller! - The resolver function's context argument (the 3rd argument) has changed to provide additional information without the possibility of name clashes. Previously the context argument would spread request context and override the client and cache properties to give access to both inside of a resolver. The context argument takes now takes the following shape:

    {
      // the request context. By default `TContextValue` is of type `DefaultContext`,
      // but can be changed if a `context` function is provided.
      requestContext: TContextValue,
      // The client instance making the request
      client: ApolloClient,
      // Whether the resolver is run as a result of gathering exported variables
      // or resolving the value as part of the result
      phase: "exports" | "resolve"
    }

    To migrate, pull any request context from requestContext and the cache from the client property:

    new LocalState({
      resolvers: {
        Query: {
    -     myResolver: (parent, args, { someValue, cache }) => {
    +     myResolver: (parent, args, { requestContext, client }) => {
    +       const someValue = requestContext.someValue;
    +       const cache = client.cache;
          }
        }
      }
    });
  • #12614 d2851e2 Thanks @jerelmiller! - Apollo Client no longer ships with support for @client fields out-of-the-box and now must be opt-in. To opt in to use @client fields, pass an instantiated LocalState instance to the localState option. If a query contains @client and local state hasn't been configured, an error will be thrown.

    import { LocalState } from "@apollo/client/local-state";
    
    new ApolloClient({
      localState: new LocalState(),
    });
  • #12614 d2851e2 Thanks @jerelmiller! - Remove the fragmentMatcher option from ApolloClient. Custom fragment matchers used with local state are no longer supported. Fragment matching is now performed by the configured cache via the cache.fragmentMatches API.

  • #12556 c3fceda Thanks @phryneas! - A call to ObservableQuery.setVariables with different variables or a ObservableQuery.refetch call will always now guarantee that a value will be emitted from the observable, even if it is deep equal to the previous value.

Minor Changes

  • #12614 d2851e2 Thanks @jerelmiller! - Revamp local resolvers and fix several issues from the existing resolvers option.

    • Throwing errors in a resolver will set the field value as null and add an error to the response's errors array.
    • Remote results are dealiased before they are passed as the parent object to a resolver so that you can access fields by their field name.
    • You can now specify a context function that you can use to customize the requestContext given to resolvers.
    • The LocalState class accepts a Resolvers generic that provides autocompletion and type checking against your resolver types to ensure your resolvers are type-safe.
    • data: null is now handled correctly and does not call your local resolvers when the server does not provide a result.
    • Additional warnings have been added to provide hints when resolvers behave unexpectedly.
    import { LocalState } from "@apollo/client/local-state";
    
    import { Resolvers } from "./path/to/local-resolvers-types.ts";
    
    // LocalState now accepts a `Resolvers` generic.
    const localState = new LocalState<Resolvers>({
      // The return value of this funciton
      context: (options) => ({
        // ...
      }),
      resolvers: {
        // ...
      },
    });
    
    // You may also pass a `ContextValue` generic used to ensure the `context`
    // function returns the correct type. This type is inferred from your resolvers
    // if not provided.
    new LocalState<Resolvers, ContextValue>({
      // ...
    });