-
Notifications
You must be signed in to change notification settings - Fork 1.1k
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
Input argument error paths in error response #298
Comments
The best place for this logic is in your GraphQL schema, and not in the GraphQL specification 😊 If we take your example: input MutationInput {
username: String!
categories: [ID!]!
}
type Mutation {
createUser(input: MutationInput!): ID!
} …and have it return an object type: input MutationInput {
username: String!
categories: [ID!]!
}
type Mutation {
createUser(input: MutationInput!): CreateUserPayload
}
type CreateUserPayload {
createdUserID: ID
} You could also add a field for user validation errors. (Note how input MutationInput {
username: String!
categories: [ID!]!
}
type Mutation {
createUser(input: MutationInput!): CreateUserPayload
}
type CreateUserPayload {
createdUserID: ID
errors: [MutationInputError]
}
type MutationInputError {
message: String
field: String
} This would be much easier to use as well considering that most GraphQL clients today do not have sophisticated GraphQL error handling mechanisms. I’ve also written a post with more recommendations on designing an effective GraphQL mutation system if you’re interested. |
Thanks @calebmer for your reply! I like the article you posted! This is exactly the approach that I am using in my current APIs. However, this approach seems to me to have some major limitations and inconsistencies:
I think it would be beneficial to the community to add such error handling to the spec. If this would be added to the standard, the GraphQL clients can adopt this and we can build other functionality on top of it that is reusable across all GraphQL endpoints, like generic form libraries. |
Yep, this can be frustrating. However, this is nothing new in the realm of API development 😊. For example, in HTTP you can have 4xx errors and 5xx errors. One represents an error that is fixable by the user and the other represents a fatal error that a user cannot fix. I think this distinction is useful in GraphQL as well. This pattern also allows you to be as detailed as you want with 4xx errors whereas throwing everything in
I hold the opinion that using non-null fields are a bad practice in GraphQL unless they absolutely positively make sense which to me is basically just an
That’s my manifesto on nullable vs. non-null, however there is nothing stopping you from using non-null if you really want to! Just use a union type 😊 We would change my above example to: union CreateUserPayload = CreateUserPayloadSuccess | CreateUserPayloadError
type CreateUserPayloadSuccess {
createdUserID: ID!
}
type CreateUserPayloadError {
errors: [MyCustomError!]!
} …then you could query it like so: mutation {
createUser(...) {
__typename
... on CreateUserPayloadSuccess { createdUserID }
... on CreateUserPayloadError { errors }
}
} This would result in types that look like: type X = {
createUser: {
__typename: 'CreateUserPayloadSuccess',
createdUserID: ...,
} | {
__typename: 'CreateUserPayloadError',
errors: ...,
} | {
// In case you add more types to the union in the future.
__typename: string,
},
}; A tagged union type. Exactly what you want! If this type isn’t generated from that query then I’m going to be bold and say that is a bug in whatever is generating your types 😊
You could do a similar pattern in your queries and subscriptions. For instance: type Query {
search(...): SearchResult
}
type Subscription {
search(...): SearchResult
}
union SearchResult = SearchResultSuccess | SearchResultError I would argue that this is also better then the proposal because it keeps separate 4xx style user errors and 5xx style server errors. You can also taylor your 4xx style errors to the exact needs of your UI using the full power of GraphQL 😊
Expand on this idea? There is should be no shame in bloating your schema because the ideal is for our schemas to be versionless. This means that all “versions” of your API should live together in the schema at once. GraphQL then gives you the power to pick and choose a small subset from the bloat.
Your right that this could be sad, however to be fair I have never seen a good error handling approach that works for everyone 😊 Everybody has their own needs for the information the need from errors and how to render that information in the UI. I think of the validation errors you describe as mainly a UI concern (that’s why I feel it should be in GraphQL) and very few people have the same UI. Community standards might not be the right tool here, but company standards definitely could be. It’s worth noting that Relay Modern allows you to build custom handlers for certain patterns in your GraphQL API. I haven’t seen these used outside of connections, but this could be a perfect application! Finally, if it really is super important that there is a shared standard for UI validation errors then you could write a specification for GraphQL like the Relay specifications. Ultimately, I think using GraphQL is going to more powerful and provide more optionality then any specification we could write that modifies GraphQL’s error handling behavior 😊 |
If GraphQL is going to be ubiquitous, as @leebyron would like it to be (and me too!), then there needs to be a section in the spec on standard error handling. It needs to be certainly more than what is currently in the spec right now. Thankfully, it is clear from that section that the concern of error handling is left open for future development. So now, this suggestion comes at a good time and it is just a matter of the community coming up with a proper spec for errors. 😄 Scott |
@ivome I really interested in this functionality, since we started to work on middleware for validating input args and having the standard way to report errors will increase the usability of such middleware. I suggest you make PR which adds RFC into rfcs folder. IMHO, your initial comment answers all of them except 4 & 5. I just have a few minor technical suggestions:
|
I'm going to move this RFC to Rejected for a handful of reasons:
|
I think this should be relooked at, this would be a great feature |
What about errors that can happen at any node in the graph? Such as lack of permissions to read a specific node? |
@leebyron @calebmer I do not recall ever reading this "best practice" in any of the GraphQL documentation on mutation or errors or in any articles I've read. In fact this is the first time I've ever heard that I should be writing my mutations with This is a big problem, because to my understanding taking an api like |
Following the Relay specifications is often a best practice; in particular the Relay Input Object Mutations Specification advises a shape of mutation that would allow extending the payload to add an |
The GraphQL documentation already has best practices on pagination. If this is the expected way to handle user input errors in GraphQL and not a Relay-ism then the official site should have best practices on handling user input errors. Not everyone follows the exact patterns Relay uses. The mutation documentation should also use examples that follow the best practice. |
Agreed; however I think the best practices on handling errors in mutations are still emerging. I think having the payload type for mutations is definitely a best practice though, since without this you cannot add "meta" fields without breaking the schema or having to add a new mutation. We should definitely advise at least using mutation payloads. The input object also makes a lot of sense when it comes to client applications though, IMO; for example I use Apollo with
|
Current limitation:
With the proposed changes to the RFC in #230 that are already implemented in graphql-js, it is only possible to determine which field caused an error. (Unless I am missing something?)
If you want to implement input validation with custom constraints for specific arguments, there is no way for the client to tell which input argument value is invalid with the current spec, especially if we have input values that contain lists or other complex objects.
Possible usecases:
If we would have a standardized way to process input errors, this would open up the possibility to create standard compliant libraries with server side form validation or generate complete forms and admin interfaces based on introspection queries.
Example:
Given the following schema:
Executed query:
This could result in a response like this:
This is just a quick draft. I would like to know what you think of the idea.
The text was updated successfully, but these errors were encountered: