graphql-go / graphql

An implementation of GraphQL for Go / Golang
MIT License
9.88k stars 839 forks source link

Interface implementation question #486

Open nexterot opened 5 years ago

nexterot commented 5 years ago

Hello. I created a simple example according to testutil/testutil.go. But when running my example, I get constant error. Stuck for many hours trying to find out what's wrong with my piece of code.

main.go:

package main

import (
    "encoding/json"
    "fmt"
    "net/http"

    "github.com/graphql-go/graphql"
)

var (
    Schema      graphql.Schema
    sectionType *graphql.Object
)

type CbrNode struct {
    ID       string
    TreeID   string
    RcpaCode string
    Name     string
}

func init() {
    myInterface := graphql.NewInterface(graphql.InterfaceConfig{
        Name:        "CbrNode",
        Description: "A character in the Star Wars Trilogy",
        Fields: graphql.Fields{
            "id": &graphql.Field{
                Type:        graphql.NewNonNull(graphql.String),
                Description: "The id of the character.",
            },
            "tree_id": &graphql.Field{
                Type:        graphql.NewNonNull(graphql.String),
                Description: "-",
            },
            "rcpa_code": &graphql.Field{
                Type:        graphql.NewNonNull(graphql.String),
                Description: "-",
            },
        },
        ResolveType: func(p graphql.ResolveTypeParams) *graphql.Object {
            return sectionType
        },
    })

    sectionType = graphql.NewObject(graphql.ObjectConfig{
        Name:        "CbrSection",
        Description: "A humanoid creature in the Star Wars universe.",
        Fields: graphql.Fields{
            "id": &graphql.Field{
                Type:        graphql.NewNonNull(graphql.String),
                Description: "The id of the character.",
                Resolve: func(p graphql.ResolveParams) (interface{}, error) {
                    if human, ok := p.Source.(CbrNode); ok {
                        return human.ID, nil
                    }
                    return nil, nil
                },
            },
            "tree_id": &graphql.Field{
                Type:        graphql.NewNonNull(graphql.String),
                Description: "The id of the human.",
                Resolve: func(p graphql.ResolveParams) (interface{}, error) {
                    if human, ok := p.Source.(CbrNode); ok {
                        return human.TreeID, nil
                    }
                    return nil, nil
                },
            },
            "rcpa_code": &graphql.Field{
                Type:        graphql.NewNonNull(graphql.String),
                Description: "The name of the human.",
                Resolve: func(p graphql.ResolveParams) (interface{}, error) {
                    if human, ok := p.Source.(CbrNode); ok {
                        return human.RcpaCode, nil
                    }
                    return nil, nil
                },
            },
            "name": &graphql.Field{
                Type:        graphql.NewNonNull(graphql.String),
                Description: "The name of the human.",
                Resolve: func(p graphql.ResolveParams) (interface{}, error) {
                    if human, ok := p.Source.(CbrNode); ok {
                        return human.Name, nil
                    }
                    return nil, nil
                },
            },
        },
        Interfaces: []*graphql.Interface{
            myInterface,
        },
    })

    queryType := graphql.NewObject(graphql.ObjectConfig{
        Name: "Query",
        Fields: graphql.Fields{
            "cbr_node": &graphql.Field{
                Type: myInterface,
                Args: graphql.FieldConfigArgument{
                    "rcpa_code": &graphql.ArgumentConfig{
                        Description: "If omitted, returns the hero of the whole saga. If " +
                            "provided, returns the hero of that particular episode.",
                        Type: graphql.String,
                    },
                },
                Resolve: func(p graphql.ResolveParams) (interface{}, error) {
                    return CbrNode{ID: "1", TreeID: "2", RcpaCode: "3", Name: "4"}, nil
                },
            },
        },
    })
    var err error
    Schema, err = graphql.NewSchema(graphql.SchemaConfig{
        Query: queryType,
    })
    if err != nil {
        fmt.Println(err)
    }
}

func main() {
    http.HandleFunc("/graphql", func(w http.ResponseWriter, r *http.Request) {
        query := r.URL.Query().Get("query")
        result := graphql.Do(graphql.Params{
            Schema:        Schema,
            RequestString: query,
        })
        if len(result.Errors) > 0 {
            fmt.Printf("errors: %v\n", result.Errors)
        }
        json.NewEncoder(w).Encode(result)
    })
    fmt.Println("Now server is running on port 8080")
    fmt.Println("Test with Get      : curl -g 'http://localhost:8080/graphql?query={cbr_node(rcpa_code:\"1\"){rcpa_code}}'")
    http.ListenAndServe(":8080", nil)
}

query:

http://localhost:8080/graphql?query={cbr_node(rcpa_code:"1"){rcpa_code}}

error:

{
    "data": {
        "cbr_node": null
    },
    "errors": [
        {
            "message": "Runtime Object type \"CbrSection\" is not a possible type for \"CbrNode\".",
            "locations": [
                {
                    "line": 1,
                    "column": 2
                }
            ],
            "path": [
                "cbr_node"
            ]
        }
    ]
}

Any tips appreciated!

ki4jnq commented 4 years ago

This was not obvious at all, and I too just spent several hours searching for a solution. Turns out, if you want to use your type with an Interface, you need to tell the schema about it in the SchemaConfig.Types. So in your case, I think you'd need to change your schema definition to:

Schema, err = graphql.NewSchema(graphql.SchemaConfig{
    Query: queryType,
    Types: []graphql.Type[sectionType],
})

I don't know why, but that solved my problem perfectly.

vincen320 commented 1 year ago

Have same problem here, dont know how to solve. Had try @ki4jnq solution, but dont work on me.