apollo-client-nextjs
apollo-client-nextjs copied to clipboard
Remix
First the good news: streaming SSR with the suspense hooks and Remix works.
All the setup for that is in this diff: https://github.com/phryneas/remix-apollo-stream-example/commit/bc43efe4a3f53d0672217cc33dfb20523af4d9c3
That said, supporting loaders is another beast. I'll add my thoughts in this thread.
Thoughts on Loaders
Loaders run independently of React.
- on a SSR request, they run before streaming SSR happens in
handleRequest.- as I understand it, they don't even need to run in the same process/host
- on later page navigation, they run on the server, but no more SSR happens
As a result, our current "inject into the stream" approach won't work - we have no way of sharing an AC instance reliably between handleRequest and loader, and oftentimes there's not even a handleRequest
=> each loader should create their own instance of Apollo Client and createQueryPreloader.
const loader = () => {
const preload = createQueryPreloader(makeClient())
const result1 = preload(query1)
const result2 = preload(query2)
return defer({
result1,
result2
})
}
Now, right now preload creates a queryRef - that's hardly feasible in this case, since it can't be transported over the wire.
So we probably need to wrap createQueryPreloader (https://github.com/apollographql/apollo-client/pull/11719) to have a completely different functionality in SSR.
It could create a value like
{
query,
variables,
resultPromise
}
which would then be picked up by an (also wrapped) useReadQuery that would simulate an ongoing request (simulating the request start sooner would be great, but is probably not possible).
Currently, the defer implementation doesn't support promises that are not on the top level yet.
We'd probably need buy-in from the Remix team here to be able to make according changes on their side - once we have fleshed out this story more.
Hi @phryneas,
I was trying to get the transported data stored locally to the cache and I came up with below... It does do what I intend it to but I am wondering if this is how you would go about it.
Thanks for any direction :)
// makeClient.ts
import { HttpLink } from "@apollo/client/index.js";
import { ApolloClient, InMemoryCache } from "@apollo/client-react-streaming";
import { CachePersistor } from 'apollo3-cache-persist';
export const cache = new InMemoryCache()
const persistor =
typeof window !== 'undefined'
? new CachePersistor({
cache,
storage: localStorage,
})
: null;
persistor &&
persistor.restore().then(() => {
// console.log('cache restored')
});
export function makeClient() {
return new ApolloClient({
cache: typeof window === 'undefined' ? new InMemoryCache() : cache,
link: new HttpLink({
uri: "https://swapi-graphql.netlify.app/.netlify/functions/index",
}),
});
}
@LydiaF It seems okay, although I would probably question if this is necessary at all - if you use this package to it's full extend, every page should bring their data to the cache anyways.
Also, you probably shouldn't be creating an ApolloClient outside of a makeClient function, there's a risk that you share data between different users in SSR if you do this on a module level.
I do wonder though, why are you posting this in the Remix issue? This doesn't seem to add to that topic.
Hi @phryneas, thank you for replying so quickly and for your help. I think I am okay now.
I think I was confused because I was still thinking in terms of how I used to do things -- I would look in Application local storage to see the cache data (as opposed to Apollo dev tools), and would also typically import the cache to do something like below as opposed to using useApolloClient. So I was lost as to how to call methods on the cache and then wrongly thought the data was somehow not really there.
// app/routes/_index.tsx
export default function Index() {
const client = useApolloClient();
return (
<div style={{ fontFamily: "system-ui, sans-serif", lineHeight: "1.8" }}>
<h1>Welcome to Remix</h1>
<button
onClick={() => {
client.cache.updateQuery({ query: ALL_FILMS }, (data) => ({
allFilms: {
__typename: "FilmsConnection",
films: [],
},
}));
}}
>
Press Me
</button>
<Suspense fallback="loading">
<Suspended />
</Suspense>
</div>
);
}
As for posting here, apologies, I probably should have asked in the repo you made.
Thanks again!