Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
60 commits
Select commit Hold shift + click to select a range
c6a788d
Add note about typescript in queries doc
jerelmiller Aug 7, 2025
80c9200
Update intro section
jerelmiller Aug 7, 2025
4bb918f
Remove sections on render props/hoc
jerelmiller Aug 7, 2025
cc31f5a
Assume gql codegen
jerelmiller Aug 7, 2025
aeec186
Use npm
jerelmiller Aug 7, 2025
ae802ae
Tweak command
jerelmiller Aug 7, 2025
3e84bad
Use note
jerelmiller Aug 7, 2025
a606b1a
Update doc blocks on masking types
jerelmiller Aug 7, 2025
78db0c4
1st draft of hkt for masking types
jerelmiller Aug 7, 2025
aa0b9e7
2nd draft
jerelmiller Aug 7, 2025
b6bfed0
Add note about other type overrides
jerelmiller Aug 7, 2025
ccd8678
Update codegen section
jerelmiller Aug 7, 2025
6af9128
Move typed document node up. Add advanced configuration section
jerelmiller Aug 7, 2025
c0ef038
Add additional recommended setting
jerelmiller Aug 7, 2025
e8fa968
Add notes for TypeScript in queries and getting started
jerelmiller Aug 7, 2025
36e7f03
Fix typo
jerelmiller Aug 7, 2025
56a6073
Tweak sentence
jerelmiller Aug 7, 2025
497af91
Tweak comment
jerelmiller Aug 7, 2025
3fb4c25
Use contraction
jerelmiller Aug 7, 2025
65d9a71
Tweak note
jerelmiller Aug 7, 2025
a9c6060
Tweak example
jerelmiller Aug 7, 2025
81ba318
Overhaul typed document node section
jerelmiller Aug 8, 2025
0fdfe7e
Tweak sentence
jerelmiller Aug 8, 2025
73d5ded
Tweak reqs
jerelmiller Aug 8, 2025
70687c6
Add more to variables section
jerelmiller Aug 8, 2025
1966d47
Add section on mutations
jerelmiller Aug 8, 2025
d5ae6d4
Show type in example
jerelmiller Aug 8, 2025
9824c04
Add subscriptions section
jerelmiller Aug 8, 2025
c1c7dac
Remove imports in example to simplify
jerelmiller Aug 8, 2025
b89d54e
Add imports
jerelmiller Aug 8, 2025
1011e27
Fix highlight
jerelmiller Aug 8, 2025
4d36a04
Remove accidental add to wrong section
jerelmiller Aug 8, 2025
aba9f52
Update example
jerelmiller Aug 8, 2025
7afcc7f
Add note about HKT
jerelmiller Aug 14, 2025
de22455
Add section for adding gql codegen masking types in fragments
jerelmiller Aug 14, 2025
003cda0
Remove Masked/MaskedDocumentNode from list of overridable types
jerelmiller Aug 14, 2025
75da0cc
Rework example for HKT for masking types
jerelmiller Aug 14, 2025
f2a9fb1
Add section on providing type overrides
jerelmiller Aug 14, 2025
c7b4d45
Add section for defer
jerelmiller Aug 15, 2025
86ade45
Fix typo
jerelmiller Aug 15, 2025
f020d76
Update section for client preset
jerelmiller Aug 15, 2025
c52ad4f
Add emphasis
jerelmiller Aug 15, 2025
1ca769b
Tweak caution
jerelmiller Aug 15, 2025
71107e9
Split into separate paragraphs
jerelmiller Aug 15, 2025
21259dd
Add generics
jerelmiller Aug 15, 2025
c16a124
Move parens to part of other sentence
jerelmiller Aug 15, 2025
25e0eab
Fix typo
jerelmiller Aug 15, 2025
8473df2
AI suggestions
jerelmiller Aug 15, 2025
93908a3
Add note for typescript in mutations doc
jerelmiller Aug 15, 2025
40d46be
Update api report
jerelmiller Aug 15, 2025
13a4b4f
Update size limits
jerelmiller Aug 15, 2025
a10f486
Add section for defining context types
jerelmiller Aug 15, 2025
97f66b7
Add link back to TypeScript guide for managing context
jerelmiller Aug 15, 2025
60b1875
Fix typos
jerelmiller Aug 15, 2025
7a1f711
AI suggestions
jerelmiller Aug 15, 2025
ae41553
Fix grammar
jerelmiller Aug 15, 2025
674c2e5
Change headlines
jerelmiller Aug 18, 2025
6284ba4
Modify paragraph about defaults
jerelmiller Aug 18, 2025
c540604
Merge branch 'release-4.0' into jerel/ts-docs
jerelmiller Aug 18, 2025
16c243a
Clean up Prettier, Size-limit, and Api-Extractor
jerelmiller Aug 18, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions .api-reports/api-report-masking.api.md
Original file line number Diff line number Diff line change
Expand Up @@ -62,8 +62,8 @@ type ExtractByMatchingTypeNames<Union extends {

// Warning: (ae-forgotten-export) The symbol "PreserveTypes" needs to be exported by the entry point index.d.ts
//
// @public (undocumented)
export type FragmentType<TData> = ApplyHKTImplementationWithDefault<TypeOverrides, "FragmentType", PreserveTypes.TypeOverrides, TData>;
// @public
export type FragmentType<TFragmentData> = ApplyHKTImplementationWithDefault<TypeOverrides, "FragmentType", PreserveTypes.TypeOverrides, TFragmentData>;

// @public (undocumented)
export namespace GraphQLCodegenDataMasking {
Expand Down
4 changes: 2 additions & 2 deletions .api-reports/api-report.api.md
Original file line number Diff line number Diff line change
Expand Up @@ -1146,8 +1146,8 @@ interface FragmentRegistryAPI {

// Warning: (ae-forgotten-export) The symbol "PreserveTypes" needs to be exported by the entry point index.d.ts
//
// @public (undocumented)
export type FragmentType<TData> = ApplyHKTImplementationWithDefault<TypeOverrides, "FragmentType", PreserveTypes.TypeOverrides, TData>;
// @public
export type FragmentType<TFragmentData> = ApplyHKTImplementationWithDefault<TypeOverrides, "FragmentType", PreserveTypes.TypeOverrides, TFragmentData>;

// @public @deprecated (undocumented)
export const from: typeof ApolloLink.from;
Expand Down
6 changes: 6 additions & 0 deletions docs/source/api/link/introduction.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -517,6 +517,12 @@ const link = new OperationCountLink();

As an operation moves along the link chain, it maintains a `context` that each link can read and modify. This enables both links and request-based APIs (such as [`useQuery`](../react/useQuery)) to pass metadata along the chain that other links use in their execution logic. Context is not included in the terminating link's request to the GraphQL server or other destination.

<Note>

If your application is built using TypeScript, we recommend reading the [Defining context types](../../development-testing/static-typing#defining-context-types) guide to learn how to provide types for the context object.

</Note>

### Reading context

You get the current context object by calling `operation.getContext()` in a [request handler](#the-request-handler).
Expand Down
159 changes: 159 additions & 0 deletions docs/source/data/fragments.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -1286,6 +1286,31 @@ To migrate from CodeGen's fragment masking feature to Apollo Client's data maski

3. [Enable data masking](#enabling-data-masking) in Apollo Client.

#### Setting data masking types

By default, Apollo Client makes no modification to the operation types provided to its APIs, regardless of whether the type definitions are masked or unmasked. This provides a simpler upgrade path when you're ready to [incrementally adopt](#incremental-adoption-in-an-existing-application) data masking.

To use GraphQL Codegen's masking format with your operation types, you need to tell Apollo Client to use the associated GraphQL Codegen masking types. You do this by defining a [type override](../development-testing/static-typing#overriding-type-implementations-of-built-in-types) that uses the GraphQL Codegen masking format.

Create a TypeScript file that will be used to modify the `TypeOverrides` interface. Extend `TypeOverrides` with the `GraphQLCodegenDataMasking.TypeOverrides` interface.

```ts title="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";
import type { GraphQLCodegenDataMasking } from "@apollo/client/masking";

declare module "@apollo/client" {
interface TypeOverrides extends GraphQLCodegenDataMasking.TypeOverrides {}
}
```

<Note>

This example uses `apollo-client.d.ts` as the file name to make it easily identifiable. You can give this file any name.

</Note>

#### Using with fragments

When using [colocated fragments](#colocating-fragments) with your components, it's best to ensure the object passed to your component is done in a type-safe way. This means:
Expand Down Expand Up @@ -1519,3 +1544,137 @@ query GetPosts {
Repeat this process until all `@unmask` directives have been removed from your codebase.

Congratulations 🎉! Your application is now using data masking everywhere 😎.

### Defining your own masking types using higher-kinded types

<Note>

If you are using the [data masking types generated from GraphQL Codegen](#setting-data-masking-types), you can safely skip this section.

</Note>

Apollo Client provides an [integration](#setting-data-masking-types) with GraphQL Codegen's [fragment masking](https://the-guild.dev/graphql/codegen/plugins/presets/preset-client#fragment-masking) output and orients its masking utility types around this format. If you aren't using GraphQL Codegen to generate your types, or your types are generated in a different format, the type implementations built into Apollo Client might be incompatible which results in inaccurate types.

You can use a technique called _higher-kinded types_ (HKT) to provide your own type implementations for Apollo Client's masking types. You can think of higher-kinded types as a way to define types and interfaces with generics that can be filled in by Apollo Client internals at a later time. Passing around un-evaluated types is otherwise not possible in TypeScript.

Comment thread
phryneas marked this conversation as resolved.
#### Example

Let's add our own type overrides for the `MaybeMasked` and `Unmasked` utility types.

The `MaybeMasked` type is used throughout Apollo Client to return the masked or unmasked type definition for a given type, assuming they have data masking types enabled and are maskable. Otherwise, it returns the unmasked type that is passed in.

The `Unmasked` type unwraps a masked type to its full result type and is used throughout Apollo Client where the full data type is needed for a given API (e.g. `client.writeQuery`).

For this example, we'll assume the data masking types are enabled and the operation types are generated using two variations:

1. Data masked types are generated with a `__masked` virtual property. Its value is the operation type with any masked fields removed from the type.

```ts
type MaskedQuery = {
// The masked variant of the operation type is provided under the
// `__masked` virtual property
__masked?: { user: { __typename: "User"; id: number } };

// The full result type includes all other fields in the type
user: { __typename: "User"; id: number; name: string };
};
```

2. Unmasked types are generated as their full result type with no `__masked` virtual property applied to them.

```ts
type UnmaskedQuery = {
user: { __typename: "User"; id: number; name: string };
};
```

<Note>

This is a hypothetical format that doesn't exist in Apollo Client or any known code generation tool. This format is used specifically for this example to illustrate how to provide type overrides to Apollo Client.

</Note>

First, let's define our custom implementation of the `MaybeMasked` type. The implementation works by checking if the `__masking` virtual property exists on the type. If so, it returns the value on the `__masked` property as the type, otherwise it returns the input type unmodified.

```ts title="masked-types.ts"
type MaybeMasked<TData> =
TData extends { __masked?: infer TMaskedData } ? TMaskedData : TData;
```

Now let's provide an implementation for the `Unmasked` type that works in contrast to `MaybeMasked` by returning the unwrapped full result type. The implementation works by removing the `__masked` virtual property on the input type. This can be accomplished using the built-in [`Omit`](https://www.typescriptlang.org/docs/handbook/utility-types.html#omittype-keys) type.

```ts {4} title="masked-types.ts"
type MaybeMasked<TData> =
TData extends { __masked?: infer TMaskedData } ? TMaskedData : TData;

type Unmasked<TData> = Omit<TData, "__masked">;
```

Now that our custom type implementations are in place, we need to define higher-kinded types for each of these custom types. This provides the bridge needed by Apollo Client to use our custom type implementations. This is done by extending the `HKT` interface exported by `@apollo/client/utilities`.

Let's provide HKTs for our `MaybeMasked` and `Unmasked` types. We'll put these in the same file as our type implementations.

```ts {1,8-11,13-16} title="masked-types.ts"
import { HKT } from "@apollo/client/utilities";

type MaybeMasked<TData> =
TData extends { __masked?: infer TMaskedData } ? TMaskedData : TData;

type Unmasked<TData> = Omit<TData, "__masked">;

export interface MaybeMaskedHKT extends HKT {
arg1: unknown; // TData
return: MaybeMasked<this["arg1"]>;
}

export interface UnmaskedHKT extends HKT {
arg1: unknown; // TData
return: Unmasked<this["arg1"]>;
}
```

With our HKT types in place, we now need to tell Apollo Client about them.

Apollo Client uses [declaration merging](https://www.typescriptlang.org/docs/handbook/declaration-merging.html) to provide a hook point for overridable types. The `TypeOverrides` interface exported by the `@apollo/client` package is used for this purpose. Each property in the `TypeOverrides` interface corresponds to an overridable type in Apollo Client.

Let's add type overrides for our custom type implementations. Create a TypeScript file and define a `TypeOverrides` interface for the `@apollo/client` module.

```ts title=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" {
export interface TypeOverrides {
// Type overrides will go here
}
}
```

Now we'll import our HKT types and add them as keys in the `TypeOverrides` interface.

```ts {4,8-9} title=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";
import { MaybeMaskedHKT, UnmaskedHKT } from "./masked-types.ts";

declare module "@apollo/client" {
export interface TypeOverrides {
MaybeMasked: MaybeMaskedHKT;
Unmasked: UnmaskedHKT;
}
}
```

And that's it! Now when Apollo Client uses the `MaybeMasked` or `Unmasked` types in its APIs, our custom implementation will be used instead 🎉.

#### Available type overrides

The following masking utility types are available to override:

- `FragmentType<TFragmentData>` - Type used with [fragments](#using-with-fragments) to ensure parent objects contain the fragment spread from the type.
- `MaybeMasked<TData>` - Conditionally returns `TData` as either its masked type or unmasked type
- `Unmasked<TData>` - Unwraps `TData` into the full result type

For more information about other overridable types in Apollo Client, see the [TypeScript guide](../development-testing/static-typing).
6 changes: 6 additions & 0 deletions docs/source/data/mutations.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,12 @@ This article demonstrates how to send updates to your GraphQL server with the `u

## Prerequisites

<Note>

If your application is built using TypeScript, we recommend reading the [TypeScript guide](../development-testing/static-typing) to learn how to use TypeScript with Apollo Client.

</Note>

This article assumes you're familiar with building basic GraphQL mutations. If you need a refresher, we recommend that you
[read this guide](http://graphql.org/learn/queries/#mutations).

Expand Down
12 changes: 12 additions & 0 deletions docs/source/data/queries.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,12 @@ This article shows how to fetch GraphQL data in React with the `useQuery` hook a

## Prerequisites

<Note>

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Without fully restructuring everything, I figured this was a cheap way to bring attention to the TypeScript guide more up-front. Other ideas welcome.


If your application is built using TypeScript, we recommend reading the [TypeScript guide](../development-testing/static-typing) to learn how to use TypeScript with Apollo Client.

</Note>

This article assumes you're familiar with building basic GraphQL queries. If you need a refresher, we recommend [this guide](http://graphql.org/learn/queries/). You can also build example queries against Apollo's [full-stack tutorial server](https://apollo-fullstack-tutorial.herokuapp.com/graphql).

This article also assumes that you've already set up Apollo Client and have wrapped your React app in an `ApolloProvider` component. For more information, see the [getting started guide](../get-started/).
Expand Down Expand Up @@ -70,6 +76,12 @@ As our query executes and the values of `loading`, `error`, and `data` change, t

When the user selects a dog breed from the populated dropdown, the selection is sent to the parent component via the provided `onDogSelected` function.

<Note>

If you are using TypeScript, `data` is typed as `unknown` when the query type is unknown. Accessing fields on `data` results in a TypeScript error. See the [TypeScript with Apollo Client](../development-testing/static-typing) guide to learn how to add types for your queries.

</Note>

In the next step, we'll associate the dropdown with a more sophisticated query that uses GraphQL variables.

## Caching query results
Expand Down
Loading