Skip to content

Schema composition with directives that use complex argument types #2845

@azaxarov

Description

@azaxarov

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:

`Schema must contain uniquely named types but contains multiple types named "${typeName}".`,
.

From what it seems, type property name is used to uniquely identify any given type in the schema, as evidenced by:

const typeName = namedType.name;

if (this._typeMap[typeName] !== undefined) {

During schema composition, all declared types are collected in a set using the following helper function:

function collectReferencedTypes(

Inside that function, there is a check that should prevent duplicate types from being added to the set of all referenced types:

if (!typeSet.has(namedType)) {

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.

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions