Given this schema:
interface INode {
id: ID
}
type Todo implements INode {
id: ID
title: String!
}
type Query {
node(id: ID!): INode
}
Given this class:
export default class Todo {
constructor (public id: string, public title: string) { }
isTypeOf(value: any): Boolean {
return value instanceof Todo;
}
}
Given this resolver:
type NodeArgs = {
id: string
}
export const resolver = {
node: ({ id }: NodeArgs) => {
return new Todo('1', 'Todo 1');
}
}
When I call the query:
query {
node(id: "1") {
id
... on Todo {
title
}
}
}
Then I get the return below:
{
"errors": [
{
"message": "Abstract type INode must resolve to an Object type at runtime for field Query.node with value { id: \"1\", title: \"Todo 1\" }, received \"undefined\". Either the INode type should provide a \"resolveType\" function or each possible type should provide an \"isTypeOf\" function.",
"locations": [
{
"line": 2,
"column": 3
}
],
"path": [
"node"
]
}
],
"data": {
"node": null
}
}
As you can see, I've implemented the isTypeOf
function but I am still getting the error message.
What am I doing wrong?
Notes:
isTypeOf
is a function that is passed to the constructor of a GraphQLObjectType when you create your schema programatically. Ditto for resolveType
functions and unions/interfaces. If you use SDL and create your schema using buildSchema
, there is no way to inject those functions into your created schema, just like you don't have a way to provide resolvers for fields on types other than Query
and Mutation
.
You have a couple of options. One option is to utilize the default resolveType
behavior. This checks for a __typename
property on the object, and falls back to calling isTypeOf
on every implementing type until it matches. That means if you're using a class, it should be sufficient to do something like this:
export default class Todo {
get __typename() {
return 'Todo'
}
}
The better option would be to drop buildSchema
and use makeExecutableSchema
from graphql-tools
. Then you can define your resolveType
and/or isTypeOf
functions directly in your resolvers. For example:
const resolvers = {
Query: {
node: (obj, args, context, info) => {
return new Todo('1', 'Todo 1')
}
},
INode: {
__resolveType: (obj, context, info) => {
if (obj instanceof Todo) return 'Todo'
},
}
}
Not only can you easily define isTypeOf
or resolveType
this way, you can also easily add resolvers for fields of any type and add custom scalars without any hassle. You cannot do any of that (easily) if you're using just buildSchema
.
Edit:
If you prefer to utilize isTypeOf
instead of resolveType
, the resolvers would look something like this:
const resolvers = {
Query: {
node: (obj, args, context, info) => {
return new Todo('1', 'Todo 1')
}
},
Todo: {
__isTypeOf: (obj, context, info) => {
return obj instanceof Todo
},
}
}
Only one or the other is necessary. Either write a resolveType
function for every abstract type you use, or write a isTypeOf
for every object type that could be an abstract type.