Type Conflict Resolution
Type Conflict Resolution
In a distributed GraphQL world, your GraphQL schema is developed, parsed, and validated independently of other GraphQL schemas. Your schema is eventually integrated with other GraphQL schemas in the orchestrator, where we merge and stitch schemas together to produce a single executable schema.
The orchestrator performs validations to ensure the merged schema conforms to the GraphQL specification, and ensures that your schema can be correctly merged into the single schema. Your schema could be validated against hundreds of other schemas that already exist in the orchestrator! Thus, how you define your types becomes important because you could be clashing with many different providers all with similar type names and schema structures.
With many different providers producing GraphQL types, you are bound to run into collisions when trying to merge schemas together.
Resolution Strategies
Let’s say you have an object-type Profile
which is conflicting with the orchestrator.
When we say a type conflicts with the orchestrator, it really means that it is conflicting with some other provider that
has already registered type Profile
. You can contact the provider and discuss the conflicting type with them and see if it qualifies
for type extension. But let’s say, the type Profile
is semantically different from the other providers Profile
type.
Below are some of the strategies you can use to resolve conflicts.
Type Renaming Resolution Strategy
Suppose you are a User service trying to expose a Profile
type representing a user’s profile.
type Query {
userProfile: Profile
}
type Profile {
userId: ID
firstName: String
lastName: String
age: Int
}
Looks good on paper, but when you try to integrate with the orchestrator, it rejects your schema because Profile is conflicting
with another type Profile
defined by another service!
type Query {
financialProfile: Profile
}
type Profile {
income: Float
debt: Float
}
How do we fix this?
Be as specific as possible when building your schema types. For example, a Profile
type can be made more specific by
answering the following questions:
- What specific profile is this (Finance, User)?
- Does your type belong to a specific product (TurboTax, Mint)?
The results of answering the questions above and applying it to the Profile
type could be:
TurboTaxUserProfile
MintUserProfile
@rename Directive Resolution Strategy
This resolution strategy is useful for initial on-boarding purposes where you have clients that already call your service’s GraphQL endpoint, but you need to rename conflicting types in order to register with the orchestrator.
Suppose that you need to change your Profile
type to TurboTaxUserProfile
. However, you have existing clients that
query your profile with fragments and fragment definitions which require the actual type name:
query {
profile {
...userProfile
}
}
fragment userProfile on Profile {
userId
firstName
lastName
}
You cannot rename the type Profile
to something else because the new type will cause the queries of existing clients
with these fragments to fail!
How do we fix this?
With @rename directive, you can expose type Profile
in your service (and accept queries that specify the
Profile
type), but expose that type as something else through the orchestrator. For example, an introspection on your
service would yield Profile
, but an introspection on the orchestrator would yield Finance_Profile
type Query {
financialProfile: Profile
}
type Profile @rename(to: "Financial_Profile"){
income: Float
debt: Float
}
In the example configuration below, types Profile
will become Financial_Profile
in the orchestrator schema,
but will remain Profile
in your service. Subsequently, at runtime, when the orchestrator calls your service to resolve Financial_Profile
,
the namespace information will be stripped away, and your service will receive queries for Profile
.
Field Conflict Resolution
When the number of providers increase, you might run into a scenario where 2 providers provide the same field names. To get past it, you can use the @rename directive at the field level and resolve such field level naming conflicts.