camelCase is a convention for field names in GraphQL.
Examples
The following example violates the rule:
GraphQL
❌ schema.graphql
typeUser{
FirstName:String!# PascalCase
}
Use instead:
GraphQL
✅ schema.graphql
typeUser{
firstName:String# camelCase
}
RESTY_FIELD_NAMES
What it does
Checks that a field's name doesn't start with any of the following verbs: get, list, post, put, patch.
Rationale
Fields should not start with a verb, except for mutations. For mutation fields, it's best to use a verb that describes the specific action being performed, for example, create, delete, or edit.
In some cases, inconsistency rules also indicate the compatibility of checked types. Two types are compatible if one is a non-nullable version, a list version, a subtype, or a combination of any of these of the other.
For example, the pricefields in the example subgraphs below are inconsistent and incompatible because they use completely different types (Float vs String):
GraphQL
Subgraph A
typeProduct{
id:ID!
name:String
price:Float
}
GraphQL
Subgraph B
typeProduct{
id:ID!
name:String
price:String
}
These pricefields in the example subgraphs below are inconsistent but compatible since both use Floats, but one is nullable and the other is the non-nullable list of Floats.
GraphQL
Subgraph A
typeProduct{
id:ID!
name:String
price:Float
}
GraphQL
Subgraph B
typeProduct{
id:ID!
name:String
price:[Float]!
}
INCONSISTENT_ARGUMENT_PRESENCE
What it does
Checks that an argument of a field or directive definition is present in all subgraphs.
Rationale
The supergraph schema only includes arguments that are exactly the same for all subgraphs that define its field or directive.
Checks that arguments (of a field, input field, or directive definition) have the exact same types in all subgraphs. This warning/error indicates the argument types are
Because subgraph A's pricefield expects a non-nullable Currencyargument type and subgraph B allows a nullable Currencyargument type, the following example violates the rule:
GraphQL
❌ Subgraph A
typeProduct{
id:ID!
name:String
price(currency:Currency!):Float
}
enumCurrency{
USD
EUR
GBP
JPY
AUD
CAD
}
GraphQL
❌ Subgraph B
typeProduct{
id:ID!
name:String
price(currency:Currency):Float
}
enumCurrency{
USD
EUR
GBP
JPY
AUD
CAD
}
Use instead:
GraphQL
✅ Subgraph A
typeProduct{
id:ID!
name:String
price(currency:Currency!):Float
}
enumCurrency{
USD
EUR
GBP
JPY
AUD
CAD
}
GraphQL
✅ Subgraph B
typeProduct{
id:ID!
name:String
price(currency:Currency!):Float
}
enumCurrency{
USD
EUR
GBP
JPY
AUD
CAD
}
INCONSISTENT_BUT_COMPATIBLE_FIELD_TYPE
What it does
Checks that fields have the exact same types in all subgraphs. This warning/error indicates the field types are
Inconsistent types can lead to discrepancies in the way data is retrieved and processed, resulting in unexpected client behavior.
Examples
The following example violates the rule:
GraphQL
❌ Subgraph A
typeProduct{
id:ID!
name:String
price:Money
}
typeMoney{
amount:Float!
currency:Currency!
}
enumCurrency{
USD
EUR
GBP
JPY
AUD
CAD
}
GraphQL
❌ Subgraph B
typeProduct{
id:ID!
name:String
price:Money!
}
typeMoney{
amount:Float!
currency:Currency!
}
enumCurrency{
USD
EUR
GBP
JPY
AUD
CAD
}
Use instead:
GraphQL
✅ Subgraph A
typeProduct{
id:ID!
name:String
price:Money!
}
typeMoney{
amount:Float!
currency:Currency!
}
enumCurrency{
USD
EUR
GBP
JPY
AUD
CAD
}
GraphQL
✅ Subgraph B
typeProduct{
id:ID!
name:String
price:Money!
}
typeMoney{
amount:Float!
currency:Currency!
}
enumCurrency{
USD
EUR
GBP
JPY
AUD
CAD
}
INCONSISTENT_DEFAULT_VALUE_PRESENCE
What it does
Checks that argument definitions (of a field, input field, or directive definition) consistently include—or consistently don't include—a default value in all subgraphs that define the argument.
Rationale
Inconsistent defaults can lead to discrepancies in the way data is retrieved and processed, resulting in unexpected client behavior.
Examples
The following example violates the rule:
GraphQL
❌ Subgraph A
typeProduct{
id:ID!
name:String
weight(kg:Float=1.0):Float
}
GraphQL
❌ Subgraph B
typeProduct{
id:ID!
name:String
weight(kg:Float):Float
}
Use instead:
GraphQL
✅ Subgraph A
typeProduct{
id:ID!
name:String
weight(kg:Float=1.0):Float
}
GraphQL
✅ Subgraph B
typeProduct{
id:ID!
name:String
weight(kg:Float=1.0):Float
}
INCONSISTENT_DESCRIPTION
What it does
Checks that a type's description is consistent across subgraphs.
Rationale
Inconsistent type descriptions can lead to inconsistent expectations around type values resulting in unexpected client behavior.
Examples
The following example violates the rule:
GraphQL
❌ Subgraph A
"""
A type representing a product.
"""
typeProduct{
id:ID!
name:String
}
GraphQL
❌ Subgraph B
"""
An object representing a product.
"""
typeProduct{
id:ID!
name:String
}
Use instead:
GraphQL
✅ Subgraph A
"""
A type representing a product.
"""
typeProduct{
id:ID!
name:String
}
GraphQL
✅ Subgraph B
"""
A type representing a product.
"""
typeProduct{
id:ID!
name:String
}
INCONSISTENT_ENTITY
What it does
Checks that an object is consistently declared as an
(has a @key) in all subgraphs in which the object is defined.
Rationale
If an object is only declared as an entity in some subgraphs, the federated schema won't have complete information about that entity.
Examples
The following example violates the rule:
GraphQL
❌ Subgraph A
typeProduct@key(fields:"id"){
id:ID!
name:String
}
GraphQL
❌ Subgraph B
typeProduct{
id:ID!
stock:Int
}
Use instead:
GraphQL
✅ Subgraph A
typeProduct@key(fields:"id"){
id:ID!
name:String
}
GraphQL
✅ Subgraph B
typeProduct@key(fields:"id"){
id:ID!
stock:Int
}
INCONSISTENT_ENUM_VALUE_FOR_INPUT_ENUM
What it does
Checks that values of an input enum type are consistently defined in all subgraphs that declare the enum.
Rationale
When a value of an enum that is only used as an input type is defined in only some of the subgraphs that declare the enum, inconsistent values won't be merged into the supergraph.
Checks that values of an output enum type are consistently defined in all subgraphs that declare the enum.
Rationale
When values of an output or unused enum type definition are inconsistent, all values are merged into the supergraph. Regardless, it can be helpful to set expectations by including all possible values in all subgraphs defining the enum.
(has no @key in any subgraph) is defined in all the subgraphs that declare the type.
Rationale
If different subgraphs contribute different fields to an interface type, any object types that implement that interface must define all contributed fields from all subgraphs. Otherwise, composition fails.
(has no @key in any subgraph) declare the same fields in all subgraphs that declare the type.
Rationale
When an object value type includes differing fields across subgraphs, the supergraph schema includes the union of all fields. Depending on which subgraph executes the query,
Checks that a @shareablefield returns consistent sets of runtime types in all subgraphs in which it's defined.
Rationale
Each subgraph's resolver for a @shareablefield should behave identically. Otherwise, requests might return inconsistent results depending on which subgraph resolves the field.
Checks that a type systemdirective definition is marked repeatable in all subgraphs that declare the directive and will be repeatable in the supergraph.
Rationale
To ensure consistent expectations, directives should have consistent definitions across subgraphs, including whether they are repeatable.
Checks that a member of a union definition is defined in all subgraphs that declare the union.
Rationale
When a union definition has inconsistent members, the supergraph schema includes all members in the union definition. Nevertheless, to ensure consistent expectations, it's best that all union definitions declare the same members across subgraphs.
directive's usage. If arguments differ, it may be a sign that subgraph owners need to communicate about the directive's usage. If the arguments need to differ, consider using a
Directives must only be used in the locations they are declared to belong in. If the same executable directive is defined with different locations in different subgraphs, it may be a sign that subgraph owners need to communicate about the directive's usage.
directive indicates that an object field is now resolved by a different subgraph. The directive can't work unless you specify an existing subgraph to resolve the field from.
Checks that every type defined in a schema is be used at least once.
Rationale
Unused types waste resources and should be immediately removed once they've been refactored out of a schema.
Examples
The following example violates the rule:
GraphQL
❌ schema.graphql
typeSomeUnusedType{# Also fails the TYPE_SUFFIX rule!
name:String!
}
typeAnActuallyUsedType{
name:String!
}
typeQuery{
hello:String!
title:AnActuallyUsedType
}
Use instead:
GraphQL
✅ schema.graphql
typeBook{
title:String!
}
typeQuery{
books:[Book!]!
}
QUERY_DOCUMENT_DECLARATION
What it does
Checks that schemas don't define operations, such as queries and mutations.
Rationale
Operations should be defined on the client side, not within schemas. Schemas define types, fields, and their relationships. Operations specify what data to fetch or change. Since what to fetch or change is a client concern, this separation allows for a clean and modular architecture, making it easier to manage and evolve the API over time.
Checks that subgraph schemas always provide owner contact details via the @contactdirective.
Rationale
You can use the @contactdirective to add your team's contact info to a subgraph schema. This info is displayed in GraphOS Studio, which helps other teams know who to contact for assistance with the subgraph.