Skip to content

advanced breaking change detection for input types #7176

@n1ru4l

Description

@n1ru4l

The problem

Tracking whether removing fields used by input types is tricky.

Today, treating all fields reachable from within an input type as being "in use" is the safest bet. However, this does not make it easy to systematically deprecated and remove these fields.

Today, as an additional feature, we allow a usage reporting for analysing the input variables and reporting input usage based on this. Also this approach is not 100% safe, as e.g. a nullable field not being used today could be a coincidence as it is a value that is rarely provided by a client.

Thus these options cannot guarantee hazzle-free (conditional) breaking changes.


Idea for an alternative way to track input usage

What if the clients define what input types they use for their GraphQL operation? This could be an annotation on the GraphQL operation variables definition and tools like GraphQL Code Generator or Relay could compliment the DX by only generating variable types based on that constraint definition.

Example GraphQL Schema:

enum ProjectType {
  FEDERATION
  MONOLITH
  STITCHING
}

type ProjectSettingsInput {
  compositionUrl: String
  someOtherSetting: String
}

input CreateProjectInput {
  type: ProjectType!
  slug: String!
  settings: ProjectSettingsInput
}

type Mutation {
  createProject(input: CreateProjectInput!): Boolean
}

Example GraphQL Operation:

mutation CreateProject(
  $input: CreateProjectInput!
    @constraints(selectionSet:
      """
        {
          type @constraints(enumValues: [ProjectType.Federation, ProjectType.STITCHING])
          slug
          settings {
            compositionUrl
          }
        }
      """
    )
  ) {
  createProject(input: $input)
}

The @constraints directive would be used on input types for defining the contract of the input type for this operation type. The argument @constraints(selectionSet:), would be a multi-line string that specifies the (deep) selection set being used within the input type.

In addition to that @constraints(enumValues:) can also be used to restrict enum input values.

Based on the above operation, the GraphQL API could remove ProjectType.MONOLITH and ProjectSettingsInput.someOtherSetting, without the client being affected.

Input constraint usage implications:

  • The GraphQL would raise an error if the above operation is executed with either ProjectType.MONOLITH or ProjectSettingsInput.someOtherSetting within the variable values, based on the defined contract.

  • GraphQL Code Generator would generate the variable definitions for the mutation with the contraints in mind:

    type CreateProjectVariables = {
      type: "FEDERATION" | "STITCHING";
      slug: string;
      settings: { compositionUrl: string } | null
    }
  • The Hive Router/SDK reporting could report the usage to the registry based on the specified constraint.

  • The registry can provide conditional breaking changes based on the constrained usage reporting/ app deployemt operation sets

How would the deprecation/removal process for a field look like

Based on the above operation and schema, let's say we want to remove ProjectSettingsInput.settings. As usual we would first mark it as deprecated. As soon as the field is marked as deprecated, client consumers using that field would be informed (based on alert configuration etc.). They can then update their operation input contract to remove that field usage.

 mutation CreateProject(
   $input: CreateProjectInput!
     @constraints(selectionSet:
       """
         {
           type @constraints(enumValues: [ProjectType.Federation, ProjectType.STITCHING])
           slug
-           settings {
-             compositionUrl
-           }
         }
       """
     )
   ) {
   createProject(input: $input)
 }

Once the client usage is in production and the contract definition is reflected in the usage of the schema, the field ProjectSettingsInput.settings is then safe to be removed.

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions