Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

create branded QueryRef type without exposed properties #11824

Merged
merged 21 commits into from
May 13, 2024
Merged

Conversation

phryneas
Copy link
Member

@phryneas phryneas commented May 2, 2024

Create branded QueryRef type without exposed properties.

This change deprecates QueryReference in favor of a QueryRef type that doesn't expose any properties.
This change also updates preloadQuery to return a new PreloadedQueryRef type, which exposes the toPromise function as it does today. This means that query refs produced by useBackgroundQuery and useLoadableQuery now return QueryRef types that do not have access to a toPromise function, which was never meant to be used in combination with these hooks.

While we tend to avoid any types of breaking changes in patch releases as this, this change was necessary to support an upcoming version of the React Server Component integration, which needed to omit the toPromise function that would otherwise have broken at runtime.
Note that this is a TypeScript-only change. At runtime, toPromise is still present on all queryRefs currently created by this package - but we strongly want to discourage you from accessing it in all cases except for the PreloadedQueryRef use case.

Migration is as simple as replacing all references to QueryReference with QueryRef, so it should be possible to do this with a search & replace in most code bases:

-import { QueryReference } from '@apollo/client'
+import { QueryRef } from '@apollo/client'

- function Component({ queryRef }: { queryRef: QueryReference<TData> }) {
+ function Component({ queryRef }: { queryRef: QueryRef<TData> }) {
  // ...
}

Copy link

changeset-bot bot commented May 2, 2024

🦋 Changeset detected

Latest commit: acc9ba9

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 1 package
Name Type
@apollo/client Patch

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

Copy link
Contributor

github-actions bot commented May 2, 2024

size-limit report 📦

Path Size
dist/apollo-client.min.cjs 38.65 KB (+0.1% 🔺)
import { ApolloClient, InMemoryCache, HttpLink } from "dist/main.cjs" 47.06 KB (+1.22% 🔺)
import { ApolloClient, InMemoryCache, HttpLink } from "dist/main.cjs" (production) 44.68 KB (+1.45% 🔺)
import { ApolloClient, InMemoryCache, HttpLink } from "dist/index.js" 34.17 KB (+0.01% 🔺)
import { ApolloClient, InMemoryCache, HttpLink } from "dist/index.js" (production) 32.06 KB (+0.01% 🔺)
import { ApolloProvider } from "dist/react/index.js" 1.23 KB (0%)
import { ApolloProvider } from "dist/react/index.js" (production) 1.22 KB (0%)
import { useQuery } from "dist/react/index.js" 5.28 KB (0%)
import { useQuery } from "dist/react/index.js" (production) 4.36 KB (-0.03% 🔽)
import { useLazyQuery } from "dist/react/index.js" 5.52 KB (0%)
import { useLazyQuery } from "dist/react/index.js" (production) 4.59 KB (-0.03% 🔽)
import { useMutation } from "dist/react/index.js" 3.52 KB (0%)
import { useMutation } from "dist/react/index.js" (production) 2.74 KB (0%)
import { useSubscription } from "dist/react/index.js" 3.21 KB (-0.07% 🔽)
import { useSubscription } from "dist/react/index.js" (production) 2.4 KB (0%)
import { useSuspenseQuery } from "dist/react/index.js" 5.44 KB (0%)
import { useSuspenseQuery } from "dist/react/index.js" (production) 4.1 KB (+0.03% 🔺)
import { useBackgroundQuery } from "dist/react/index.js" 4.96 KB (-0.06% 🔽)
import { useBackgroundQuery } from "dist/react/index.js" (production) 3.61 KB (0%)
import { useLoadableQuery } from "dist/react/index.js" 5.07 KB (+0.49% 🔺)
import { useLoadableQuery } from "dist/react/index.js" (production) 3.72 KB (+0.45% 🔺)
import { useReadQuery } from "dist/react/index.js" 3.33 KB (+0.57% 🔺)
import { useReadQuery } from "dist/react/index.js" (production) 3.27 KB (+0.61% 🔺)
import { useFragment } from "dist/react/index.js" 2.29 KB (0%)
import { useFragment } from "dist/react/index.js" (production) 2.23 KB (0%)

Copy link

netlify bot commented May 2, 2024

Deploy Preview for apollo-client-docs ready!

Name Link
🔨 Latest commit acc9ba9
🔍 Latest deploy log https://app.netlify.com/sites/apollo-client-docs/deploys/664209c7afd94d0008083e48
😎 Deploy Preview https://deploy-preview-11824--apollo-client-docs.netlify.app
📱 Preview on mobile
Toggle QR Code...

QR Code

Use your smartphone camera to open QR code link.

To edit notification comments on pull requests, go to your Netlify site configuration.

@phryneas
Copy link
Member Author

phryneas commented May 3, 2024

/release:pr

Copy link
Contributor

github-actions bot commented May 3, 2024

Please add a changeset via npx changeset before attempting a snapshot release.

@phryneas
Copy link
Member Author

phryneas commented May 3, 2024

/release:pr

Copy link
Contributor

github-actions bot commented May 3, 2024

A new release has been made for this PR. You can install it with:

npm i @apollo/client@0.0.0-pr-11824-20240503100254

Comment on lines 36 to 47
export interface QueryReferenceBase<TData = unknown, TVariables = unknown> {
/** @internal */
[QUERY_REF_BRAND]?(variables: TVariables): TData;
}

/**
* A `QueryReference` is an opaque object returned by `useBackgroundQuery`.
* A child component reading the `QueryReference` via `useReadQuery` will
* suspend until the promise resolves.
*/
export interface QueryReference<TData = unknown, TVariables = unknown> {
export interface QueryReference<TData = unknown, TVariables = unknown>
extends QueryReferenceBase<TData, TVariables> {
Copy link
Member Author

@phryneas phryneas May 3, 2024

Choose a reason for hiding this comment

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

I'm a bit unhappy with a new QueryReferenceBase type here tbh., this should be the QueryReference type, and useBackgroundQuery and QueryPreloader should return two distinct types, where the toPromise callback should only be exposed on the QueryPreloader return value - it only makes sense there.

It's kinda breaking, but might be a consideration sooner rather than later @jerelmiller WDYT?

Copy link
Member

Choose a reason for hiding this comment

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

Let me take the weekend to think about this.

I'm in the same boat. If we can avoid the QueryReferenceBase type, I would love that, but I can definitely understand why we might need it.

@phryneas phryneas changed the title create branded QueryReferenceBase type create branded QueryRef type without exposed properties May 7, 2024
@phryneas
Copy link
Member Author

phryneas commented May 7, 2024

/release:pr

Copy link
Contributor

github-actions bot commented May 7, 2024

A new release has been made for this PR. You can install it with:

npm i @apollo/client@3.10.2

@phryneas
Copy link
Member Author

phryneas commented May 7, 2024

/release:pr

Copy link
Contributor

github-actions bot commented May 7, 2024

A new release has been made for this PR. You can install it with:

npm i @apollo/client@3.10.2

@phryneas
Copy link
Member Author

phryneas commented May 8, 2024

/release:pr

Copy link
Contributor

github-actions bot commented May 8, 2024

A new release has been made for this PR. You can install it with:

npm i @apollo/client@0.0.0-pr-11824-20240508093324

Comment on lines -1294 to -1296
// @public (undocumented)
class InternalQueryReference<TData = unknown> {
// Warning: (ae-forgotten-export) The symbol "InternalQueryReferenceOptions" needs to be exported by the entry point index.d.ts
Copy link
Member Author

Choose a reason for hiding this comment

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

These internal types are now no longer exposed 🎉

Comment on lines +6350 to 6352
expectTypeOf(inferredQueryRef).toMatchTypeOf<
QueryReference<VariablesCaseData, VariablesCaseVariables> | undefined
>();
Copy link
Member Author

Choose a reason for hiding this comment

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

ensuring similarity to the old QueryReference type

TData,
TVariables
> | null>(null);

assertWrappedQueryRef(queryRef);
Copy link
Member Author

Choose a reason for hiding this comment

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

additional assertion to make sure that a potential wrapper converted the QueryRef into a WrappedQueryRef correctly

@@ -27,17 +28,51 @@ type FetchMoreOptions<TData> = Parameters<

const QUERY_REFERENCE_SYMBOL: unique symbol = Symbol();
const PROMISE_SYMBOL: unique symbol = Symbol();

declare const QUERY_REF_BRAND: unique symbol;
Copy link
Member Author

Choose a reason for hiding this comment

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

this exists only on type level

export interface QueryReference<TData = unknown, TVariables = unknown> {
export interface QueryRef<TData = unknown, TVariables = unknown> {
/** @internal */
[QUERY_REF_BRAND]?(variables: TVariables): TData;
Copy link
Member Author

Choose a reason for hiding this comment

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

using a function signature to capture the generics on this type

Copy link
Member Author

Choose a reason for hiding this comment

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

this was a bit too loose for TVariables previously

*
* {@inheritDoc @apollo/client!PreloadedQueryRef#toPromise:member(1)}
*/
toPromise?: unknown;
Copy link
Member Author

Choose a reason for hiding this comment

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

This is kinda breaking, but will only break when calling toPromise on this type - but otherwise it ensures that this type and QueryRef can be assigned to each other, which is more important for backwards compat.

* @internal
* For usage in internal helpers only.
*/
interface WrappedQueryRef<TData = unknown, TVariables = unknown>
Copy link
Member Author

@phryneas phryneas May 7, 2024

Choose a reason for hiding this comment

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

This is now a purely internal type (not even exported, thanks to assertWrappedQueryRef), which removes type references to all kinds of internals from the api surface

@phryneas
Copy link
Member Author

phryneas commented May 8, 2024

Digging into that (unrelated) test failure, but already opening this for review.

@phryneas phryneas marked this pull request as ready for review May 8, 2024 12:21
@phryneas phryneas merged commit 47ad806 into main May 13, 2024
39 checks passed
@phryneas phryneas deleted the pr/queryRefBase branch May 13, 2024 12:48
@github-actions github-actions bot mentioned this pull request May 13, 2024
@github-actions github-actions bot locked as resolved and limited conversation to collaborators Jun 13, 2024
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants