question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. It collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

Handling promise errors from mutations client-side

See original GitHub issue

I have looked everywhere for a solutions for this…

I have a mutation that is checking for run-time validation errors and throwing those back to the client. The problem I am having is my client side code does not give me back the errors array.

In the network tab in Chrome I can see the following:

{"data":{"addMount":null},"errors":[{"message":"List or Action not found.","locations":[{"line":2,"column":3}],"path":["addMount"]}]}

That is what I want. It is also coming back as a 200 response.

The console in Chrome shows:

  errors:  Error: GraphQL error: List or Action not found.
    at new ApolloError (bundle.js:10238)
    at bundle.js:32100
    at <anonymous>

What I should get back is an array of errors, but i just get an normal error. What happened to the errors array?

Client side call:

  AddMount({
      variables: {
        id: mountId,
        parent,
        parentType,
        owner,
      },
    })
    .then((data) => {
      console.log('data: ', data);
    })
    .catch((errors) => {
      console.log('errors: ', errors);
    });

Here is my mutation:

  addMount: {
      type: ActionType,
      args: {
        id: { type: new GraphQLNonNull(GraphQLString) },
        parent: { type: new GraphQLNonNull(GraphQLString) },
        parentType: { type: new GraphQLNonNull(GraphQLString) },
        owner: { type: new GraphQLNonNull(GraphQLString) },
      },
      resolve(parentValue, { id, parent, parentType, owner }, req) {
        console.log('adding mount...');
        console.log('id: ', id);
        console.log('parent: ', parent);
        console.log('parentType: ', parentType);
        console.log('owner: ', owner);

        return new Promise((resolve, reject) => {

          // there can be only one mount of this id per parent
          Action.findOne({ mount: id, parent })
          .exec((err, existingMount) => {
            if (err) reject(err);

            console.log('existingMount: ', existingMount);
            if (existingMount) {
              reject('That mount already exists.');
            } else {
              let actionLookup = null;
              let listLookup = null;
              const queries = [];

              queries.push(new Promise((resolver) => {
                Action.findById(id)
                .exec((err, res) => {
                  if (err) reject(err);
                  console.log('actionLookup res: ', res);
                  actionLookup = res;
                  resolver(res);
                })
              }));
              queries.push(new Promise((resolver) => {
                List.findById(id)
                .exec((err, res) => {
                  if (err) reject(err);
                  console.log('listLookup res: ', res);
                  listLookup = res;
                  resolver(res);
                })
              }));

              // when all queries are done
              Promise.all(queries).then(() => {
                console.log('actionLookup: ', actionLookup);
                console.log('listLookup: ', listLookup);

                // must be either a list or action
                if (!listLookup && !actionLookup) {
                  reject('List or Action not found.');
                } else {
                  const type = listLookup ? 'list' : 'action';
                  console.log('type: ', type);

                  const parentModel = parentType === 'list' ? List : Action;
                  parentModel.findById(parent)
                  .exec((err, parentObj) => {
                    if (err) reject(err);

                    console.log('parentObj: ', parentObj);
                    if (!parentObj) {
                      reject('Parent was not found.');
                    } else {
                      const action = new Action({
                        mount: id,
                        type,
                        parent,
                        parentType,
                        owner,
                        order: parentObj.actions.length + 1,
                      });

                      action.save((err, action) => {
                        if (err) reject(err);

                        parentModel.findOneAndUpdate({
                          _id: parent,
                        },
                        { $push: { actions: action._id } },
                        (err, res) => {
                          err ? reject(err) : resolve(action);
                        });
                      });
                    }
                  });
                }
              });
            }
          });
        });
      },
    },

Issue Analytics

  • State:closed
  • Created 6 years ago
  • Comments:5 (1 by maintainers)

github_iconTop GitHub Comments

3reactions
helfercommented, Jun 16, 2017

Hi @appjitsu 👋

Apollo Client currently returns either data or an error, not both (this is because we couldn’t cache errors in the normalized store in a meaningful way without a path). However, on the error you get, there should be a property that lets you access the original errors array: error.graphqlErrors. If it wasn’t a GraphQL error, that property will of course not contain any errors, so make sure to watch out for that.

0reactions
AhmedQamar10commented, Feb 6, 2019

Hi @helfer 👋 import React, { Component } from ‘react’; import { View, Text, TouchableOpacity, AsyncStorage, TextInput, Keyboard } from ‘react-native’; import Icon from ‘react-native-vector-icons/MaterialIcons’ import { graphql,compose } from ‘react-apollo’ import loginmutation from ‘…/graphql/mutations/signin’ import { connect } from ‘react-redux’; import { login } from ‘…/actions/user’; import Loading from ‘…/components/loading’ class SigninForm extends Component {

state = {
    loading:false, 
    email: '', password: '', 
}
_onChangeText = (text, type) => this.setState({ [type]: text })
_checkIfDisabled() {
    const { email, password, } = this.state;

    if (!email || !password) {
        return true;
    }

    return false;
}
_onSignupPress = async () => {
    this.setState({ loading: true });

    const { email, password } = this.state;
    //const avatar = 'http://i65.tinypic.com/mrb979.png';

    try {
        const { data } = await this.props.mutate({
            variables: {
                email,
                password,
                //avatar,
            },
        });
        await AsyncStorage.setItem(
        '@twitteryoutubeclone', data.login.token);
         this.setState({ loading: false });
        return this.props.login();
    } catch (error) {
        throw error;
    }
};

render() {
    if (this.state.loading) {
        return <Loading />;
    }
    return (
        <View
            onPress={() => Keyboard.dismiss()}
            style={{
                flex: 1, justifyContent: 'center',
                alignItems: 'center',
                backgroundColor: '#353b48', position: 'relative'
            }}>
            <TouchableOpacity
                hitSlop={{ top: 20, bottom: 20, right: 20, left: 20 }}
                style={{
                    zIndex: 1,
                    justifyContent: 'center',
                    alignItems: 'center', position: 'absolute',
                    top: '5%', left: '5%'
                }} onPress={this.props.onBackPress}>
                <Icon color='white' size={30} name="arrow-back" />
            </TouchableOpacity>
            <View style={{
                flex: 1,
                alignSelf: 'stretch', alignItems: 'center',
                justifyContent: 'center',
            }}>                    
                <View style={{
                    height: 50, width: '70%', borderBottomWidth: 2,
                    borderBottomColor: 'white', marginVertical: 5
                }}>
                    <TextInput style={{
                        height: 50, fontSize: 30,
                        color: 'white'
                    }}
                        placeholder="Email" keyboardType="email-address"
                        autoCapitalize="none"
                        onChangeText={text => this._onChangeText(text, 'email')}
                    />
                </View>
                <View style={{ paddingBottom: 40, }}></View>
                <View style={{
                    height: 50, width: '70%', borderBottomWidth: 2,
                    borderBottomColor: 'white', marginVertical: 5
                }}>
                    <TextInput style={{
                        height: 50, fontSize: 30,
                        color: 'white'
                    }}
                        placeholder="Password" secureTextEntry
                        onChangeText={text => this._onChangeText(text, 'password')}
                    />
                </View>
                <View style={{ paddingBottom: 160 }}></View>

            </View>
            <TouchableOpacity
                onPress={this._onSignupPress}
                disabled={this._checkIfDisabled()}
                style={{
                    justifyContent: 'center',
                    alignItems: 'center', position: 'absolute', bottom: '15%',
                    width: '70%', height: '10%', backgroundColor: '#0097e6',
                    borderRadius: 10, shadowOpacity: 0.2, shadowRadius: 5,
                    shadowOffset: '0px 2px', shadowColor: '#000', elevation: 2
                }} >
                <Text style={{
                    fontSize: 30,
                    borderRadius: 10, justifyContent: 'center',
                    alignItems: 'center', color: 'white', fontWeight: '900'
                }}>Login</Text>
            </TouchableOpacity>
        </View>
    );
}

}

export default compose(graphql(loginmutation), connect(undefined, { login }))( SigninForm, );

Possible Unhandled Promise Rejection (id: 0): Error: GraphQL error: User not exist! at new ApolloError

Read more comments on GitHub >

github_iconTop Results From Across the Web

Handling operation errors - Apollo GraphQL Docs
Apollo Client can encounter a variety of errors when executing operations on your GraphQL server. Apollo Client helps you handle these errors according...
Read more >
Apollo client mutation error handling - graphql - Stack Overflow
I'm using GraphQL and mongoose on the server. When a validation error occurs the GraphQL mutation sends a ...
Read more >
Handling Errors with Apollo Client (React) - YouTube
In this video I talk about handling errors when running queries and mutations using Apollo Client in a React project.Table of contents:00:00 ...
Read more >
The Definitive Guide to Handling GraphQL Errors | by Matt Krick
Remember, this works perfectly for mutations, but queries and subscriptions swallow errors unless they're thrown, which means if you want it in ...
Read more >
Error handling with promises
Asynchronous actions may sometimes fail: in case of an error the corresponding promise becomes rejected. For instance, fetch fails if the remote server...
Read more >

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found