Open nodegin opened 1 month ago
Actually I am upgrading from 0.8.0 to 0.9.1 it became broken
NextSSRApolloClient is also not usable.
Error: __TURBOPACK__imported__module__$5b$project$5d2f$node_modules$2f2e$pnpm$2f40$apollo$2b$experimental$2d$nextjs$2d$app$2d$support$40$0$2e$9$2e$1_$40$apollo$2b$client$40$3$2e$9$2e$10_next$40$14$2e$1$2e$4_react$40$18$2e$2$2e$0$2f$node_modules$2f40$apollo$2f$experimental$2d$nextjs$2d$app$2d$support$2f$dist$2f$ssr$2f$index$2e$rsc$2e$js__$5b$app$2d$rsc$5d$__$28$ecmascript$29$__$7b$exports$7d$__.NextSSRApolloClient is not a constructor
I tried to import
import * as test from '@apollo/experimental-nextjs-app-support/ssr'
and the result:
Object [Module] {
DebounceMultipartResponsesLink: [Getter],
RemoveMultipartDirectivesLink: [Getter],
SSRMultipartLink: [Getter]
}
Hmm, it seems like you're getting the RSC entry point (where those shouldn't be available and using them was always a bug).
Do you still get that error when you mark the file importing it as "use client"
?
Are you maybe building for another runtime such as edge? We got reports about that and I'm looking into that soon (just came back from vacation)
Assuming you are using the edge
runtime, I believe this will be solved by https://github.com/apollographql/apollo-client-nextjs/pull/263
Could you please give
npm i @apollo/experimental-nextjs-app-support@0.0.0-commit-84f7c8d
a try?
The same issue occurs when importing registerApolloClient
import { registerApolloClient } from "@apollo/experimental-nextjs-app-support/rsc";
just copy pasted the example from the github readme
export const { getClient } = registerApolloClient(() => { return new ApolloClient({ ... }) }
@Giusti you should be getting an error there if you are not importing registerApolloClient
from a Server Component import tree - and that's intentional, as registerApolloClient
can only safely be used in server components.
What environment are you using this in?
I found the issue is that in version 0.9.0
the usage is changed, some exports is moved to another new package.
To fix the issue, simply change the import from:
import {
NextSSRApolloClient,
NextSSRInMemoryCache,
SSRMultipartLink,
} from "@apollo/experimental-nextjs-app-support/ssr";
to:
import {
ApolloClient,
InMemoryCache,
SSRMultipartLink,
} from '@apollo/client-react-streaming'
I also created a PR #269 to update the documents
Closing this as I found the solution
@nodegin as I commented in the PR, that's not the intended way of using it.
You should continue to use "@apollo/experimental-nextjs-app-support/ssr"
- as you can see in the code that you yourself quoted up there, all those exports are still present:
export { DebounceMultipartResponsesLink, InMemoryCache as NextSSRInMemoryCache, RemoveMultipartDirectivesLink, SSRMultipartLink } from '@apollo/client-react-streaming';
import { ApolloClient } from '@apollo/client-react-streaming';
var NextSSRApolloClient = class extends ApolloClient {
static info = bundle;
};
export { NextSSRApolloClient };
If you are still encountering issues with that usage, we'll need to investigate that.
Is there any chance you can provide us with a reproduction of your issue?
@phryneas I see, I think you can reproduce it by just installing 0.9.1 and navigate to @apollo/experimental-nextjs-app-support/dist/ssr/index.ssr.js
, as I posted the exports are missing.
However changing to import from '@apollo/client-react-streaming' did fix for me, at least my app is running normally.
I guess the root cause could be some build config error casuing the re-export not working?
the exports are missing.
That might be just a TypeScript error there, what your IDE sees and what the bundler sees are two very different things.
Are you importing from @apollo/experimental-nextjs-app-support/dist/ssr/index.ssr.js
directly, or from @apollo/experimental-nextjs-app-support/ssr
?
Depending on if you are in a RSC, SSR or Browser context, that import points to completely different files.
I do have an inkling though - you might have imports that accidentally pull Client files into RSC, or RSC files into Client Components. Could you read through #268 and see if that is similar to your situation?
Especially thinking about your solution - @apollo/client-react-streaming
exports ApolloClient
in the RSC, SSR and Browser entry points, while @apollo/experimental-nextjs-app-support/ssr
exports NextSSRInMemoryCache
only in the SSR and Browser entrypoints (as in the past it was always considered a bug to use it from RSC).
I believe you have an import chain somewhere that pulls the file with your NextSSRInMemoryCache
import into a RSC environment (and likewise, in @Giusti's case, an import chain that pulls the file with registerApolloClient
into a Client Component environment).
@phryneas Hmm, I see that could be the reason.
I have a apps/web/lib/apollo/index.ts
in my setup:
export * from './apolloClient'
export * from './apolloProvider'
But that is just for simplifying the import statement, is that a problem?
How about if I import from @apollo/client-react-streaming
as NextSSR* is basically just re-exporting.
The behavior was working fine before v0.9.0
The behavior was working fine before v0.9.0
Yes, with 0.9.0 we aligned with how a React Server Component package should be packaged according to the React team. Their philosophy is "fail early on bundling, not late with a runtime error", which makes sense but doesn't give any good error messages :/
I'm honestly really sorry for these errors, but this is pretty much the experience it is meant to be :(
Your barrel exports here look very much like they could be the problem.
I'd highly recommend not to switch over to the @apollo/client-react-streaming
package for this, but to fix your bug here - you are likely ending up with a much bigger bundle size because you are bundling things that are not meant to be bundled.
I did ask around for tools that might help with this on Twitter, and https://github.com/pahen/madge was suggested - maybe that helps you identify these imports faster.
Especially thinking about your solution -
@apollo/client-react-streaming
exportsApolloClient
in the RSC, SSR and Browser entry points, while@apollo/experimental-nextjs-app-support/ssr
exportsNextSSRInMemoryCache
only in the SSR and Browser entrypoints (as in the past it was always considered a bug to use it from RSC).I believe you have an import chain somewhere that pulls the file with your
NextSSRInMemoryCache
import into a RSC environment (and likewise, in @Giusti's case, an import chain that pulls the file withregisterApolloClient
into a Client Component environment).
Hm, I've got to confess, my understanding of SSR and its ways in Next.js 13+ are still lacking. But I'm facing the same error, even with a really minimal setup: https://github.com/JonasDoe/debug-apollo-client. There isn't a "use client"
directive, and yet npm run build
will result in the warning:
> build
> next build
▲ Next.js 14.2.2
Creating an optimized production build ...
⚠ Compiled with warnings
./app/layout.tsx
Attempted import error: 'NextSSRInMemoryCache' is not exported from '@apollo/experimental-nextjs-app-support/ssr' (imported as 'NextSSRInMemoryCache').
Import trace for requested module:
./app/layout.tsx
...
This is not just a type error, since - in the real, deployed application -, calling it will result in an error when the @apollo/experimental-nextjs-app-support
is greater version 0.8.0
.
@JonasDoe NextSSRInMemoryCache
is an import for SSR only, which means "use client"
files - you cannot import it from RSC, which you are doing.
There are three things in Next.js
"use client"
in the import path to this fileSo you'd use NextSSRInMemoryCache
in Client Components (it needs to be used in SSR and Browser), but not in RSC.
There isn't a "use client" directive, and yet npm run build will result in the warning:
Exactly, your example is a React Server Component trying to import something that should only be used in SSR and Browser. In RSC, you'd just use ApolloClient from @apollo/client
(as we show in our examples).
Using the wrong import was always a bug in your app, we just enforce it the way the React team intends it to be enforced (with missing imports) since 0.9.0.
@phryneas Phew, thanks for the detailed explanation! So if I understand correcly, RSC is the "purest" form of a component and has the highest restrictions. It is encapsulated and doesn't need to synchronize with the client. It can reside in app
. When it's done, it passes its data as RSC Payload
to the SSR (i.e. a component in app
) and indirectly to the CSR. When it comes to GraphQL data loading in a RSC, I utilize rsc/registerApolloClient
from the experimental package - together with @apollo/client/ApolloClient
-, to share the GraphQL client and it's cache between all RSCs in a single request (happens automagically due to the registerApolloClient
logic).
In the next step (SSR), in your example you've added "use client"
at the top to ensure that it won't accidentally become a RSC, i.e. you can safely use NextSSRApolloClient
or NextSSRInMemoryCache
. Is that correct?
SSR and CSR will, in turn, utilize NextSSRApolloClient
and the alike. What's the gain compared to the ordinary @apollo/client/ApolloClient
? I think I've read the SSR clients come with automatic cache hydration at the client side, but I can't really find anything about it right now. In this explanatory blog entry the "full snippet" looks like this:
// lib/client.js
import { HttpLink } from "@apollo/client";
import {
NextSSRInMemoryCache,
NextSSRApolloClient,
} from "@apollo/experimental-nextjs-app-support/ssr";
import { registerApolloClient } from "@apollo/experimental-nextjs-app-support/rsc";
export const { getClient } = registerApolloClient(() => {
return new NextSSRApolloClient({
cache: new NextSSRInMemoryCache(),
link: new HttpLink({
uri: "https://main--time-pav6zq.apollographos.net/graphql",
}),
});
});
So for some reason, RSC and SSR come together again, and it differs from the repo's README
in that regard. So I'm a bit confused here.
Also, if I understand correctly, the @apollo/client/ApolloClient
is completely decoupled from the experimental SSR client's. No cache data is shared between them and - if I understand RSC vs client component correctly - is not intended either. Is that right?
Using the wrong import was always a bug in your app, we just enforce it the way the React team intends it to be enforced (with missing imports) since 0.9.0.
I don't doubt that, but I wasn't experiencing any faulty behavior prior to 0.9.0
, that's why I am a bit suprised.
@phryneas Phew, thanks for the detailed explanation! So if I understand correcly, RSC is the "purest" form of a component and has the highest restrictions. It is encapsulated and doesn't need to synchronize with the client. It can reside in
app
. When it's done, it passes its data asRSC Payload
to the SSR (i.e. a component inapp
) and indirectly to the CSR. When it comes to GraphQL data loading in a RSC, I utilizersc/registerApolloClient
from the experimental package - together with@apollo/client/ApolloClient
-, to share the GraphQL client and it's cache between all RSCs in a single request (happens automagically due to theregisterApolloClient
logic).
That pretty much sums it up.
In the next step (SSR), in your example you've added
"use client"
at the top to ensure that it won't accidentally become a RSC, i.e. you can safely useNextSSRApolloClient
orNextSSRInMemoryCache
. Is that correct?
Yup.
SSR and CSR will, in turn, utilize
NextSSRApolloClient
and the alike. What's the gain compared to the ordinary@apollo/client/ApolloClient
? I think I've read the SSR clients come with automatic cache hydration at the client side, but I can't really find anything about it right now. In this explanatory blog entry the "full snippet" looks like this:
If you use useSuspenseQuery
or useBackgroundQuery
, we transport the result over from SSR into the browser and ensure you won't encounter hydration errors.
If you use useQuery
, we prevent requests from happening on the server (because SSR only renders once, so all you'd see there is a loading
indicator, but your server would still make an unneccessary request).
You can get more context in this RFC or in this conference talk.
// lib/client.js import { HttpLink } from "@apollo/client"; import { NextSSRInMemoryCache, NextSSRApolloClient, } from "@apollo/experimental-nextjs-app-support/ssr"; import { registerApolloClient } from "@apollo/experimental-nextjs-app-support/rsc"; export const { getClient } = registerApolloClient(() => { return new NextSSRApolloClient({ cache: new NextSSRInMemoryCache(), link: new HttpLink({ uri: "https://main--time-pav6zq.apollographos.net/graphql", }), }); });
So for some reason, RSC and SSR come together again, and it differs from the repo's
README
in that regard. So I'm a bit confused here.
🤦 I know you won't believe it, but I already fixed this blog article twice. This just seems to come back. I fixed it again and contacted some people internally to find out why it keeps switching back.
Also, if I understand correctly, the
@apollo/client/ApolloClient
is completely decoupled from the experimental SSR client's. No cache data is shared between them and - if I understand RSC vs client component correctly - is not intended either. Is that right?
Yes, quoting from the README:
❗️ We do handle "RSC" and "SSR" use cases as completely separate.
You should generally try not to have overlapping queries between the two, as all queries made in SSR can dynamically update in the browser as the cache updates (e.g. from a mutation or another query), but queries made in RSC will not be updated in the browser - for that purpose, the full page would need to rerender. As a result, any overlapping data would result in inconsistencies in your UI.
So decide for yourself, which queries you want to make in RSC and which in SSR, and don't have them overlap.
We will soon have a way of "prefetching for SSR/Browser in RSC" though, see #258.
I don't doubt that, but I wasn't experiencing any faulty behavior prior to 0.9.0, that's why I am a bit suprised.
Yeah, it doesn't really "error", but your bundle size might have been increased significantly, at least we had reports of that.
Again, I'm sorry for all of that. We will make some adjustments in the next version where we move the /ssr
and /rsc
entrypoints both into /
and rename NextSSR*
to just the plain names InMemoryCache
and ApolloClient
. If you import them in RSC, it will give you the plain ones from @apollo/client
and if you import them in SSR/Browser, you'll get the SSR implementation.
That will hopefully make this a lot better.
You can get more context in this RFC or in this conference talk
Wow, this the RFC is impressive and rather helpful for some more insights, thank you! (Btw. in "as React does not a mechanism of injecting data into the stream at arbitrary points in time"
a verb is missing.)
If you use useQuery, we prevent requests from happening on the server (because SSR only renders once, so all you'd see there is a loading indicator, but your server would still make an unneccessary request).
So where's the place I'm supposed to fetch initial data that might change later? I'ld rather do some general fetches at server-side for a performance gain. The RSC does fetches at server side, but isn't really supposed to have overlapping queries with the SSR/CSR, as you quote. In the SSR stage, in turn, doing data fetches (via hooks, now) isn't supported and will be skipped. I would expect that the RSC should fetch this data, and I stringify and pass it to the SSR as initial cache. Is this what https://github.com/apollographql/apollo-client-nextjs/pull/258 is there for?
Again, I'm sorry for all of that.
I'm really grateful that you take your time to explain the framework to users! Not only here in comments, but also at so many other places, which is quite some uphill fighting.
So where's the place I'm supposed to fetch initial data that might change later? [...] Is this what https://github.com/apollographql/apollo-client-nextjs/pull/258 is there for?
Yup. That, or you just use useSuspenseQuery
in a client component, which will fetch the data in SSR, not RSC - but only on the first request, not on further navigation. (That's what you'll be using RSC+PreloadQuery from #258 for)
In
node_modules/.pnpm/@apollo+experimental-nextjs-app-support@0.9.1_@apollo+client@3.9.10_next@14.1.4_react@18.2.0/node_modules/@apollo/experimental-nextjs-app-support/dist/ssr/index.ssr.js
NextSSRInMemoryCache is not exported.
I am using version 0.9.1