Apollo-client: Query Error Handling

Created on 19 Jul 2016  路  8Comments  路  Source: apollographql/apollo-client

I am using apollo server and apollo client. Here is a resolver passed to apollo server:

loginUser: async (root, { email, password }) => {
      const users = await User.filter({ email });
      if (isEmpty(users)) {
        throw new Error('No user with that email address exists.');
      }
      const user = head(users);
      console.log(await user.validatePassword(password));
      if (!await user.validatePassword(password)) {
        throw new Error('Invalid email or password.');
      }
      const authToken = user.signJwt();
      return { user, authToken };
    }

and my query

client.query({
      query: gql`
        query User($email: Email!, $password: Password!) {
          loginUser(email: $email, password: $password) {
            user {
              id
              email
            }
            authToken
          }
        }
      `,
      variables: {
        email: email.value,
        password: password.value
      }
    }).then((result) => {
      console.log('got result: ', result);
    }).catch((error) => {
      console.log('there was an error sending the query: ', error);
    });

Everything works correctly, however when the resolve method throws an Error, the client console prints QueryManager.js:409 Uncaught (in promise) ApolloError {graphQLErrors: Array[1], networkError: undefined, message: "GraphQL error: No user with that email address exists."}.

What am I doing wrong? The .catch method catches the error and logs it, but I am also receiving this uncaught (in promise) error. How can I avoid this?

馃悶 bug

Most helpful comment

OK, a tentative work around is the following:

handleFormSubmit = ({variables, mutation}) => {
  // The catch below prevents the unhandled promise rejection ...
  mutation({variables}).catch(error => console.log("An error", error));
}

...

<Mutation ...>
  {mutation => <SomeForm onSubmit={variables => handleFormSubmit({variables, mutation})}/>}
</Mutation>

I wanted to share this to help others.

However, this is a violation of the principle of least surprise - if I'm already handling the error in the Mutation child function or its onError property callback, I shouldn't also need to handle it when invoking the mutation. This is very confusing and should be fixed by the Apollo team - it looks like you're still double-rejecting the promise?

All 8 comments

Good catch! I think just one small change would fix this. We shouldn't call reject here, since we already dispatched an error action: https://github.com/apollostack/apollo-client/blob/378cbbe6054d0bae2b4ede2387d19dc2d1927567/src/QueryManager.ts#L732

We already identify unhandled errors here: https://github.com/apollostack/apollo-client/blob/378cbbe6054d0bae2b4ede2387d19dc2d1927567/src/QueryManager.ts#L294

Please submit a PR for this, or otherwise we will try to get to it ASAP.

Should be fixed with #445. If it isn't, we can re-open the issue.

Released in 0.4.8!

@octohedron Please open a new issue with an accurate description of the error you're experiencing.

I am still seeing this bug. Is this not fixed in the latest ApolloClient?

OK, a tentative work around is the following:

handleFormSubmit = ({variables, mutation}) => {
  // The catch below prevents the unhandled promise rejection ...
  mutation({variables}).catch(error => console.log("An error", error));
}

...

<Mutation ...>
  {mutation => <SomeForm onSubmit={variables => handleFormSubmit({variables, mutation})}/>}
</Mutation>

I wanted to share this to help others.

However, this is a violation of the principle of least surprise - if I'm already handling the error in the Mutation child function or its onError property callback, I shouldn't also need to handle it when invoking the mutation. This is very confusing and should be fixed by the Apollo team - it looks like you're still double-rejecting the promise?

Also ran into this bug when returning unauthorized response (with React Native). Followed suite and not adding a catch() on our mutations to handle this promise.

Would love to just have our error component deal with this instead.

Was this page helpful?
0 / 5 - 0 ratings