graphql-request: rawRequest() return type wrong: can never include errors

The return type signature of rawRequest() includes errors?: GraphqlError[]

https://github.com/prisma-labs/graphql-request/blob/4b02516fc3707f00bd27ddd724a190bd78a32beb/src/index.ts#L16-L25

However, when processing the result of the operation the function explicitly will not return if the result contains any errors. Instead it throws a new ClientError:

https://github.com/prisma-labs/graphql-request/blob/4b02516fc3707f00bd27ddd724a190bd78a32beb/src/index.ts#L71-L76

So it seems to me that the return type is incorrect, and this has led to confusion in my team about how to correctly handle errors. The correct way is with try/catch, not by checking whether there is an errors property in the return value.

I think the solution is to just remove errors from the return type.

About this issue

  • Original URL
  • State: closed
  • Created 4 years ago
  • Reactions: 5
  • Comments: 17 (9 by maintainers)

Commits related to this issue

Most upvoted comments

I would like to get more feedback about if people prefer thrown error or error as data.

I am in favour of error as data.

Maybe there should be a config option which adjusts the API behaviour.

new GraphQLClient({ throwOnClientError: true })
// or
new GraphQLClient({ clientErrorsAsData: true })

Then use conditional types to maintain an accurately typed API.

I just ran into this problem as well because it looks like graphql-codegen is still generating code that includes the GraphQLError type. It’s great that this library has removed the ambiguity though, so at least now I can see pretty readily that we need to do this:

try {
  // some query...
catch (error) {
  if (error instanceof ClientError) {
    // handle specifics, for example:
    console.error(error.message)
  } else {
    // possibly rethrow or do other things here
  }
}

I think it would be worth reconsidering try/catch versus explicit return types. A lot of GraphQL APIs end up using the extensions object to surface errors, including those that are domain-specific (for instance a constraint violation when a user tries to create a record with a duplicate name). This means if I want to handle the error in my domain code I have to wrap the invocation as per the above before I can extract the relevant extension information from the errors object. My feeling is that this results in (a) annoying boilerplate, (b) less type safety (thrown errors have no type information) and © occasionally puzzling control flow issues (where did that error come from? why didn’t this code run?).

I really like the simplicity of the library and so I’m providing this example as consideration for building on that simplicity.