-
Notifications
You must be signed in to change notification settings - Fork 36
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
implement multipart streaming for PreloadQuery
#389
base: next
Are you sure you want to change the base?
Conversation
PreloadQuery
commit: |
#343 Bundle Size — 1.32MiB (+5.89%).5bacf2e(current) vs 9d0a77a main#331(baseline) Warning Bundle contains 1 duplicate package – View duplicate packages Bundle metrics
|
Current #343 |
Baseline #331 |
|
---|---|---|
Initial JS | 1.04MiB (+4.3% ) |
1016.77KiB |
Initial CSS | 70B |
70B |
Cache Invalidation | 85.18% |
0.08% |
Chunks | 36 (+5.88% ) |
34 |
Assets | 62 (+5.08% ) |
59 |
Modules | 679 (+6.76% ) |
636 |
Duplicate Modules | 119 (+12.26% ) |
106 |
Duplicate Code | 5.35% (+14.81% ) |
4.66% |
Packages | 26 |
26 |
Duplicate Packages | 1 |
1 |
Bundle size by type 2 changes
2 regressions
Current #343 |
Baseline #331 |
|
---|---|---|
JS | 1.31MiB (+5.86% ) |
1.24MiB |
Other | 10.01KiB (+10.12% ) |
9.09KiB |
CSS | 70B |
70B |
Bundle analysis report Branch pr/multipart-streaming Project dashboard
Generated by RelativeCI Documentation Report issue
|
} from "graphql"; | ||
import { ObjMap } from "graphql/jsutils/ObjMap"; | ||
|
||
export class IncrementalSchemaLink extends ApolloLink { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
For use in tests, essentially a SchemaLink
with @defer
support.
rating: (source, args, context) => { | ||
return new Promise((resolve) => | ||
setTimeout(resolve, Math.random() * 2 * args.delay, { | ||
value: products.find((p) => p.id === source.id)?.rating, | ||
env: getEnv(context), | ||
}) | ||
); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
adding a slow field that can be used to test @defer
return ( | ||
<ApolloWrapper> | ||
<PreloadQuery | ||
query={QUERY} | ||
context={{ | ||
delay: 1000, | ||
error: searchParams?.errorIn || undefined, | ||
error: (await searchParams)?.errorIn || undefined, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
These are still leftovers from changing to Next 15 and only surfaced now.
data: null, | ||
errors: [new GraphQLError("Simulated error")], | ||
}); | ||
if (errorConditions.includes("network_error")) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This link can now simulate both GraphQLErrors and NetworkErrors in tests.
this.setLink(this.link); | ||
} | ||
|
||
setLink(newLink: ApolloLink) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
RSC: adds TeeToReadableStreamLink
,
SSR & Browser: adds ReadFromReadableStreamLink
for consumption
}): Promise<React.ReactElement> { | ||
const preloader = createTransportedQueryPreloader(await getClient()); | ||
const { query, ...transportedOptions } = options; | ||
const queryRef = preloader(query, transportedOptions); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
All the logic now moved into createTransportedQueryPreloader
as that will also be used by other frameworks in SSR loaders
const tryClose = () => { | ||
try { | ||
controller.close(); | ||
} catch { | ||
// maybe we already tried to close the stream, nothing to worry about | ||
} | ||
}; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This can be triggered from multiple different places, and we want the first call to close and the later calls to not error.
}); | ||
} | ||
const client = useApolloClient(); | ||
reviveTransportedQueryRef(queryRef, client); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
All of that logic now lives in reviveTransportedQueryRef
as it will also be used by other framework implementations.
client | ||
.query({ | ||
query, | ||
...options, | ||
// ensure that this query makes it to the network | ||
fetchPolicy: "network-only", | ||
context: skipDataTransport( | ||
teeToReadableStream(__injectIntoStream!, { | ||
...options?.context, | ||
// we want to do this even if the query is already running for another reason | ||
queryDeduplication: false, | ||
}) | ||
), | ||
}) | ||
.catch(() => { | ||
/* we want to avoid any floating promise rejections */ | ||
}); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is the core "preload into a ReadableStream" implementation
const internalQueryRef = getSuspenseCache(client).getQueryRef( | ||
cacheKey, | ||
() => | ||
client.watchQuery({ | ||
...hydratedOptions, | ||
fetchPolicy: "network-only", | ||
context: skipDataTransport( | ||
readFromReadableStream(stream.pipeThrough(new JSONDecodeStream()), { | ||
...hydratedOptions.context, | ||
queryDeduplication: true, | ||
}) | ||
), | ||
}) | ||
); | ||
Object.assign(queryRef, wrapQueryRef(internalQueryRef)); | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is the core "recreate a normal queryRef from a transported queryRef" implementation
Here's the happy case (still without
@defer
):https://apollo-git-6cd975-apollo-client-next-package-integration-tests.vercel.app/rsc/dynamic/PreloadQuery/queryRef-useReadQuery
Here we have a simulated error as part of a regular graphql response
https://apollo-git-6cd975-apollo-client-next-package-integration-tests.vercel.app/rsc/dynamic/PreloadQuery/queryRef-useReadQuery?errorIn=rsc
and here we have a link/network error:
https://apollo-git-6cd975-apollo-client-next-package-integration-tests.vercel.app/rsc/dynamic/PreloadQuery/queryRef-useReadQuery?errorIn=rsc,network_error
Adding
@defer
to the mix:https://apollo-git-6cd975-apollo-client-next-package-integration-tests.vercel.app/rsc/dynamic/PreloadQuery/defer-queryRef-useReadQuery