I asked a question on StackOverflow today here.
I thunk about it harder afterwards and decided that this is a discussion worthy of a Git issue.
_Any_ error gets thrown in getDataFromTree.
Return the data with an error Array, without throwing an error.
Why?
Our GraphQL server speaks to multiple APIs and returns a graphQL response which is handled by Apollo UI.
Certain GraphQL errors are not considered critical, and we want to still render a page with it. E.g.
data: {
thing: {
id: "1234"
name: "Thing"
nonCriticalData: null
},
error: {
graphQLErrors: [
path: ['thing', 'nonCriticalData']
]
}
With the above response we get from apollo, we want to still render a page with thing.
I have followed https://github.com/apollographql/react-apollo/pull/488#issuecomment-284756892 and got to this point.
In my mind the errors should be returned and not thrown unless necessary, because not all errors are equal.
Ideally I would like to do the following
getDataFromTree(app)
.then(() => {
const content = ReactDOMServer.renderToString(app);
const { data } = client.getInitialState(); <------ Data (with errors)
// check for expected errors and render
return renderPage(data);
})
.catch(error => {
// handle unexpected errors
});
P.S. Thanks for an awesome library. Happy to help out if needed.
Hey @choonkending, sorry it took me so long to get around this issue. I agree with you 100% that this behavior isn't consistent, but because of the way server side rendering is implemented, we can't easily change it to do what you want. The reason is that the queries are all executed in a first pass on the route, then their data is stored in the cache. When you call renderToStringWithData, the data from the cache is used to render the tree synchronously. We don't cache errors, so there's currently no easy way to change this without either changing the way server side rendering works (make it asynchronous), or changing the way errors are handled in the store. Even if the rendering were identical on the server and the client given the same query, the initial react render on the client would be unaware of the errors (because they're not stored), so there would be an inconsistency there. ๐
We're planning improvements in the next version of the API that would make this problem go away, but for the moment I think it's quite difficult to change.
If you're very interested and motivated to work on this problem, just let me know, we could definitely use the help!
@helfer No worries! Thanks for getting back to us.
If you're very interested and motivated to work on this problem, just let me know, we could definitely use the help!
Definitely.
Just to get a better understanding:
@choonkending the cache normalizes all data, and when you run a query against the cache, the result is assembled from the normalized parts. That's a problem if errors don't have paths, because then there's no way to know which field an error is associated with, and no way to know if an error should be returned with the query run against the cache.
There's no discussion issue yet for the next version of the API, but I'm trying to put an initial post together soon. In the meantime, I think the best thing to do would be to write a use-case for each requirement that we can think of. Having SSR be consistent with client rendering is one use-case, and I think we should write that down and document it well, so we can have a discussion around it.
For that purpose, could you add a comment here, please? https://github.com/apollographql/react-apollo/issues/519
You should also feel free to write down other use-cases that haven't been mentioned yet (so far there are very few, I'm hoping people will add more to it, even current functionality of react-apollo that they are relying on).
This really needs to be talked through and fixed ๐ช After I clean up the tests and add better typings. I'll try to get around to error handling and SSR!
Just want to leave a comment in support of @choonkending proposal. I already handle errors in components that perform queries: if {props.data.error} return a component that shows the error's message (I'm talking about errors that are thrown from resolvers/connectors). So I expect the server to render the exact result that would be rendered on the client. I tried to use a workaround suggested in https://github.com/apollographql/react-apollo/pull/488#issuecomment-284415525 but it doesn't really work, because apolloClient.getInitialState() returns a data object without an error.
I would just like to say that this has become a big issue for me.
As my app has gotten more complex, i have gone down the route of having 2 bundles, and if a 404 then rendering 2nd bundle, and losing the SPA benefits.
If you could still keep query errors in state, and allow SSR of 404 pages, it would simply things massively
Hi @helfer
If you're very interested and motivated to work on this problem, just let me know, we could definitely use the help!
I'm very interested and motivated (as long as it's my impediment).
Could you let me know if you already started approaching this problem? If no, do you have some thoughts how you would approach it?
Hi @edoroshenko. That's awesome. @jbaxleyiii can probably tell you more about it since he designed it together with Tom and is now working on Apollo full time!
Thanks @helfer!
Do you guys have a slack channel or what is the best way to communicate?
@edoroshenko you bet! Check out http://dev.apollodata.com/community/ and join us on slack! I'd be happy to work together on finally bringing some better error support to Apollo and SSR in general!
The way I see the solution:
error-policy option, that would define what to do with errors in the cache. It could be either refetch or reuse. refetch is a default one and should work as it works now. So apollo should always do query even if there's an error in the cache. reuse should tell apollo client to respond with an error if it has one in the cache.graphql high order component we should always use reuse error policy first time and refetch on any subsequent request. This way we'll be able to transfer the state with errors from server to client and perform first client-side rendering based on it without extra queries to the server.This plan will affect apollo-client and react-apollo repos.
@jbaxleyiii , @helfer, what do you think about this plan?
Or do you have another ideas, how to approach this issue?
This issue has been automatically labled because it has not had recent activity. If you have not received a response from anyone, please mention the repository maintainer (most likely @jbaxleyiii). It will be closed if no further activity occurs. Thank you for your contributions to React Apollo!
This issue has been automatically closed because it has not had recent activity after being marked as no recent activyt. If you belive this issue is still a problem or should be reopened, please reopen it! Thank you for your contributions to React Apollo!
This is still a huge issue for us, how do I re-open it?
We've wasted a lot of time trying to figure out why our error handling doesn't work on SSR while it works fine in the browser. It should be documented somewhere that it isn't expected to work yet!
Same problem here. Any news? For example i want to throw error with info when user is not authenticated. This doesn't work with SSR getDataFromTree ๐ข
@jbaxleyiii any news on this since apollo 2.0 is coming, would be nice to see something like this sorted
This is still happening and it kinda breaks SSR.
when query works:
getDataFromTree => data = {loading: false, error: false, <key>: ...}
when query fails:
getDataFromTree => error thrown and all data discarded {error}
This makes that after SSR there is no error, loading is set to true and every <key> needed is empty.
I think it should work kinda like this
getDataFromTree => data = {loading: false, error: true, <key>: ...}
Because if it doesnt, the whole app lose consistency.
@jbaxleyiii Can this issue please be re-opened?
Any news on this? Having the same issue with react-apollo 2.0
Hey peeps, just giving an update of the approach we've taken in my team since this issue was created. Hopefully it helps if it applies to your use case!
We opted to rely less on react-apollo when it came to data fetching. (I'm not bagging the good work that's been done on react-apollo! It's just the way we decided to go from a technical perspective)
Use ApolloClient.query in ApolloClient instead
Use errorPolicy: all in errorPolicies
๐ Returns data + errors
You should be able to handle server side rendering with just apollo client!
I hope this helps!
I'm having the same problem. My ideal scenario would be if getDataFromTree would fill the cache and afterwards I can choose if I want to render the app with the existing cache (e.g. rendering errors or something smarter if components have errorPolicy configured) or not render the app at all (current situation).
@jbaxleyiii Based on the involvement of the community in this thread, would you mind reopening this issue?
Re: point 2 on @choonkending's comment above:
Use errorPolicy: all in errorPolicies
๐ Returns data + errors
I don't think this is working correctly currently. See #1781
Any update on this? This is a huge problem as it causes an entire page to error if a single query fails.
Our only other option is to basically not handle errors at all.
Our solution was just to import the node module manually (folder in our
repo) and comment out the line that throws. Hope this helps.
On Thu, Apr 26, 2018, 12:10 PM Sasha Solomon notifications@github.com
wrote:
Any update on this? This is a huge problem as it causes an entire page to
error if a single query fails.Our only other option is to basically not handle errors at all.
โ
You are receiving this because you are subscribed to this thread.
Reply to this email directly, view it on GitHub
https://github.com/apollographql/react-apollo/issues/615#issuecomment-384698855,
or mute the thread
https://github.com/notifications/unsubscribe-auth/AIvoQZOIrNstbV6AMWuf9F7j01Wf8Ow_ks5tsfFdgaJpZM4M4m2s
.
This is a problem for me, too. As a sort-of temporary workaround I've been experimenting with catching errors in my resolvers and adding a custom error field to pass the data down as "regular data".
Basically I have a GraphQL Error type:
type Error {
type: String
message: String
}
And I can add that to types as needed, based mostly on the underlying queries:
type Widget {
id: String
error: Error
name: String
description: String
}
# etc...
type Query {
widget(id: String): Widget
}
Then in my resolver:
const widgetResolvers = {
Query: {
widget(_, { id }) {
try {
return Widget.find(id);
} catch(err) {
return {
id,
error: {
type: 'UNKNOWN', // Or something meaningful
message: err.message, // Maybe display this client-side?
}
}
}
}
}
}
Then in my component I can check myQuery.widget.error (instead of myQuery.error, which would be the case with the built-in error handling) to determine if there is some sort of error. I think you actually would want to check for _both_ error fields and handle either or.
It's not ideal. And it requires wrapper types for certain queries. For example, instead of being able to just return an array:
type Query {
allWidgets(): [Widget],
}
I've got to do something like:
type WidgetCollection {
error: Error,
widgets: [Widget]
}
type Query {
allWidgets(): WidgetCollection,
}
I haven't gotten too deep with this yet, so I'm not sure if this is going to be a viable workaround or not. But I'm not sure how else to handle it until there is a proper fix. Perhaps ApolloLink could be used to inject the error data some how?
Still same error on renderToStringWithData error data can't reach to components on SSR. If i apply errorPolicy: all, i'm getting error undefined any updates?
Given that this is still around, this issue should definitely be reopened.
This issue should be reopened. I am using a similar setup as described in the question and every time there is an error, the .catch() statement logs the error to the console and stops the entire page from rendering. Any help would be appreciated.
Here's one approach to dealing with this. Just ignore errors on the server, but make sure you handle them in your components;
// Ignore errors
const errorLink = onError(({ graphQLErrors, networkError, response, operation, forward }) => {
return forward(operation);
});
// Create Apollo client and link to schema
const client = new ApolloClient({
ssrMode: true,
cache,
link: ApolloLink.from([
errorLink,
stateLink,
new SchemaLink({
schema,
context: { req, res },
rootValue: {
currentUser: req.user ? req.user : null
}
})
])
});
@mhaagens but how do you handle the errors in your components?
If the app errors on the server I add the error to apollo local state in a second pass from a try/catch handler and then render a generic error component in the app. I can then pick up that error state on the client as well. If it errors on the client I render the same component with the client side error instead.
Basic flow;
Now make sure you don't follow this example to the letter in production, you don't want to leak those errors to the client, but you can use the same idea.
SSR Route handler;
export default async (req, res) => {
try {
// Render response from React app
const response = await createResponse(req, res);
res.send(response);
} catch (e) {
try {
// If errored run second pass to get React app error response
const serverContext = { error: JSON.stringify(e) };
const response = await createResponse(req, res, serverContext);
res.send(response);
} catch (e) {
// Couldn't render error response with React
res.send("Something went very very wrong. Failed to catch error.");
}
}
};
Top level app component;
{this.state.errors || this.props.clientState.serverError ? (
<ServerError clientErrors={this.state.errors} serverError={this.props.clientState.serverError} />
) : <App />}
any update?
It really sucks. This situation disappoints me so much. Just never use Apollo, React-Apollo and other stupid stuff!
Please, reopen the issue.
Wow - sorry, this issue should have been re-opened a while ago ๐ณ. I'll digest everything here and hash out a plan of attack based on how getDataFromTree works now (since as of react-apollo 2.3.0 it's using ReactDOM.renderToStaticMarkup to be compatible with React Hooks). I'll post back with the details, and if anyone is interested in working on this, that would be awesome!
Thanks a lot @hwillson, Apollo is great, this is just the last pain point for me right now
Was this issue closed by an accident or is it expected?
@nilfalse Closed intentionally - the changes in https://github.com/apollographql/react-apollo/pull/2753 should fix this. We'll be pushing out a new version with these changes next week.
We'll be pushing out a new version with these changes next week.
Wasn't #2753 published in 2.4.1?
network errors thrown from [email protected] still causes getDataFromTree to throw in 2.4.1. Just wondering does apollo-link-rest throws errors incorrectly or is there any other issue?
@gbiryukov i believe the fix is only for GraphQL errors, as Network errors are realistically a different thing all together and shouldn't be swallowed.
aah, the issue with this idea is that it works different on client.
when network error occurs in browser โ component receives error prop.
when network error occurs on server getDataFromTree just throws.
this makes impossible to handle errors consistently on both environments
Well there is a lot of debate around this, but i think the current solution of network errors throwing and graphql errors being passed to props is correct in my opinion.
We have full consistency with graphql errors on both environments, and that's the important part, because that part is what we realistically control, we don't control the network (if you get what l mean).
My solution is if l hit a network error, refresh the page. My server should then throw an error if it still is a network error, so l can redirect to say a status page.
react-apollo version: 3.1.1 I can confirm using ApolloError while using renderToStringWithData or getDataFromTree just throws it and doesn't pass down the error as intended. I am rerendering without apollo on catch statement of renderToStringWithData and letting the client redo the request.
@kaanozcan do you have errorPolicy: all set?
That indeed works. Thanks @OllieJennings
I was stuck on this forever. It seems possible that if you're using useQuery hooks that there are better way to handles this. Since the project I'm working on was built before hooks was introduced, I had to look for an alternative method.
The problem seemed to stem from rendering Apollo providers with a null Apollo state. It seemed to get stuck in an endless loop, thus the unresponsive server.
To get around this, I evaluated if the apolloState was empty or not. If it was in fact empty, that pointed to an Error in getDataFromTree.
From there, knowing I had an error, I'd render my app (_app) with an error prop passed in. The error flag stops the ApolloProviders from rendering, and the app from rendering, and instead returns an ErrorComponent. This has stopped network errors from bombing my server.
It's not pretty, and I don't love it, but it means that the server won't require a manual restart in the event of a backend outage, and that I can render a consistent error message. I believe it won't automatically resolve on a resumed connection + navigation to another page, because the client does not have any other routes in this. This means you'd need to refresh the browser to clear the error.
I'll post some samples below in case it will help anyone, and I'm open to additional opinions!
// withApollo.js
....
render() {
const isApolloEmpty = Object.keys(this.props.apolloState).length
return (
<App
{...this.props}
error={!isApolloEmpty}
apolloClient={this.apolloClient}
/>
);
}
...
//_app.js
render() {
const { Component, pageProps, apolloClient, error } = this.props;
if (!error) {
return (
<Container>
<ApolloProvider client={apolloClient}>
<ThemeProvider theme={theme}>
<AppWrapper>
<Component {...pageProps} />
</AppWrapper>
</ThemeProvider>
</ApolloProvider>
</Container>
);
} else {
return (
<Container>
<ThemeProvider theme={theme}>
<Error statusCode={"A Network Error has Occured"} />
</ThemeProvider>
</Container>
);
}
}
Most helpful comment
This is still a huge issue for us, how do I re-open it?
We've wasted a lot of time trying to figure out why our error handling doesn't work on SSR while it works fine in the browser. It should be documented somewhere that it isn't expected to work yet!