Skip to content
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

Let graphene handle serializer errors #380

Closed
annshress opened this issue Jan 22, 2018 · 19 comments
Closed

Let graphene handle serializer errors #380

annshress opened this issue Jan 22, 2018 · 19 comments

Comments

@annshress
Copy link

annshress commented Jan 22, 2018

Currently, we have separate errors field rather than using already existing error feature provided by graphene.

https://github.com/graphql-python/graphene-django/blob/master/graphene_django/rest_framework/mutation.py#L77
https://github.com/graphql-python/graphene-django/blob/master/graphene_django/rest_framework/mutation.py#L128

So instead we could simply raise the ValidationError raised by serializer, and let graphene handle it.

annshress added a commit to annshress/graphene-django that referenced this issue Jan 22, 2018
@patrick91
Copy link
Member

mmh, I'm not sure this is something we want. I usually treat the graphene errors as something that's not fixable by the end user. But that's my personal preference, also because graphql errors are a bit limited.

I'm up for suggestions here, @syrusakbary @spockNinja

@spockNinja
Copy link
Contributor

I agree with @patrick91. I believe the error fields on the SerializerMutation are quite intentionally separate from GraphQL errors. These are generally field-level validation errors and will not only have a different structure from normal errors, but are handled differently by the client.

For example, getting a GraphQL query syntax error is not your end-user's fault, and should be handled by a developer before you get into production. However, the "errors" on the SerializerMutation will happen all the time in production and should be passed on to your end user so that they can fix the data in their form and try again.

@annshress
Copy link
Author

Makes sense. Thanks for feedback.

@sliverc
Copy link

sliverc commented Aug 22, 2018

I have just bumped into the same issue and first thought this is a bug before I have encountered this issue.

I think this decision should be reconsidered, let me outline why:

  1. When executing a mutation with a validation error it returns 200 Success status code even though there is a failure and nothing has been mutated
  2. Client always has to request the errors in its query even though in general it is simply interested in the success results
  3. when a graphql error occurs graphene currently returns a 400 Bad Request error which indicates that request is invalid and can be fixed. If it were a programmer error it would need to return 500 but this is not the case
  4. The specification doesn't state that those are not fixable by the users (a user for me is the api client). It actually now also allows extensions to give more information to the client.

Reference for status codes: https://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html

What do you think?

I am happy to work on a PR if we can agree that this should be adjusted.

@hcharley
Copy link

I just got tripped up on this as well @sliverc. I had to read through the SerializerMutation source code to figure out what was happening.

@patrick91
Copy link
Member

@sliverc @Charlex I see your point, but I'm not sure how we should manage errors, would something like this work?

{
  "errors": [
    {
      "message": "Validation failed.",
      "locations": [ { "line": 6, "column": 7 } ],
      "path": [ "createHero" ],
      "extensions": {
        "fieldErrors": {
          "name": ["This field cannot be empty"]
        },
        "timestamp": "Fri Feb 9 14:33:09 UTC 2018"
      }
    }
  ]
}

@patrick91
Copy link
Member

Related: graphql/graphql-spec#391

@sliverc
Copy link

sliverc commented Sep 4, 2018

GraphQL errors encode exceptional scenarios - like a service being down or some other internal failure. Errors which are part of the API domain should be captured within that domain.

I am a bit confused by this statement in the related issue as the specification doesn't explicitly state that this is the case (at least that's how I read it).

The specification rather states:

If the result of resolving a field is null (either because the function to resolve the field returned null or because an error occurred), and that field is of a Non-Null type, then a field error is thrown. The error must be added to the "errors" list in the response.

Trying to rephrase this for our case:
Each field returned by mutation which is null because of an error should have an error in errors.

Hence I suggest Graphene Django adds one error for each validation exception. This might need changes on graphql-core as well? The way how you have suggested would also be a good first step to move forward.

Also such a change depends on a fix for issue graphql-python/graphql-core#203

All in all changing this won't be trivial as changes in graphql-core are most likely necessary. But could we still reopen this issue? This way users with the same issue will find it better as the discussion continues. And as stated when we agree on a solution I am open to help with a PR if needed.

@annshress annshress reopened this Oct 27, 2018
@patrick91
Copy link
Member

@sliverc sorry I missed your reply! Thank you @annshress for reopening this!

I'm honestly a bit conflicted between the various option, I was actually writing a blog post about dealing with user errors in GraphQL. My main reason to have errors in the schema is that they are typed[1], so they super easy to use (especially if you want to show them inline with the fields).
Unfortunately, I've seen people misusing these errors and not actually requesting them when doing mutations, so I was thinking of other approaches for handling them, and from what I've seen there are 3 options:

A. Errors in GraphQL, I like it, but they can be easily ignored by devs (happens a lot, unfortunately) but also they are not typed (I'm not sure having extensions will help us here)
B. Errors as fields like we have now (but typed better) same here, it is really easy to not request the field errors or miss a single field. But they are typed
C. Return a union type for the mutation, so when using the mutation and its return type we would need to add a check for the type, this basically adds a hint to the developers that they have to fetch the errors, but even here it is easy to miss a field.

Example for option A:

mutation {
  createUser(name: "example") {
    user { 
      name
    }
  }
}

Here there's no need to request for the errors, they would be all passed into the error field of the response.

Example for option B:

mutation {
  createUser(name: "example") {
    user { 
      name
    }
    errors {
      name
      nonFieldErrors
    }
  }
}

Here we have to fetch the errors by field (including nonFieldErrors), again this is quite easy to miss

Example for option C:

mutation {
  createUser(name: "example") {
    ... on User { 
      name
    }
    ... on CreateUserErrors {
      name
      nonFieldErrors
    }
  }
}

Here we need to handle the union type, this kinda forces us to check the errors but you could still miss a field.

I'm working on a PR to improve Form and Serializer mutations, and I might include a new error handling, I'm honestly thinking of implementing all 3 solutions and let whoever is using the library which one they prefer, I'll keep you posted :)

[1] Graphene Django Mutation is not nicely typed but that's another issue

@sliverc
Copy link

sliverc commented Nov 12, 2018

@patrick91 Thanks for your feedback and working on this. Providing all three options might be a way forward.

Personally I think though this is rather a matter of what the specification states and not what we prefer to do in graphene. Maybe this needs to be raised again within context of graphql specification as it seems the specification is not clear at this point.

@vinayan3
Copy link

I've run into this today in terms of handling errors in my application. I have been creating Union types between Nodes and Errors so I can return DRF validation errors.

I'm hooking this up to an apollo-client which thinks because the request has 200 response everything is fine. However, there is a validation errors in the data key. For now I'm making the client check the data key for errors.

It'd be great if there was a way to reduce the tedium and have something that produces errors into a single field.

@patrick91 for option A how will we handle multiple validation errors? The graphql.error.GraphQLError only encapsulates a single instance of an error. DRF's validation errors are an array with multiple messages. It'd be great if we didn't lose that existing functionality.

@stale
Copy link

stale bot commented Jun 11, 2019

This issue has been automatically marked as stale because it has not had recent activity. It will be closed if no further activity occurs. Thank you for your contributions.

@stale stale bot added the wontfix label Jun 11, 2019
@jl-DaDar
Copy link

I am facing an issue maybe similar to this one?

I am using model serializer in serializermutation which relies on models' field attributes null=True to determine whether a field is required. This is normally fine for most operation except delete, in which requires me to enter data for all required field when only id is needed.

We really need a way to fix this.

`{
"errors": [
{
"message": "Argument "input" has invalid value {id: "V_cX1txRTVaMI0jDu5uoaQ"}.\nIn field "type": Expected "String!", found null.\nIn field "suffix": Expected "String!", found null.\nIn field "mime": Expected "String!", found null.",

}

]
}`

@stale stale bot removed the wontfix label Jun 15, 2019
@stale
Copy link

stale bot commented Aug 14, 2019

This issue has been automatically marked as stale because it has not had recent activity. It will be closed if no further activity occurs. Thank you for your contributions.

@stale stale bot added the wontfix label Aug 14, 2019
@stale stale bot closed this as completed Aug 21, 2019
@asalmoqbel
Copy link

is there any update on this issue?

we would like to standardize our mutations so that there is no need to specify/select "errors" .. it should be there by default if there is an error.

@patrick91 I believe that you ware working on a PR that address this issue, I am not sure what is that status.

@tony
Copy link

tony commented Oct 22, 2020

By the way, here is what Apollo server does: https://www.apollographql.com/docs/apollo-server/data/errors/#augmenting-error-details

@patrick91 is it okay with we re-open and prevent the stale bot from closing?

@jkimbo
Copy link
Member

jkimbo commented Oct 22, 2020

I'll reopen this issue for more discussion but IMO expected errors (like validation errors) should be modelled in the schema. The top level errors key is reserved for unexpected server errors and that seems to be official position from the GraphQL spec as well: graphql/graphql-spec#391 (comment)

@tony
Copy link

tony commented May 5, 2023

@firaskafri For those of us not up to speed, is there an final word of what led to this closing?

@firaskafri
Copy link
Collaborator

firaskafri commented May 7, 2023

Hi @tony,

Thank you for raising this issue again although its around 5 years old now.
I understand that you are suggesting a different approach to handling errors in our GraphQL implementation. Let me summarize the current error handling process based on the GraphQL specification and discuss potential alternatives or improvements.

According to the GraphQL specification, errors are handled in a structured manner and can be classified into two categories: validation errors and execution errors.

  1. Validation Errors: These errors occur when a query or mutation fails to meet the defined schema rules, such as using undefined fields or incorrect query structures. GraphQL performs a validation step before executing the query/mutation. If any validation errors are encountered, the operation will not be executed, and the errors will be returned to the client in a standardized format.

  2. Execution Errors: These errors occur during the actual execution of a query/mutation and can arise from various reasons such as internal server errors, incorrect data, or authorization issues. When execution errors occur, GraphQL will continue executing the operation as much as possible and will return partial data alongside an "errors" array containing error details.

The error objects in the "errors" array typically include properties like message, locations, path, and extensions. This standardized error format ensures that both server and client developers can consistently handle and process errors while the extensions dict allows developers add customization to their errors.

This is currently supported as it was implemented in graphql-core (GraphQLFormattedError)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests