42

I'm using Apollo-client and I don't understand why this is so hard to debug my error: I'm trying to perform a mutate call on my graphene python implementation of GraphQL. It end up with a 400 error code, and I have no way to retrieve the info.

For example: when I try my graphene service on the /graphQL interface, it return me helpful errors as bad input or bad methode name. Here, on apollo client, It only throw me a 400 code error. That doesn't help me at all. So is it possible to have the error info from graphene from my apolo client, instead of an unhelpful 400 code error?

Here an example, from my graphene interface (/graphQL):

mutation myMutation {
  uploadFile(input:"") {
    success
  }
}

will output:

{
  "errors": [
    {
      "message": "Argument \"input\" has invalid value \"\".\nExpected \"UploadFileMutationInput\", found not an object.",
      "locations": [
        {
          "column": 20,
          "line": 2
        }
      ]
    }
  ]
}

When I try the same on apollo client (js):

client.mutate({
  mutation: gql`
    mutation($file: Upload!) {
      uploadFile(input: $file) {
        success
      }
    }
  `,
  variables: { file } })
  .then(console.log)
  .catch((e) => { console.log("catch", e) })

I get Error: Network error: Response not successful: Received status code 400

My catch is never called, and the documentation is not helping me at all.

What I want is get the mutation detail errors of my calls: When I use improper method call or bad input type, I should not fall in a 400 without any information of what made my request bad.

Mr Bonjour
  • 3,330
  • 2
  • 23
  • 46
  • this is supposed to be done in your mutation options with `options: () => ({ errorPolicy: 'all' }),` but I'm still unsuccessful... I don't understand why it isn't documented with a clear example. – MacKentoch Mar 23 '18 at 21:18

11 Answers11

43

Error and console.log surprises

When trying to use console.log to view an error, you might be surprised to not see all the error data available. This is because console.log treats values which are instances of the built-in Error class in a special way, printing only the message, the type, and the stack.

Apollo extends its own errors from the built-in Error class. This can be easily tested by:

const error = apolloError // from async try/catch, onError method, or a promise .catch

console.log(error instanceof Error);
// --> true

When extending from Error, Apollo will add it's own data to the object. Yet the error will still be an instance of Error, and thus console.log will only display a limited amount of information,

console.log(error);

// output:
// Error: Network error: Response not successful: Received status code 400
//     at ...
//     at ...
//     at ...

Suggestion (solution?)

If you know where to look, you can just do a property lookup on the error object. For example, you can log error.networkError.result.errors. This approach may be too surgical and lead to an incomplete picture of all the things that went wrong.

Alternatively, we can use JSON.stringify on the error object, and the entirety of the data will be printed to the console,

// tip: use stringify's formatting options for better readability
console.log(JSON.stringify(error, null, 2));
{
  "graphQLErrors": [],
  "networkError": {
    "name": "ServerError",
    "response": {},
    "statusCode": 400,
    "result": {
      "errors": [...here you will find what you're looking for]
    }
  },
  "message": "Network error: Response not successful: Received status code 400",
  "name": "Error",
  "stack": "...same as before"
}

and you'll be able to tell at a glance what went wrong.

aryzing
  • 4,982
  • 7
  • 39
  • 42
  • 2
    Thank you for this comment! I did not know where to look, and the `error.networkError.result.errors` had the clear error I needed! – egervais7 Jul 24 '20 at 15:39
24

You should use this link: https://www.npmjs.com/package/apollo-link-error when instantiating ApolloClient. It should go before other links, like so:

import { ApolloClient, ApolloLink } from 'apollo-boost'
import { onError } from 'apollo-link-error'

const errorLink = onError(({ graphQLErrors }) => {
  if (graphQLErrors) graphQLErrors.map(({ message }) => console.log(message))
})

new ApolloClient({
  ...
  link: ApolloLink.from([errorLink, authLink, restLink, httpLink]),
})
Federico Grandi
  • 6,785
  • 5
  • 30
  • 50
Yan Takushevich
  • 1,843
  • 2
  • 18
  • 23
  • why does it have to go before other links? – Vladyslav Zavalykhatko Oct 08 '19 at 09:35
  • @VladyslavZavalykhatkoI think it was stated somewhere in apollo docs. Was very long ago, so not sure though. – Yan Takushevich Oct 08 '19 at 15:16
  • @YanTakushevich I don't have all the links after errorLink. When I include no links (how I have it now) my queries work but when I include the errorLink then I can no longer query the db. Do you have any suggestions? – financial_physician Jun 06 '21 at 08:38
  • @financial_physician if you are using '@apollo/client' you should use https://www.apollographql.com/docs/react/api/link/apollo-link-error/ instead. For more info refer to this section: https://www.apollographql.com/docs/react/api/link/apollo-link-error/ – Yan Takushevich Jun 17 '21 at 10:10
16

This solution solved it for me:

const client = new ApolloClient({
  uri: 'http://localhost:4444/graphql',
  onError: ({ networkError, graphQLErrors }) => {
    console.log('graphQLErrors', graphQLErrors)
    console.log('networkError', networkError)
  }
})
Fellow Stranger
  • 32,129
  • 35
  • 168
  • 232
  • This solution is effectively the same as @Yan Takushevich's above, but doesn't require any additional libraries. Apollo provides documentation here: https://www.apollographql.com/docs/react/data/error-handling/ – gortron Nov 11 '20 at 00:30
  • 1
    This isn't correct anymore, put this now like this: `new ApolloClient({ link: new HttpLink({ onError: ({ networkError, graphQLErrors }) => { console.log('graphQLErrors', graphQLErrors) console.log('networkError', networkError) } }), });` – julian libor Nov 11 '20 at 15:40
6

In my case there was a typo in the query. However it ended up in an 400 error.

To debug such cases you can simply use the following code:

function query() {
  try {
    /**
     * DO QUERY
     */
  } catch (e) {
    // Here you can catch any errors from Apollo Error.
    console.log(e.networkError.result.errors);
  }
}

Hint: GraphQL Errors will not always end up in graphQLErrors so it is a good idea to check the network errors together with your graphql errors. To see the GraphQL errors: console.log(e.graphQLErrors); Be aware that graphQLErrors will be an array.

Here is a full example:

async function userQueryInterface(userid = '1234', usermail = 'test@test.test') {
  try {
    let users = await client.query({
      query: gql`
        query GetUsers($userid: ID!, $usermail: String) {
          getUsers(vlanid: $userid, usermail: $usermail) {
            _id
            mail
          }
        }
      `,
      variables: { userid, usermail }
    });

    return users.data.getUsers;
  } catch (e) {
    console.log(e.networkError.result.errors); // here you can see your network
  }
}
Megajin
  • 2,648
  • 2
  • 25
  • 44
0

I managed to do it by putting an onError function in the client config directly (graphQLErrors is always empty when doing it in the mutation):

const client = new ApolloClient({
  uri: 'http://localhost:3000/graphql',
  onError: (e) => { console.log(e) },
});
Almaju
  • 1,283
  • 12
  • 31
  • It does work for me. Maybe you should explain your problem instead of just downvoting. – Almaju Sep 17 '18 at 12:45
  • 1
    Actually `Almaju` and `Fellow Stranger` answers are similar. In `Fellow Stranger` destruction of error object is done before passing to the function. @Almaju I would prefer to log the whole error object `console.log(e)`, bcz depending on the error, some child may or may not occur – Saahithyan Vigneswaran Jan 12 '19 at 17:14
0

None of the other suggested answers, let alone apollo-link-error and its code that you can allegedly copy and paste (and solve easily), worked for me.

I'm only posting this since I managed to solve this problem without the use of installing any additional packages or libraries or pasting in any code (that won't even work). And this is to remind myself should I google this problem again.

The easy solution: Look in the Network tab of the console in Google Chrome. You can see the stack trace and more details of the error.

pizzae
  • 2,815
  • 6
  • 26
  • 45
0

You can also use Network tab in Chrome Developer Tools. It shows all of the GraphQL and network errors.

realplay
  • 2,078
  • 20
  • 32
0

There are two solutions for that

1) ApolloClientDevTools install it in your Chrome browser! It is GraphQL debugging tools for Apollo Client. Apollo Client Dev tools is a Chrome extension for the open-source GraphQL client, Apollo Client. This extension has 4 main features:

  1. A built-in version of the Apollo Studio Explorer that allows you to make queries against your GraphQL server using your app's network interface directly (no configuration necessary).
  2. A query watcher that shows you which queries are being watched by the current page, when those queries are loading, and what variables those queries are using.
  3. A mutation inspector that displays the mutations made to your Apollo Client application data.
  4. A cache inspector that displays your Apollo Client cache data. You can explore the cache through a tree-like interface, and search for specific field keys and values.

2)console.log(JSON.stringify(error, null, 2));

This will show you an error trace in your console.

{
  "graphQLErrors": [],
  "networkError": {
    "name": "ServerError",
    "response": {},
    "statusCode": 400,
    "result": {
      "errors": //IT will show you here your issue.
    }
  },
  "message": "Network error: Response not successful: Received status code 400",
  "name": "Error",
  "stack": "...same as before"
}
Jamal Ashraf
  • 569
  • 6
  • 9
0

As the top voted solution says, console.log is not logging properly apollo errors.

You have to use an apollo link (it's already within the library @apollo/client)

import { ApolloClient, InMemoryCache, ApolloProvider, from, HttpLink } from '@apollo/client'
import { onError } from '@apollo/client/link/error'

const httpLink = new HttpLink({
  uri: 'http://localhost:4000',
})

// Log any GraphQL errors or network error that occurred
const errorLink = onError(({ graphQLErrors, networkError }) => {
  if (graphQLErrors)
    graphQLErrors.forEach(({ message, locations, path }) =>
      console.error(`[GraphQL error]: Message: ${message}, Location: ${locations}, Path: ${path}`)
    )
  if (networkError) console.error(`[Network error]: ${JSON.stringify(networkError, null, 2)})`)
})

const client = new ApolloClient({
  link: from([errorLink, httpLink]), // `httpLink` must be the last
  cache: new InMemoryCache(),
})

Note that you need to remove uri and give it via httpLink as the last element of the array:

If you provide a link chain to ApolloClient, you don't provide the uri option.

Here's what will be logged:

[GraphQL error]: Message: Variable "$level" of required type "Int!" was not provided., Location: undefined, Path: undefined

and

[Network error]: {
  "name": "ServerError",
  "response": {},
  "statusCode": 400,
  "result": {
    "errors": [
      {
        "message": "Variable \"$level\" of required type \"Int!\" was not provided.",
        "extensions": {
          "code": "BAD_USER_INPUT",
          "exception": {
            "stacktrace": [
              "GraphQLError: Variable \"$level\" of required type \"Int!\" was not provided.",
              "    at coerceVariableValues (/home/bias-input/projects/personal/escape-online/escape-online-back/node_modules/graphql/execution/values.js:105:11)",
              "    at getVariableValues (/home/bias-input/projects/personal/escape-online/escape-online-back/node_modules/graphql/execution/values.js:45:21)",
              "    at buildExecutionContext (/home/bias-input/projects/personal/escape-online/escape-online-back/node_modules/graphql/execution/execute.js:280:63)",
              "    at execute (/home/bias-input/projects/personal/escape-online/escape-online-back/node_modules/graphql/execution/execute.js:116:22)",
              "    at execute (/home/bias-input/projects/personal/escape-online/escape-online-back/node_modules/apollo-server-core/dist/requestPipeline.js:205:48)",
              "    at processGraphQLRequest (/home/bias-input/projects/personal/escape-online/escape-online-back/node_modules/apollo-server-core/dist/requestPipeline.js:148:34)",
              "    at processTicksAndRejections (node:internal/process/task_queues:96:5)",
              "    at async processHTTPRequest (/home/bias-input/projects/personal/escape-online/escape-online-back/node_modules/apollo-server-core/dist/runHttpQuery.js:186:30)"
            ]
          }
        }
      }
    ]
  }
})

You can customize the ouput inside the errorLink function

Eric Aya
  • 69,473
  • 35
  • 181
  • 253
BiasInput
  • 654
  • 1
  • 10
  • 28
0
const addProductMutation = gql`
mutation addProduct($id: String!, $name: String!, $organisationId: String!) {
  addProduct(_id: $id, name: $name, organisationId: $organisationId) {
    _id
    name
  }
}`;

The following mutation gave me a response 400 error, because I wasn't passing "id" as "ID" as I defined in the apollo-server schema and resolvers. The error messages that I get back from apollo-server, I don't find it that useful as they don't really tell you the error. It is better to use tools like "graphiql" to run the same query, it highlights your error and tells you about it very easily. So for the above snippet, the corrected version will be the following

const addProductMutation = gql`
mutation addProduct($id: ID!, $name: String!, $organisationId: String!) {
  addProduct(_id: $id, name: $name, organisationId: $organisationId) {
    _id
    name
  }
}`;
DanteDX
  • 1,059
  • 7
  • 12
0

2023 Suggestion answer

You can also try to use the onError method from @apollo/client/link/error as follows, it worked for me:

import { onError } from '@apollo/client/link/error';


const errorLink = onError(
    ({ graphQLErrors, networkError }) => {
        if (graphQLErrors)
            graphQLErrors.forEach(
                ({ message, locations, path }) =>
                    console.log(
                        `[GraphQL error]: Message: ${message}, Location: ${JSON.stringify(locations)}, Path: ${JSON.stringify(path)}`
                    )
            );
        if (networkError) {
            console.log(`[Network error]: ${JSON.stringify(networkError)}`);
        }
    }
);


export const client = new ApolloClient(
    {
        link: from([errorLink, link]),
        cache: new InMemoryCache(),
    }
);

Use this link for reference apollo-link-error

GeekDev
  • 145
  • 3
  • 10