graphql icon indicating copy to clipboard operation
graphql copied to clipboard

Interface implementation question

Open nexterot opened this issue 6 years ago • 2 comments

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!

nexterot avatar Jun 05 '19 09:06 nexterot

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.

ki4jnq avatar Nov 15 '19 00:11 ki4jnq

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

vincen320 avatar May 13 '23 19:05 vincen320