-
Notifications
You must be signed in to change notification settings - Fork 822
Return custom error message #513
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Comments
I usually return a GraphQLError.
It takes a few more arguments: |
This kind of depends on what you're trying to achieve. Are you trying to send back a message to the front end user? Or are you trying to send a error message to a fellow developer? Based on the context of your question I would guess you are trying to send a message to the front end user. If that is the case then you need to catch the exception, or capture the error message and return it as part of the payload of your mutation. Now this gets tedious if you want to achieve this for all your mutations. What I've done is written my own Mutation Class that extends ClientIDMutation I'll provide the solution I came up with. I only needed simple single string error messages. If you need to return more complex error details then this will need to be expanded. class CustomClientIDMutationMeta(ClientIDMutationMeta):
''' We have to subclass the metaclass of ClientIDMutatio and inject the errors field.
we do this because ClientIDMutation subclasses do not inherit the fields on it.
'''
def __new__(mcs, name, bases, attrs):
attrs['errors'] = String()
return super().__new__(mcs, name, bases, attrs)
class CustomClientIDMutation(ClientIDMutation, metaclass=CustomClientIDMutationMeta):
''' Custom ClientIDMutation that has a errors @fields.'''
@classmethod
def mutate(cls, root, args, context, info):
try:
return super().mutate(root, args, context, info)
except MutationException as e:
return cls(errors=str(e)) Also note that I've created a custom Exception Object called MutationException class MutationException(Exception):
'''A Mutation Exception is an exception that is raised
when an error message needs to be passed back to the frontend client
our mutation base class will catch it and return it appropriately
'''
pass This solution has worked really nicely for my needs because anytime I need to send a validation message I just have to raise a MutationException and it will propagate back to the frontend user nicely |
Thanks! Really awesome usage! I think we should include the |
@BossGrand wouldn't the user need to query for errors on the mutation? |
I'm going to close this issue since it's stale. However just to add: I've found that modeling expected errors as part of your mutation response is essential. Your client needs to know what to do if your mutation fails. To do that I've found that modeling your response types as unions works really well since it allows you explicitly model the errors you're expecting. So in a class CreateUserFailUsernameExists(graphene.ObjectType):
suggested_alternatives = graphene.List(graphene.String)
error_message = graphene.String(required=True)
class CreateUserFailOther(graphene.ObjectType):
error_message = graphene.String(required=True)
class CreateUserSuccess(graphene.ObjectType):
user = graphene.Field(User, required=True)
class CreateUserPayload(graphene.Union):
class Meta:
types = (CreateUserFailUsernameExists, CreateUserFailOther, CreateUserSuccess)
class CreateUser(graphene.Mutation):
class Arguments:
username = graphene.String(required=True)
password = graphene.String(required=True)
Output = CreateUserPayload
def mutate(root, info, username, password):
if User.objects.filter(username=username).exists():
return CreateUserFailUsernameExists(
error_message="Username already exists",
suggested_alternatives=get_alternatives(username)
)
try:
user = create_user(username, password)
return CreateUserSuccess(user=user)
except:
return CreateUserFailOther(error_message="Something went wrong") Then in your client your mutation query becomes: mutation createUser($username, String!, $password: String!) {
createUser(username: $username, password: $password) {
__typename
... on CreateUserFailUsernameExists {
suggestedAlternatives
}
... on CreateUserFailOther {
errorMessage
}
... on CreateUserSuccess {
user {
id
}
}
}
} Then you just need to check the |
just raise GraphQL may not a good idea? for I find the error directly raised in mutation wont be even catched? so tests with
|
The problem is if you raise an error (such as Does anyone have a solution for this? |
@dspacejs With
It was using class GraphQLLogFilter(logging.Filter):
def filter(self, record):
exc_type, exc, _ = record.exc_info
custom_error = isinstance(exc, GraphQLError)
if custom_error:
return False
return True
LOGGING = {
'version': 1,
'disable_existing_loggers': False,
'handlers': {
'console': {
'level': 'DEBUG',
'class': 'logging.StreamHandler',
},
},
# Prevent graphql exception from displaying in console
'filters': {
'graphql_log_filter': {
'()': GraphQLLogFilter,
}
},
'loggers': {
'graphql.execution.executor': {
'level': 'WARNING',
'handlers': ['console'],
'filters': ['graphql_log_filter'],
},
},
} With
It is no longer an exception but an error log and the logger has changed to class GraphQLLogFilter(logging.Filter):
def filter(self, record):
if 'graphql.error.located_error.GraphQLLocatedError:' in record.msg:
return False
return True
LOGGING = {
'version': 1,
'disable_existing_loggers': False,
'handlers': {
'console': {
'level': 'DEBUG',
'class': 'logging.StreamHandler',
},
},
# Prevent graphql exception from displaying in console
'filters': {
'graphql_log_filter': {
'()': GraphQLLogFilter,
}
},
'loggers': {
'graphql.execution.utils': {
'level': 'WARNING',
'handlers': ['console'],
'filters': ['graphql_log_filter'],
},
},
} I believe that this is no longer used and could be removed. |
@Siecje brilliant solution, it works great. Thanks! Edit: a few issues though, with this solution there's no way to conditionally log errors. Whenever any exception is thrown, it's received as a Therefore all you can do is filter all exceptions because they're all received as Another problem: it doesn't seem to prevent
I inserted a breakpoint into I also tried using |
What is different about testing? Which logger did you disable? The new logger is The current code isn't logging an exception, it is logging a message with level error. So yes it means we can't filter based on exception type. Which is what I was doing in the early version. |
@dspacejs Is it safe to not log all the |
@GitRon You are correct that this will not log any exceptions. I thought it was only
|
Sorry to bring up an old issue, but I have a question... The Union approach (suggested by @jkimbo ) seems nice, but the problem I see with implementing it (at least for us) is that when you add in an error case you lose backwards compatibility, no? We have a lot of mutations already, so tricky to add in retrospect. To deal with this, have you used this mutation-style from the start for every single mutation? Without doing that, it becomes difficult to add a user error later down the line when you decide you need one. |
Ended up with this filter function. Before I always missed a few exceptions from different places, which was confusing.
|
Hi there!
I'd like to know the better way to raise an exception on a Mutation. For example, while trying to create a User with an email address that already exists.
I know that I can
raise Exception('message')
, but is this the best way? Can we do it differently?Thanks!
The text was updated successfully, but these errors were encountered: