-
Notifications
You must be signed in to change notification settings - Fork 843
Adding extra fields to errors #109
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
The
Having extra fields is actually addressed in the spec as well:
Would love to start a discussion if / how we want to achieve this. Cheers! |
Maybe the function's signature can become: func(p graphql.ResolveParams) (interface{}, error, interface{}) So, if there are any extra fields, you can do: type extraErrors struct {
Code string `json:"code"`
Field string `json:"field"`
}
fields := graphql.Fields{
"hello": &graphql.Field{
Type: graphql.String,
Resolve: func(p graphql.ResolveParams) (interface{}, error, interface{}) {
extras := &extraErrors{Code: "WHOOPS_ERROR", Field: "someField"}
return nil, errors.New("Whoops! An error occurred")
},
},
} This means that existing resolvers will need to be updated to use:
|
I think logrus.WithFields(logrus.Fields{
"one": "two",
}).Info("Hey, it's a log") Given the nature of type ErrorFields map[string]interface{}
type Error struct {
Message string
Fields ErrorFields
}
func (e *Error) Error() string {
return e.Message
}
func (e *Error) WithFields(fields ErrorFields) {
e.Fields = fields
}
func (e *Error) WithField(name string, value interface{}) {
e.Fields[name] = value
}
func NewError(message string) *Error {
return &Error{
Message: message,
Fields: make(ErrorFields),
}
} Usage from a Resolve: func (params graphql.ResolveParams) (interface{}, error) {
return nil, graphql.NewError("this failed").WithFields(graphql.ErrorFields{
"one": "two",
})
} Probably needs some significant cleaning up, or works. I think it's a simple and clean API. |
@F21 I think changing the return signature for resolve functions would not be such a great idea. The biggest reason is I would imagine that additional error information is not a terribly common requirement so most of the time it would be |
@bbuck After looking at your proposal, I agree! |
By all means the credit should lie with Sirupsen who wrote |
@bbuck This solution looks good, also saves any regression issues for people that are already using this. Is anyone working on this? If not I'd like to take it on. |
I've started implementing this issue and I'm pretty much done as per you suggestions above. However, I want to discuss how to handle marshalling of this. This new error type eventually gets converted to a {
"message": "an error occurred",
"fields": {
"customOne": "valueOne",
"customTwo": "valueTwo:
}
} Or do we want to try and flatten it out like: {
"message": "an error occurred",
"customOne": "valueOne",
"customTwo": "valueTwo:
} If we want to do the latter how do we go about this in go so that it can be easily marshalled? |
@Mleonard87 I definitely think the nested approach feels a lot better than flattening it out in some way. To your second question (even though I don't prefer this method) you'd want to implement the |
Ok, excellent. Are you happy with the name And finally, what is the purpose of the |
@Mleonard87 I'm not a decision maker for this project, I'd wait for @sogko or @chris-ramon to respond before finalizing any decisions. That being said though, I think fields is clean enough -- and I'm not familiar enough at this moment to respond on |
May I throw in that the flat structure would allow to (re-)implement all other GraphQL-APIs in this point? Having a fixed key here feels a bit like an artificial restriction. |
Any updates/decisions on this? |
Hey guys, I'd like to know how this issue is going, and also to give my input. For me the flattening of fields isn't such a bad idea. I have to respect an error format like so {
"message": "Whoops! An error occurred",
"locations": [
],
"title": "hello",
"code": 400
} That means @Mleonard87's first solution wouldn't work out for me. I also see this #238 PR seems to implement a similar thing. |
Just want to bring to your attention a PR I submitted related to this issue: #279. This PR basically
This enables me to render my domain errors differently, since I now have access to the actual underlying error. |
@dvic Could you provide a usage example ? |
@plassa-b It would look something like this where you handle the graphql query: res := graphql.Do(params)
// instead of rendering res directly, we're going to do something with the errors
if res.HasErrors() {
var errorsArray []interface{}
for _, err := range res.Errors {
// errors.Cause(...) unwraps the underling error, e.g., if errors.Wrap(someErr) was used
// we get 'someErr' instead of the wrapped error
if err, ok := errors.Cause(err.Cause()).(interface {
AsPublicJSON() []byte
}); ok {
errorsArray = append(errorsArray, json.RawMessage(err.AsPublicJSON()))
continue
}
errorsArray = append(errorsArray, err)
}
writeJSON(rw, map[string]interface{}{"Errors": errorsArray})
} else {
writeJSON(rw, map[string]interface{}{"Data": res.Data})
} in the resolvers you can then return an error of the following type: type myErr struct {
message string
title string
code int
}
func (e myErr) Error() string {
return e.message
}
func (e myErr) AsPublicJSON() []byte {
ret, err := json.Marshal(map[string]interface{}{
"message": e.message,
"locations": []interface{}{},
"title": e.title,
"code": e.code,
})
if err != nil {
panic(errors.WithStack(err))
}
return ret
} Of course, you can also do the type check differently (on a specific error type, other methods, etc.). |
Ok it looks great ! |
Hi all! What is the current status of this enhancement? We really need this to start using this graphql solution. Returning machine readable error codes - is important part of any big project. |
I think that the proposition of @bbuck is crystal clear :) But, to sum up I do not want to use the term "field", but rather "entries". From spec:
Because if you are an error between two fields, it's an entry error no a field error. Interface type ErrorEntries map[string]interface{}
type Error struct {
Message string
Entries ErrorEntries
}
func (e *Error) Error() string {
return e.Message
}
func (e *Error) WithEntries(entries ErrorEntries) {
e.Entries = entries
}
func (e *Error) WithEntry(name string, value interface{}) {
e.Entries[name] = value
}
func NewError(message string) *Error {
return &Error{
Message: message,
Entries: make(ErrorEntries),
}
} Resolver func (params graphql.ResolveParams) (interface{}, error) {
return nil, graphql.NewError("Register failed").WithEntry("email", "Must be unique")
} @sogko or @chris-ramon can you give your vision/opinion? (as usual, a big thank you for the work) |
Great idea! |
@sogko or @chris-ramon can you give your vision/opinion? Do you need a PR? |
This is covered by the latest GraphQL spec. Your resolvers can now return errors that implement
|
@ccbrown Thanks, I think that this issue can be closed ;) |
The issue has been resolved, so I'm closing it. |
Let's say I have a graphql schema with a field that looks like this:
The result would be:
What I would like to do is to be able to add extra fields to the errors object. For example:
It would be really cool if there's way to be able to add these extra fields into the error message when an error happens.
The text was updated successfully, but these errors were encountered: