-
Notifications
You must be signed in to change notification settings - Fork 2.7k
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
SSR and cache-and-network fetch policy #2119
Comments
I just started using Apollo and am seeing this as well. Not sure if there's an existing solution or if this is a bug. |
As a workaround, I'm now using the |
This issue has been automatically marked as stale because it has not had recent activity. It will be closed if no further activity occurs. Thank you for your contributions to Apollo Client! |
This is still an issue. |
Definitely still an issue. |
Isn't this expected behavior with |
No. I want my query to run again when a user visits a page again to get the freshest data, but I also want it to show the stale data for a faster render and nicer UX. It's just now rendered with the fresh data on the server, why would I want it to stress my server by unnecessarily fetching the exact same queries that were just loaded literally milliseconds ago? |
Yeah I understand that, just wanted to say that it was kind of the expected behavior IMO, because the component is returning the data from the cache and also firing a network request, as it is documented. The problem though is that in this case, the cache was populated by that same component on the server side, but that fact is not transferred to the client (since it's not part of the store), only the data in the store. Maybe there could be a way to set the fetchPolicy to To make this work "transparently" the client would have to also save its internal state for the queries being watched, and have some way to rehydrate those based on the components that mount. This looks like it would be very hard to implement. Another solution would be to ignore the fetch policy when hydrating from the server, and just populate the data from the cache. Something similar to |
@mxstbr so you are saying the initial data from the SSR isn't present and it resets and makes a network request? Looking at the intended outcome from the issue, you don't want it to make the request again but that is what that policy is designed to do? I'm not sure I understand what is wrong? |
No, the data is there, that's the whole point.
That sixth step doesn't make any sense: the server just loaded the data and sent it down, why would Apollo go back to the server to get the exact same data again?! |
@mxstbr that totally shouldn't be happening! Does that only happen with certain fetch policies? Can you share your initialization code and SSR code where you inline the state? I've got a few SSR apps and they don't double request so I'm looking to narrow down the issue! |
It only happens with You can replicate this in your own browser by opening
Client initialisation: const store = initStore(window.__SERVER_STATE__ || initialState, {
middleware: [client.middleware()],
reducers: {
apollo: client.reducer(),
},
});
function render() {
return ReactDOM.render(
<ApolloProvider store={store} client={client}>
<Router history={history}>
<Routes
maintenanceMode={process.env.REACT_APP_MAINTENANCE_MODE === 'enabled'}
/>
</Router>
</ApolloProvider>,
document.querySelector('#root')
);
} Client creation: const networkInterface = createBatchingNetworkInterface({
uri: API_URI,
batchInterval: 10,
opts: {
credentials: 'include',
},
});
const networkInterfaceWithSubscriptions = addGraphQLSubscriptions(
networkInterface,
wsClient
);
const fragmentMatcher = new IntrospectionFragmentMatcher({
introspectionQueryResultData,
});
export const client = new ApolloClient({
networkInterface: networkInterfaceWithSubscriptions,
fragmentMatcher,
initialState: window.__SERVER_STATE__ && {
apollo: window.__SERVER_STATE__.apollo,
},
ssrForceFetchDelay: 100,
...getSharedApolloClientOptions(),
}); Server (express.js) route: const renderer = (req, res) => {
// apollo
const client = new ApolloClient({
ssrMode: true,
networkInterface: createLocalInterface(graphql, schema, {
context: {
loaders: createLoaders(),
user: req.user,
},
}),
...getSharedApolloClientOptions(),
});
// redux
const initialReduxState = {
users: {
currentUser: req.user,
},
};
const store = initStore(initialReduxState, {
// Inject the server-side client's middleware and reducer
middleware: [client.middleware()],
reducers: {
apollo: client.reducer(),
},
});
const context = {};
// Frontend
const frontend = (
<ApolloProvider store={store} client={client}>
<StaticRouter location={req.url} context={context}>
<Routes maintenanceMode={IN_MAINTENANCE_MODE} />
</StaticRouter>
</ApolloProvider>
);
// styled-components
const sheet = new ServerStyleSheet();
renderToStringWithData(sheet.collectStyles(frontend))
.then(content => {
if (context.url) {
res.redirect(301, context.url);
return;
}
const state = store.getState();
// react-helmet for meta tags
const helmet = Helmet.renderStatic();
res.status(200);
// Compile the HTML and send it down
res.send(
constructHTML({
content,
state,
styleTags: sheet.getStyleTags(),
metaTags:
helmet.title.toString() +
helmet.meta.toString() +
helmet.link.toString(),
})
);
res.end();
})
.catch(err => {
console.log(err);
res.status(500);
res.end();
throw err;
});
}; |
This issue has been automatically marked as stale because it has not had recent activity. It will be closed if no further activity occurs. Thank you for your contributions to Apollo Client! |
Argg, not stale. Thank god I can label it myself now... |
This issue has been automatically marked as stale because it has not had recent activity. It will be closed if no further activity occurs. Thank you for your contributions to Apollo Client! |
Wait that still doesn't work, wat |
Still not stale. |
@mxstbr What version of apollo you use ? I have same issue with apollo-client@2.1.0 then I realized that I did not migrated code from 1.0 correctly.
to
solved my problem. Hope this help :) |
I'm on 1.0 |
This issue persists with Apollo 2.0 if you use const client = new ApolloClient({
link,
cache: new InMemoryCache().restore(window.__APOLLO_STATE__),
defaultOptions: {
query: {
fetchPolicy: "cache-and-network",
},
},
ssrForceFetchDelay: 1000,
}); It would be nice to be able to use "cache-first" as the network policy for the first second after SSR render, then switch to "cache-and-network" after - an option like |
It seems like the issue could be solved around here: apollo-client/packages/apollo-client/src/ApolloClient.ts Lines 209 to 211 in 3373141
Maybe something like this would work: if (this.disableNetworkFetches && (
options.fetchPolicy === 'network-only' || options.fetchPolicy === 'cache-and-network'
)) {
options = { ...options, fetchPolicy: 'cache-first' } as WatchQueryOptions;
} Would a PR performing this change be considered? |
Seems like an easy fix for a long open issue |
I'd be more than happy to fix this in a PR if I can get a collaborator to just give me the okay. |
@benjie go for it! |
@benjie any update on this ? |
Sorry I’ve not had time :( A potential fix is detailed above though, feel free to give it a go yourself. |
#3372 should be reviewed and merged. |
@ciscorn It will be reviewed shortly (it's on my radar for hopefully later today). Thanks! |
Any news? |
Was this ever fixed? I'm still getting the exact same issue @mxstbr was reporting, where cache-and-network queries refetch once the client bundle loads in. |
Is it still open or merged? |
Having this issue too. My use case was: View page -> Edit -> Save -> Back to View. It was still returning the cached version. Ended up using a query search param: let fetchPolicy = "cache-first";
if (this.props.location.search === "?updated") {
fetchPolicy = "network-only";
}
<Query
// ...
fetchPolicy={fetchPolicy}
>
// .... |
Has anyone a workaround for this issue? |
The solution to this problem is setting an appropriate
As stated on the Apollo SSR docu. |
Is this resolved? As the same is still reproducible. |
Boom! using |
Intended outcome:
I server-side render my React application. I used
renderToStringWithData
on the backend to get all the data, then serialized the state with Yahoosserialize-javascript
into the generated HTML and then added it to my custom Redux store as the initial state viawindow.__SERVER_STATE__
.I expected the client bundle to load in and not fetch anything.
Actual outcome:
The client bundle loads in and React rehydrates, then Apollo fetches
/api
even though it already has all the data from the initial state in the HTML.This seems backwards, since even with
cache-and-network
after the SSR'd first result I wouldn't expect apollo client to go to the network again on first render.How to reproduce the issue:
cache-and-network
Version
/cc @peterpme who alerted me of this issue
The text was updated successfully, but these errors were encountered: