graphql-hive / graphql-hive/console
advanced breaking change detection for input types
- Dominant language
- TypeScript
- Stars
- 483
- Forks
- 145
- Avg merge
- 2d 5h
- Merged PRs (30d)
- 65
Description
## 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:**
```graphql
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**:
```graphql
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:
```ts
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.
```diff
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.
Contributor guide
Assessment
This issue has not been assessed yet.