-
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
Errors during SSR are rendered as "loading" #3897
Comments
This is a big issue since this messes with SEO, loading pages being index etc. Apollo is swallowing errors, even though error handling has been implemented simply because it is not being propagated when doing SSR. It works perfectly in the client. Is there any plans on fixing this? Or on how to do this the right way with apollo? |
@JohannesHome Apollo Client caches the objects received from query or mutation responses by their ids (normalization); it doesn't cache metadata about the actual network requests such as HTTP status. Where would that data live, if a response gets torn into multiple separately cached pieces? A query component might render data pieced together from cache from 5 separate requests. Loading and error states are local component state that can't be serialized on the server for hydration on the client. I doubt you will be able to SSR errors with Apollo anytime soon. Some of my design goals for |
@jaydenseric what are you saying? My question: When an error occurs that is passed through by the |
Server side rendering happens in 2 phases, firstly |
Sorry english is not my native tongue. So your saying no? |
No, the behavior is not exactly the same on the server as the client. Even if it was, due to the Apollo cache format it has no way to communicate that there were loading errors to the client. So the client would notice some data is missing for some components and attempt to load them, only to discover the same errors the server discovered. |
Looks to me like this issue is a duplicate to apollographql/react-apollo#2134 I fixed the issue for me through catching the error from |
I've opened a PR to fix the docs with respect to the incorrect guidance around |
This should no longer be an issue with current versions of |
And yet it still is. |
This is still an issue. If you need a repo case, let me know and I can get you one. |
@jacob-ebey If you can provide a repro using React Apollo 3, that would be great! |
@hwillson, Here is a repro case: https://github.com/jacob-ebey/apollo-ssr-bug Just run (fyi, if you guys are hiring, I'd love to contribute to this project in a professional capacity) |
Thanks for the reproduction @jacob-ebey! We're definitely hiring - all of our open positions are here, but the position that's most directly related to this repo is here. Thanks again! |
I did a tiny bit of preliminary digging and it appears that in both ssr/getMarkupFromTree as well as hooks/ssr/consumeAndAwaitPromises have zero error handling for promises. Adding in a concept of partial success instead of using Promise.all may be a potential solution here: packages/hooks/src/ssr/RenderPromises.ts public consumeAndAwaitPromises() {
const promises: Promise<any>[] = [];
this.queryPromises.forEach((promise, queryInstance) => {
// Make sure we never try to call fetchData for this query document and
// these variables again. Since the queryInstance objects change with
// every rendering, deduplicating them by query and variables is the
// best we can do. If a different Query component happens to have the
// same query document and variables, it will be immediately rendered
// by calling finish() in addQueryPromise, which could result in the
// rendering of an unwanted loading state, but that's not nearly as bad
// as getting stuck in an infinite rendering loop because we kept calling
// queryInstance.fetchData for the same Query component indefinitely.
this.lookupQueryInfo(queryInstance).seen = true;
promises.push(promise);
});
this.queryPromises.clear();
const results = await Promise.all(promises.map(p => p.catch(e => e)));
const processed = results.reduce((p, c) => {
if (!(c instanceof Error)) {
return [[...p[0], c], p[1]];
} else {
return [p[0], [...p[1], c]];
}
}, [[], []]); // Returns valid results in index 0 and errors in index 1
return processed;
} |
any update on this ? I'm still having this issue |
Im having the same issue and made a nextjs reproduction in apollographql/react-apollo#3918 I added the bug in the apollo-react part because of the treewalker and its unclear for me where the real problem is. So thats why i will reference my bug to this one as well :) Reading the comment above #3897 (comment) makes me think this will not be an easy fix :( p.s. as @kachkaev is mentioning "When a request fails during getDataFromTree(), the errors are suppressed with catch", according to errorPolicy if you set it to 'all' you don't need to catch it. But you will still have same outcome, no error passed :( |
Hi, I've struggled for a while with this bug, in the context of Next too. Still not completely sure what happens.
It's kinda disturbing at first but at least the server is showing a clear error message, so that's a sensible behaviour But... that's where things are becoming fishy:
|
After spending a bunch of time debugging, I was able to figure out that the issue is specific to the getDataFromTree() SSR method whereas the renderToStringWithData() SRR method doesn't have the issue and behaves as expected. Here are some sandboxes demonstrating the difference on apollo client v3 (I was also able to repro on v2): 👎 getDataFromTree() method sandbox: https://codesandbox.io/s/react-apollo-ssr-error-isnt-cached-getdatafromtree-method-upgraded-to-apolloclient3-p0k1y getDataFromTree() The
renderToStringWithData() This is in contrast with the It looks like renderToStringWithData() is the more correct/consistent method for SSR until the apollo cache starts caching error for getDataFromTree() method to work as expected. |
Thanks all for your patience on this issue. The team is prioritizing addressing SSR use cases, see #10231 and feel free to leave feedback! |
I'm writing a Next.js app that's based on their
with-apollo
example and would like to share a confusion with the server-side error handling. I believe it might be either a bug in apollo client or something we can consider as a feature request.In the example, the logic of a HOC that deals with Apollo data is located in with-apollo-client.js and mine is not very different. When the code runs on the server,
getDataFromTree()
is called and then the React tree renders using the data fromapollo.cache.extract()
. This works fine if all requests have succeeded – the server sends a fully rendered tree with all the data, that's cool.When a request fails during
getDataFromTree()
, the errors are suppressed withcatch
. This is generally fine, because we do not want a small faulty widget in a side panel to crash the whole page. However, becauseapollo.cache.extract()
does not contain any information about the failed queries, failed query components render as if the data is still being loaded.This peculiarity is a bit hard to spot in Next.js, because client-side rendering follows the server-side one and
loading...
gets replaced with a real error quickly (no data in cache → new query → render loading → register error → render error). However, this kind of "fix" makes the situation even harder to realise rather than resolved.Imagine I have a page with a blog post, which is identified by a slug in the current URL. Unlike for a small side-widget I mentioned above, the success of a query that gets me the post is critical to the page. In this case, a failure means it's either 404 or 500:
The errors that are thrown here get handled by Next's
_error.js
, which can render404 - Post not found
or500 - App error, please reload
using some custom logic. In neither case I want the server to return 200 even though the error will pop out on the client side shortly – search engines won't like this. The code above renders a proper 404 page whendata.blogPost
is null, but if a graphql server goes down for some time, all my blog posts - existing or non-existing - will return 200 and this can quickly ruin google search results for my website.How to reproduce the issue:
Install
with-apollo
exampleAdd this line to
components/PostList.js
:function PostList ({ data: { loading, error, allPosts, _allPostsMeta }, loadMorePosts }) { + console.log('RENDERING POST LIST', { loading, error: !!error, allPosts: !!allPosts }); if (error) return <ErrorMessage message='Error loading posts.' />
Launch it using
yarn dev
and openlocalhost:3000
in a browser. You will see:This looks fine.
Simulate a graphql server failure (e.g. open
lib/init-apollo.js
and replaceapi.graph.cool
withunavailable-host
).Reload the page. You will still see
Error loading posts.
, however your logs will say(instead of
loading: false, error: true
on the server)Turn off javascript in the browser via dev tools and reload the page.
Expected visible content:
Error loading posts.
Actual visible content:
Loading
Versions
Meanwhile, my current workaround for critical server-side queries that are not allowed to fail will be something like this:
I don't see how I would render server-side 500 instead of 200 otherwise and I can also imagine that quite a few developers are not aware of what's going on 🤔
WDYT folks?
The text was updated successfully, but these errors were encountered: