Replies: 2 comments 2 replies
-
Hey, I really appreciate you writing this proposal up! I want to give people time to respond before I make up my own mind. My feelings on monads in Go is very accurately reflected by In search of better error handling for Go - I like monads in other languages, but I don’t see user-implemented monads generally as a perfect fit for Go until some other things are added to the language (please give me sum types). However, in GraphQL, and in gqlgen, we already have to support things that are awkward in Go. (I'm looking at you Enums!) Maybe monadic Results are a better way to represent GraphQL partial success. On the other hand, I really hate breaking backwards compatibility and forcing people to change what already works requires a really good justification. So I'm currently conflicted and would like other people's opinion(s). |
Beta Was this translation helpful? Give feedback.
-
I personally am supportive of this API. I agree that monads would be much better with sum types, exhaustive switch statements, and generic method support. That said, these are quite possibly (sadly) never coming to Go. The current error handling API in gqlgen is extremely error prone / difficult to use correctly, and I can't think of a better approach to this problem than what's described here. I think we can find a way to make this backwards compatible with a switch. I do also fundamentally think there's a few critical improvements like this that can be made to |
Beta Was this translation helpful? Give feedback.
-
Structured GraphQL Error Handling
Fields serve as the fundamental atomic units requested by the client. In GraphQL, each Field can either return null (if nullable) or an error. The client relies on the detailed, Field level error information to determine whether this data unit should be cached. Therefore, it's important to manage errors at the field level to ensure that callers comprehend why they were unable to retrieve a specific Field and behave appropriately.
gqlgen handles required fields errors as,
createdAt
field inPastTransportJob
Type is a required field. When the data for this field comes back as null, we get 2 error messages because it appears in bothedges
andnodes
inPastOrdersConnection
with different paths.The API today in gqlgen is to call graphql.AddError:
This has a few issues:
We believe there’s a simpler, less error prone API that solves all of these issues. The approach is similar to the Omittable type used on input types today in gqlgen.
We propose introducing a Result wrapper struct for all scalar types:
This is very similar to the Result monad in Kotlin.
When developers hydrate data for a field, they can either set a value or set an error based on whether the downstream production service is able to return a valid response or not.
This API enables us to model the 3 possible states of each field:
The modelgen code for this proposal works as follows:
Runtime implementation
After we get the response from a resolver, the response carries Field data with Result types. Resolvers will return response data to the GraphQL response parser. The parser traverses the query and adds an error when it hits an marshall error with the corresponding path information. This is the same idea of the existing GraphQL parser for inputs using UnmarshalJSON. In gqlgen, when there is an unmarshall error for a query input model, the caller of UnmarshalJSON will add the error with path info with graphql.AddErrorOnPath. We propose implementing the graphql.Marshaler interface for Result types to either:
Update gqlgen make the caller of MarshalJSON add this error with path info.
Example
Schema
Generated models
Beta Was this translation helpful? Give feedback.
All reactions