graphql-go / graphql-go/graphql

Returning nil interface{} vs. returning nil pointer in Resolve functions

Open
#134 2 comments 0 reactions 0 assignees View on GitHub
Dominant language
Go
Stars
10.1k
Forks
845
PR merge metrics
No merged PRs in 30d

Description

I discovered that graphql-go behaves differently if I directly return nil from a `Resolve` function or if a return nil which is stored inside a pointer field.

In the first case graphql-go will directly interpret it is nil and won't call the child fields Resolve function. If I return a nil pointer it will instead call the chields field resolve function and will set the `Source` argument to nil. This is at least in my cases not what I expect, as I expect that returning nil should mean sending nil as the fields value to the client and not trying to resolve fields for that.

Some code to show the difference:

``` go
type ChildStruct struct {
FieldA string
}

type MainStruct struct {
Child *ChildStruct
}

var ChildStructSchema = NewObject(graphql.ObjectConfig{
Name: "ChildStruct",
Fields: graphql.Fields{
"fieldA": &graphql.Field{
Type: graphql.String,
Resolve: func(p graphql.ResolveParams) (interface{}, error) {
switch v := p.Source.(type) {
case ChildStruct:
return p.FieldA
case *ChildStruct:
return p.FieldA
default:
panic("didn't expect that")
}
},
},
},
})

var MainStructSchema = NewObject(graphql.ObjectConfig{
Name: "MainStruct",
Fields: graphql.Fields{
"child": &graphql.Field{
Type: ChildStructSchema,
Resolve: func(p graphql.ResolveParams) (interface{}, error) {
v := p.Source.(MainStruct)

// If I apply this method then child we be transmitted as nil and the Resolve methods
// in ChildStructSchema won't be called
if v.Child == nil {
return nil, nil
}
return v.Child, nil

// If I however directly return the field (nil pointer) then the resolve method
// will be called with a nil Source
return v.Child
},
},
},
})

// I I apply the second MainStruct.child resolving method I can trigger a panic with having a MainStruct
// defined as
var main = MainStruct{
Child: nil,
}
```

I think the reason for that is that the executor probably only checks if the return value is directly nil but not whether a nil is encapsulated in an interface. To work around this graphql-go would need to check if there's some pointer in the return value and if this nil besides only directly checking for nil.

What I don't know was if this is behavior was an explicit design decision or if it's just a bug. I currently can't see any situation where a nil pointer should lead the resolution of child fields - because if the Source is really not needed and childs should generated through some other mechanism one could simply return a non-nil dummy value. And as the executor anyway seems to check the returned value via reflection it could also check if this is Pointer and treat a nil pointer as a null value. Of course only if NonNull is not specified - otherwise it should be an error.

Contributor guide

Open the contributing guide

Assessment

This issue has not been assessed yet.

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.