Schema composition with directives that use complex argument types
See original GitHub issueConsider the following type definitions that will be used to build an Apollo Federation schema:
directive @something(rules: [Rule!]!) on OBJECT | FIELD_DEFINITION
enum Number {
one
two
}
input Rule {
numbers: [Number!]!
}
type Something {
id: ID!
}
extend type Query {
getSomething(id: ID!): Something!
@something(rules: [{ numbers: [one, two] }])
}
A single directive called @something is declared, that takes in a non-nullable list of GraphQL input object types. This directive can then be used on objects and field definitions to perform some task. An example of a similar directive can be found here: https://docs.amplify.aws/cli/graphql-transformer/auth#auth.
The problem arises during composition of the schema, namely that input type Rule is being registered twice, leading to this error: https://github.com/graphql/graphql-js/blob/00eab30fb269b6e2a4e8e61d097d3e249319420e/src/type/schema.js#L223.
From what it seems, type property name is used to uniquely identify any given type in the schema, as evidenced by:
https://github.com/graphql/graphql-js/blob/00eab30fb269b6e2a4e8e61d097d3e249319420e/src/type/schema.js#L216
https://github.com/graphql/graphql-js/blob/00eab30fb269b6e2a4e8e61d097d3e249319420e/src/type/schema.js#L221
During schema composition, all declared types are collected in a set using the following helper function: https://github.com/graphql/graphql-js/blob/00eab30fb269b6e2a4e8e61d097d3e249319420e/src/type/schema.js#L403
Inside that function, there is a check that should prevent duplicate types from being added to the set of all referenced types: https://github.com/graphql/graphql-js/blob/00eab30fb269b6e2a4e8e61d097d3e249319420e/src/type/schema.js#L409
Given this information and understanding the mechanics of Set class in JS, there is a chance that !typeSet.has(namedType)will result in a false positive if two objects have different references. As a consequence, some types may be registered twice, as in the example above with Rule type.
This problem appears to be quite simple to fix. There are two approaches:
-
Use
Setclass fromimmutable-js, which I would not recommend (see: https://stackoverflow.com/a/56353815) -
Change the following check
!typeSet.has(namedType)to something like![...typeSet].find(({ name }) => name === namedType.name)(see the following line: https://github.com/graphql/graphql-js/blob/00eab30fb269b6e2a4e8e61d097d3e249319420e/src/type/schema.js#L409)
I published an example repository to illustrate the problem: https://github.com/azaxarov/graphql-service-with-complex-directive-parameter
So far, I see no obvious side-effects of implementing a fix using approach nr. 2.
Issue Analytics
- State:
- Created 3 years ago
- Reactions:1
- Comments:12 (8 by maintainers)

Top Related StackOverflow Question
Not sure I agree where the bug is. Seems like similar types with identical names are being created unnecessarily/inappropriately.
@azaxarov every time this issue brought up is due to bugs in libraries using
graphql-js. The problem here is that we can’t deeply compare non-trivial objects two types can have differences hidden in unexpected places (e.g.extensionsor custom class inherited fromGraphQLInputObjectType). So our position is to be safe-side instead of silently ignoring such bugs.That said if you think such checks restricting valid usage scenarios please feel free to open an issue and describe a such scenario.