nautilus/gateway

Error: "cannot query field "id" on type "UnionType"

prokaktus opened this issue · 3 comments

Hello!

I've met the error when using inline fragments.

Description

Schema:

type Cat {
	id: ID!
	name: String!
}

type Dog {
	id: ID!
	name: String!
}

union Animal = Cat | Dog

type Query {
	animals: [Animal!]!
}

Query:

query MyQuery {
	animals {
		... on Cat {
			id
			name
		}
		... on Dog {
			id
			name
		}
	}
}

If Dog and Cat are defined in the same location, it works well.

But if you define Dog and Cat in different locations, then first query in plans generate:

(query generated by Gateway)

{
  animals {
    ... on Cat {
      id
      name
    }
    id
  }
}

Which is invalid, because we cannot query fields on Union, except __typename. Graphql spec told so:

the following query is invalid, because the union itself defines no fields:

{
  firstSearchResult {
    name
    height
  }
}

https://spec.graphql.org/June2018/#sec-Unions

Possible workaround

Use defined fragments.

Query like this will work:

query MyQuery {
	animals {
		... on Cat {
			...CatFragment
		}
		... on Dog {
			...DogFragment
		}
	}
}

fragment CatFragment on Cat {
	id,
	name
}
fragment DogFragment on Dog {
	id,
	name
}

I'll try to provide testcase and fix for this issue.

The same problem happens with query, if Dog and Cat from different locations:

query {
	animals {
		...CatFragment
		...DogFragment
	}
}

fragment CatFragment on Cat {
	id
	name
}
fragment DogFragment on Dog {
	id
	name
}

Turned out this is a lot trickier to fix than I expected.

Problem is somewhere there:

gateway/plan.go

Lines 389 to 392 in 1078926

if checkForID {
// add the id field since duplicates are ignored
locationFields[config.parentLocation] = append(locationFields[config.parentLocation], &ast.Field{Name: "id"})
}

When gateway handling unions fragments, there added id field to unions SelectionSet, and this is invalid syntax for graphql.

Oh, damn yea this is going to bring you very deep into the weeds of the query planning process. It sounds like you are on the right track tho! Since its safe to assume that every boundary type has ID, you should be able to just ensure that ID shows up the different conditions for the union and instead of adding it straight to the selection set.