question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. It collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

Schema composition with directives that use complex argument types

See original GitHub issue

Consider 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:

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:closed
  • Created 3 years ago
  • Reactions:1
  • Comments:12 (8 by maintainers)

github_iconTop GitHub Comments

1reaction
yaacovCRcommented, Nov 12, 2020

Not sure I agree where the bug is. Seems like similar types with identical names are being created unnecessarily/inappropriately.

0reactions
IvanGoncharovcommented, Nov 20, 2020

@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. extensions or custom class inherited from GraphQLInputObjectType). 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.

Read more comments on GitHub >

github_iconTop Results From Across the Web

Schema composition - Apollo GraphQL Docs
Composition always uses the intersection strategy to merge input types and field arguments. This ensures that the router never passes an argument to...
Read more >
Directives SDL – GraphQL Tools
Using SDL directives, a subservice may express its complete schema and type merging configuration in a single document.
Read more >
XML Schema Part 1: Structures Second Edition - W3C
An element declaration is an association of a name with a type definition, either simple or complex, an (optional) default value and a...
Read more >
Schemas and Types - GraphQL
We'll use the "GraphQL schema language" - it's similar to the query language, ... Every field on a GraphQL object type can have...
Read more >
Testing Attribute Directives - Angular
Finding and testing all components that use the directive is tedious, brittle, and almost as unlikely to afford full coverage. Class-only tests might...
Read more >

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found