graphql-go / graphql

An implementation of GraphQL for Go / Golang
MIT License
9.81k stars 835 forks source link

DefaultResolveFn can't be composed because it doesn't return an error #303

Open gracenoah opened 6 years ago

gracenoah commented 6 years ago

I have the following scenario and I'm trying to implement the Resolve function on Thing.

type Base struct {
    Simple string
}
type Thing struct {
    Base
    fancy string
}
func (t Thing) Resolve(p graphql.ResolveParams) (interface{}, error) {
    // TODO: Delegate to fields in Base first
    // If the fields are not found in the base, resolve them here
    switch p.Info.FieldName {
    case "Fancy":
        return b.fancy + " is fancy", nil
    default:
        return nil, fmt.Errorf("no such field: %s", p.Info.FieldName)
    }
}

As you can see, I don't want to re-implement the resolution of fields in Base just because Thing has a custom resolve function. Note that Base is embedded in Thing and I don't want to expose this implementation detail to the client by creating another layer of nesting in the graph.

I thought about calling DefaultResolveFn, but it doesn't return an error if the field is not found. I would really like a function I can call that will error if it can't figure out how to resolve a field. DefaultResolveFn can be a wrapper for that function and just drop the error. Then I could write:

func (t Thing) Resolve(p graphql.ResolveParams) (interface{}, error) {
    p2 := p
    p2.Source = t.Base
    resolved, err := graphql.DefaultResolveFnWithError(p2)
    if err == nil {
        return resolved, nil
    }
    // If the fields are not found in the base, resolve them here
    switch p.Info.FieldName {
    case "Fancy":
        return b.fancy + " is fancy", nil
    default:
        return nil, fmt.Errorf("no such field: %s", p.Info.FieldName)
    }
}

Of course, I could also write my own resolver function that errors, but then I run the risk of diverging from the upstream resolver.

If I make a PR that adds DefaultResolveFnWithError would it be accepted? Any opinions on what it should be named?

gracenoah commented 6 years ago

Better yet, it would be really nice to have a generic resolver that can understand embedded structs, fields AND FieldResolver interfaces and automatically combine results from all of them. Maybe if there was a way to replace the default resolver, I could implement the fancy resolution logic for my project without relying on upstream.