I'd like my mutation to error out, and pass custom data in the error object. The only way to return an error from a mutation seems to be to literally raise a python error without catching it.
This would be useful, for example, for form validation.
The GraphQL spec currently don't define any way for extending the Error entries (for handling validation on fields, for example) https://facebook.github.io/graphql/#sec-Errors
So graphene will wait until the spec is better defined in this sense (we could implement something but could be confusing for the users as didn't reflect what is happening in other implementations that follow the spec).
However, I would like to give you a solution for form validation:
Add a validation_errors field in the mutation, and use it.
class Mutation(graphene.Mutation):
introduce_ship = graphene.Field(IntroduceShip)
validation_errors = graphene.List(graphene.String())
@classmethod
def mutate(cls, instance, args, info):
try:
# whatever you want to do
except Exception, e:
return Mutation(validation_errors=[str(e)])
Unfortunately, because I'm using Relay, the data returned from successful mutations is mostly out of my control.
The spec does seem to allow for extending the error objects.
GraphQL servers may provide additional entries to error as they choose to produce more helpful or machine鈥恟eadable errors, however future versions of the spec may describe additional entries to errors.
Something as simple as an addition property I can add to an exception object that can be copied transparently into the error would be immediately useful.
At the moment I've resorted to json-encoding some extra data into the message line of an exception, which makes me feel like I need a shower, haha.
I just seen this article, you might find it useful!
https://medium.com/@tarkus/validation-and-user-errors-in-graphql-mutations-39ca79cd00bf#.btrdt641t
I will try to talk with the GraphQL-js main developers (fb) and see how we can improve the spec regarding more personalized errors.
This approach would be great, but doesn't work well with react-relay.
In relay, you can't directly control the mutation queries. You tell relay which types of data may be changed after the mutation, and it generates a query that fetches just enough data to replace anything it has cached. You can't manually add fields to the mutation response.
I suppose you could work around this by having a top-level "errors" type in the schema and get relay to invalidate it's cached state when you run a mutation. It's a hack for sure, and you couldn't have errors from multiple mutations in memory at once this way, but it might work.
An update here:
I'm working in using Promises for the graphql-core package and upgrading for being compatible with the last graphql spec 0.5.0. As soon this PR (https://github.com/graphql-python/graphql-core/pull/54) is merged would be very easy to add custom errors in graphene.
Yeah also on the relay repo they seem to suggest to customize the error response: https://github.com/facebook/relay/issues/777#issuecomment-184321489
Once we can add custom errors it would also be important to be able to add multiple custom error for one mutation. So if you have form input validation that you can return all field errors at once. With the current method raise Exception('field a is invalid') you can not really have more than one error per mutation.
@defrex now with graphene 0.10 you have access to the original_error so you can already customize the error response any way you want.
we have something like:
result = schema.execute(query)
if result.errors:
# check for result.errors[...].original_error and build your own json
# send custom json response (that still follows the graphql format) to the frontend.
@defrex may be something like this:
class MyGraphQLView(GraphQLView):
@staticmethod
def format_error(error):
if isinstance(error, GraphQLError):
formatted_error = {
'message': error.message,
}
if hasattr(error, 'original_error') and isinstance(error.original_error, exceptions.GraphQL袝TypedError):
formatted_error['type'] = error.original_error.error_type
elif error.locations is not None:
formatted_error['locations'] = [{'line': loc.line, 'column': loc.column} for loc in error.locations]
return formatted_error
return {'message': six.text_type(error)}
I haven't take the time to power through the with_context breaking change yet, since it destroys my codebase utterly. I'll make the time to get up to date soon though, and let you know how this solution works for me.
I extend GraphQLView in order to custom error handling.
code and example
you just need raise special error:
raise ResponseError("Invalid Password", code='invalid_old_password')
and get error response:
{
"errors": [
{
"message": "Invalid Password",
"code": "invalid-old-password",
"params": null
}
],
"data": {
"changePassword": null
}
}
the only problem is logging ResponseError.
Hi @defrex . It looks like this issue might have been solved. In an effort to try and clean up the issue tracker I'm going to close this.
According to GraphQL specs custom info like this should be added to the extensions key. It looks like this isn't yet supported in Graphene.
I extend
GraphQLViewin order to custom error handling.
code and exampleyou just need raise special error:
raise ResponseError("Invalid Password", code='invalid_old_password')and get error response:
{ "errors": [ { "message": "Invalid Password", "code": "invalid-old-password", "params": null } ], "data": { "changePassword": null } }the only problem is logging ResponseError.
Hello, is it possible to extend this to testing environment. Let's say to assertRaise and get exception.code from it.
Most helpful comment
I extend
GraphQLViewin order to custom error handling.code and example
you just need raise special error:
and get error response:
the only problem is logging ResponseError.