Schema Linter Rules
Reference for GraphOS linting rules with examples
This reference lists the rules that you can enforce with GraphOS schema linting, along with the code that GraphOS returns for each rule violation.
Naming rules
These rules enforce naming conventions. Rules are categorized by the part(s) of your schema that they correspond to.
Fields
FIELD_NAMES_SHOULD_BE_CAMEL_CASE
FIELD_NAMES_SHOULD_BE_CAMEL_CASE
What it does
Checks that all field names use camelCase
.
Rationale
camelCase
is a convention for field names in GraphQL.
Examples
The following example violates the rule:type User {
FirstName: String! # PascalCase
}
Use instead:
type User {
firstName: String # camelCase
}
RESTY_FIELD_NAMES
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
.
Examples
The following example violates the rule:type Query {
getUsers: [User!]!
}
Use instead:
type Query {
users: [User!]!
}
Types
These rules apply to all types that appear in a GraphQL schema, including:
Objects
Interfaces
Inputs
Enums
Unions
TYPE_NAMES_SHOULD_BE_PASCAL_CASE
TYPE_NAMES_SHOULD_BE_PASCAL_CASE
What it does
Checks that all type names use PascalCase
.
Rationale
PascalCase
is a convention for type names in GraphQL.
Examples
The following example violates the rule:type streamingService { # camelCase
id: ID!
}
Use instead:
type StreamingService { # PascalCase
id: ID!
}
TYPE_PREFIX
TYPE_PREFIX
What it does
Checks that type names don't start with the prefix Type
.
Rationale
Avoid using the redundant Type
prefix to shorten your schema definitions and improve readability.
Examples
The following example violates the rule:type TypeBook {
title: String!
}
Use instead:
type Book {
title: String!
}
TYPE_SUFFIX
TYPE_SUFFIX
What it does
Checks that type names don't use the suffix Type
.
Rationale
Avoid using the redundant Type
suffix to shorten your schema definitions and improve readability.
Examples
The following example violates the rule:type BookType {
title: String!
}
Use instead:
type Book {
title: String!
}
Objects
OBJECT_PREFIX
OBJECT_PREFIX
What it does
Checks that object type names don't start with the prefix Object
.
Rationale
Avoid using the redundant Object
prefix to shorten your schema definitions and improve readability.
Examples
The following example violates the rule:type ObjectBook {
title: String!
}
Use instead:
type Book {
title: String!
}
OBJECT_SUFFIX
OBJECT_SUFFIX
What it does
Checks that object type names don't use the suffix Object
.
Rationale
Avoid using the redundant Object
suffix to shorten your schema definitions and improve readability.
Examples
The following example violates the rule:type BookObject {
title: String!
}
Use instead:
type Book {
title: String!
}
Interfaces
INTERFACE_PREFIX
INTERFACE_PREFIX
What it does
Checks that interface type names don't start with the prefix Interface
.
Rationale
Avoid using the redundant Interface
prefix to shorten your schema definitions and improve readability.
Examples
The following example violates the rule:interface InterfaceBook {
title: String
author: String
}
Use instead:
interface Book {
title: String
author: String
}
INTERFACE_SUFFIX
INTERFACE_SUFFIX
What it does
Checks that interface type names don't use the suffix Interface
.
Rationale
Avoid using the redundant Interface
suffix to shorten your schema definitions and improve readability.
Examples
The following example violates the rule:interface BookInterface {
title: String
author: String
}
Use instead:
interface Book {
title: String
author: String
}
Inputs and arguments
INPUT_ARGUMENT_NAMES_SHOULD_BE_CAMEL_CASE
INPUT_ARGUMENT_NAMES_SHOULD_BE_CAMEL_CASE
What it does
Checks that argument names use camelCase
.
Rationale
camelCase
is a convention for argument names in GraphQL.
Examples
The following example violates the rule:type Mutation {
createBlogPost(BlogPostContent: BlogPostContent!): Post # PascalCase
}
Use instead:
type Mutation {
createBlogPost(blogPostContent: BlogPostContent!): Post # camelCase
}
INPUT_TYPE_SUFFIX
INPUT_TYPE_SUFFIX
What it does
Checks that input type names use the suffix Input
.
Rationale
Ending input type names with Input
distinguishes input types from "output" types like objects.
Examples
The following example violates the rule:input BlogPostDetails {
title: String!
content: String!
}
Use instead:
input BlogPostDetailsInput {
title: String!
content: String!
}
Enums
ENUM_VALUES_SHOULD_BE_SCREAMING_SNAKE_CASE
ENUM_VALUES_SHOULD_BE_SCREAMING_SNAKE_CASE
What it does
Checks that enum values use SCREAMING_SNAKE_CASE
.
Rationale
SCREAMING_SNAKE_CASE
is a convention for enum values in GraphQL.
Examples
The following example violates the rule:enum Amenity {
public_park # snake_case
}
Use instead:
enum Amenity {
PUBLIC_PARK # SCREAMING_SNAKE_CASE 😱
}
ENUM_PREFIX
ENUM_PREFIX
What it does
Checks that enum type names don't start with the prefix Enum
.
Rationale
Avoid using the redundant Enum
prefix to shorten your schema definitions and improve readability.
Examples
The following example violates the rule:enum EnumResidence {
HOUSE
APARTMENT
CONDO
}
Use instead:
enum Residence {
HOUSE
APARTMENT
CONDO
}
ENUM_SUFFIX
ENUM_SUFFIX
What it does
Checks that enum type names don't use the suffix Enum
.
Rationale
Avoid using the redundant Enum
suffix to shorten your schema definitions and improve readability.
Examples
The following example violates the rule:enum ResidenceEnum {
HOUSE
APARTMENT
CONDO
}
Use instead:
enum Residence {
HOUSE
APARTMENT
CONDO
}
ENUM_USED_AS_INPUT_WITHOUT_SUFFIX
ENUM_USED_AS_INPUT_WITHOUT_SUFFIX
What it does
If an enum type is used as an input argument, checks that its name uses the suffix Input
.
Rationale
Ending input arguments with Input
distinguishes inputs from "output" types like objects.
Examples
The following example violates the rule:enum Role {
EDITOR
VIEWER
}
type Query {
users(role: Role): [User!]!
}
Use instead:
enum RoleInput {
EDITOR
VIEWER
}
type Query {
users(role: RoleInput): [User!]!
}
ENUM_USED_AS_OUTPUT_DESPITE_SUFFIX
ENUM_USED_AS_OUTPUT_DESPITE_SUFFIX
What it does
If an enum is used as the return type of a non-input field, checks that its name doesn't use the suffix Input
.
Rationale
Including the suffix Input
on an output type is misleading.
Examples
The following example violates the rule:enum RoleInput {
EDITOR
VIEWER
}
type Query {
userRole(userId: ID!): RoleInput
}
Use instead:
enum Role {
EDITOR
VIEWER
}
type Query {
userRole(userId: ID!): Role
}
Directives
DIRECTIVE_NAMES_SHOULD_BE_CAMEL_CASE
DIRECTIVE_NAMES_SHOULD_BE_CAMEL_CASE
What it does
Checks that directive names use camelCase
.
Rationale
camelCase
is a convention for directive names in GraphQL.
Examples
The following example violates the rule:directive @SpecialField on FIELD_DEFINITION # PascalCase
Use instead:
directive @specialField on FIELD_DEFINITION # camelCase
Composition rulesSince 2.4
2.4
or later. You can update a graph's version from its Settings page in GraphOS Studio.Composition rules flag potential improvements to subgraph schemas used to compose a supergraph schema.
Inconsistent elements
These rules identity inconsistencies in fields, types, arguments, etc across subgraphs. Such inconsistencies can disrupt or even break composition.
Compatibility
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 price
fields in the example subgraphs below are inconsistent and incompatible because they use completely different types (Float
vs String
):
type Product {
id: ID!
name: String
price: Float
}
type Product {
id: ID!
name: String
price: String
}
These price
fields in the example subgraphs below are inconsistent but compatible since both use Float
s, but one is nullable and the other is the non-nullable list of Float
s.
type Product {
id: ID!
name: String
price: Float
}
type Product {
id: ID!
name: String
price: [Float]!
}
INCONSISTENT_ARGUMENT_PRESENCE
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. Learn more.
Examples
The following example violates the rule:type Product {
id: ID!
name: String
price(currency: Currency): Float
}
type Product {
id: ID!
name: String
price(currency: Currency, taxIncluded: Boolean): Float
}
Use instead:
type Product {
id: ID!
name: String
price(currency: Currency, taxIncluded: Boolean): Float
}
type Product {
id: ID!
name: String
price(currency: Currency, taxIncluded: Boolean): Float
}
INCONSISTENT_BUT_COMPATIBLE_ARGUMENT_TYPE
INCONSISTENT_BUT_COMPATIBLE_ARGUMENT_TYPE
What it does
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 compatible but inconsistent.
Rationale
The supergraph schema only includes arguments that are exactly the same for all subgraphs that define its field or directive. Learn more.
Examples
Because subgraph A's
price
field expects a non-nullable Currency
argument type and subgraph B allows a nullable Currency
argument type, the following example violates the rule:type Product {
id: ID!
name: String
price(currency: Currency!): Float
}
enum Currency {
USD
EUR
GBP
JPY
AUD
CAD
}
type Product {
id: ID!
name: String
price(currency: Currency): Float
}
enum Currency {
USD
EUR
GBP
JPY
AUD
CAD
}
Use instead:
type Product {
id: ID!
name: String
price(currency: Currency!): Float
}
enum Currency {
USD
EUR
GBP
JPY
AUD
CAD
}
type Product {
id: ID!
name: String
price(currency: Currency!): Float
}
enum Currency {
USD
EUR
GBP
JPY
AUD
CAD
}
INCONSISTENT_BUT_COMPATIBLE_FIELD_TYPE
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 compatible but inconsistent.
Rationale
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:
type Product {
id: ID!
name: String
price: Money
}
type Money {
amount: Float!
currency: Currency!
}
enum Currency {
USD
EUR
GBP
JPY
AUD
CAD
}
type Product {
id: ID!
name: String
price: Money!
}
type Money {
amount: Float!
currency: Currency!
}
enum Currency {
USD
EUR
GBP
JPY
AUD
CAD
}
Use instead:
type Product {
id: ID!
name: String
price: Money!
}
type Money {
amount: Float!
currency: Currency!
}
enum Currency {
USD
EUR
GBP
JPY
AUD
CAD
}
type Product {
id: ID!
name: String
price: Money!
}
type Money {
amount: Float!
currency: Currency!
}
enum Currency {
USD
EUR
GBP
JPY
AUD
CAD
}
INCONSISTENT_DEFAULT_VALUE_PRESENCE
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:type Product {
id: ID!
name: String
weight(kg: Float = 1.0): Float
}
type Product {
id: ID!
name: String
weight(kg: Float): Float
}
Use instead:
type Product {
id: ID!
name: String
weight(kg: Float = 1.0): Float
}
type Product {
id: ID!
name: String
weight(kg: Float = 1.0): Float
}
INCONSISTENT_DESCRIPTION
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:"""
A type representing a product.
"""
type Product {
id: ID!
name: String
}
"""
An object representing a product.
"""
type Product {
id: ID!
name: String
}
Use instead:
"""
A type representing a product.
"""
type Product {
id: ID!
name: String
}
"""
A type representing a product.
"""
type Product {
id: ID!
name: String
}
INCONSISTENT_ENTITY
INCONSISTENT_ENTITY
What it does
Checks that an object is consistently declared as an entity (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:type Product
@key(fields: "id") {
id: ID!
name: String
}
type Product {
id: ID!
stock: Int
}
Use instead:
type Product
@key(fields: "id") {
id: ID!
name: String
}
type Product
@key(fields: "id") {
id: ID!
stock: Int
}
INCONSISTENT_ENUM_VALUE_FOR_INPUT_ENUM
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. Learn more.
Examples
The following example violates the rule:enum ProductStatus {
AVAILABLE
SOLD_OUT
BACK_ORDER
}
input ProductInput {
name: String!
status: ProductStatus!
}
enum ProductStatus {
AVAILABLE
SOLD_OUT
}
input ProductInput {
name: String!
status: ProductStatus!
}
Use instead:
enum ProductStatus {
AVAILABLE
SOLD_OUT
BACK_ORDER
}
input ProductInput {
name: String!
status: ProductStatus!
}
enum ProductStatus {
AVAILABLE
SOLD_OUT
BACK_ORDER
}
input ProductInput {
name: String!
status: ProductStatus!
}
INCONSISTENT_ENUM_VALUE_FOR_OUTPUT_ENUM
INCONSISTENT_ENUM_VALUE_FOR_OUTPUT_ENUM
What it does
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. Learn more.
Examples
The following example violates the rule:enum OrderStatus {
CREATED
PROCESSING
COMPLETED
}
type Order {
name: String!
status: OrderStatus!
}
enum OrderStatus {
CREATED
COMPLETED
}
type Order {
name: String!
status: OrderStatus!
}
Use instead:
enum OrderStatus {
CREATED
PROCESSING
COMPLETED
}
type Order {
name: String!
status: OrderStatus!
}
enum OrderStatus {
CREATED
PROCESSING
COMPLETED
}
type Order {
name: String!
status: OrderStatus!
}
INCONSISTENT_EXECUTABLE_DIRECTIVE_LOCATIONS
INCONSISTENT_EXECUTABLE_DIRECTIVE_LOCATIONS
What it does
Checks that an executable directive definition is declared with consistent locations across all subgraphs.
Rationale
An executable directive is composed into the supergraph schema only when it is defined identically in all subgraphs. Learn more.
Examples
The following example violates the rule:directive @log(message: String!) on QUERY
directive @log(message: String!) on FIELD
Use instead:
directive @log(message: String!) on QUERY | FIELD
directive @log(message: String!) on QUERY | FIELD
INCONSISTENT_EXECUTABLE_DIRECTIVE_PRESENCE
INCONSISTENT_EXECUTABLE_DIRECTIVE_PRESENCE
What it does
Checks that an executable directive definition is declared in all subgraphs.
Rationale
An executable directive is composed into the supergraph schema only if it's defined in all subgraphs. Learn more.
Examples
The following example violates the rule:directive @modify(field: String!) on FIELD
# 🦗🦗🦗
Use instead:
directive @modify(field: String!) on FIELD
directive @modify(field: String!) on FIELD
INCONSISTENT_EXECUTABLE_DIRECTIVE_REPEATABLE
INCONSISTENT_EXECUTABLE_DIRECTIVE_REPEATABLE
What it does
Checks that an executable directive definition is marked repeatable
in all subgraphs that define it.
Rationale
Unless an executable directive is defined as repeatable
in all subgraphs, it won't be repeatable
in the supergraph.
Examples
The following example violates the rule:directive @validateLength(max: Int!) repeatable on FIELD
directive @validateLength(max: Int!) on FIELD
Use instead:
directive @validateLength(max: Int!) repeatable on FIELD
directive @validateLength(max: Int!) repeatable on FIELD
INCONSISTENT_INPUT_OBJECT_FIELD
INCONSISTENT_INPUT_OBJECT_FIELD
What it does
Checks that a field of an input object definition is defined in all the subgraphs that declare the input object.
Rationale
The supergraph schema includes only the input object fields that all subgraphs define for the object. Learn more.
Examples
The following example violates the rule:input ProductInput {
name: String
price: Float
}
input OrderInput {
product: ProductInput
}
input ProductInput {
name: String
}
input OrderInput {
product: ProductInput
}
Use instead:
input ProductInput {
name: String
price: Float
}
input OrderInput {
product: ProductInput
}
input ProductInput {
name: String
price: Float
}
input OrderInput {
product: ProductInput
}
INCONSISTENT_INTERFACE_VALUE_TYPE_FIELD
INCONSISTENT_INTERFACE_VALUE_TYPE_FIELD
What it does
Checks that a field of an interface value type (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. Learn more.
Examples
The following example violates the rule:interface Product {
id: ID!
name: String
cost: Float
}
type DigitalProduct implements Product {
id: ID!
name: String
cost: Float
size: Int
}
interface Product {
id: ID!
name: String
# cost is not defined in the interface
}
type PhysicalProduct implements Product {
id: ID!
name: String
cost: Float
weight: Float
}
Use instead:
interface Product {
id: ID!
name: String
cost: Float
}
type DigitalProduct implements Product {
id: ID!
name: String
cost: Float
size: Int
}
interface Product {
id: ID!
name: String
cost: Float
}
type PhysicalProduct implements Product {
id: ID!
name: String
cost: Float
weight: Float
}
INCONSISTENT_NON_REPEATABLE_DIRECTIVE_ARGUMENTS
INCONSISTENT_NON_REPEATABLE_DIRECTIVE_ARGUMENTS
What it does
Checks if a non-repeatable
directive is applied to a schema element across different subgraphs with differing arguments.
Rationale
Inconsistent directive argument usage can lead to misunderstandings and potential issues in client applications.
Examples
The following example violates the rule:type Product {
id: ID!
name: String
}
type Query {
allProducts: [Product] @customDirective(orderBy: "name")
}
type Product {
id: ID!
name: String
}
type Query {
allProducts: [Product] @customDirective(orderBy: "price")
}
Use instead:
type Product {
id: ID!
name: String
}
type Query {
allProducts: [Product] @customDirective(orderBy: "name")
}
type Product {
id: ID!
name: String
}
type Query {
allProducts: [Product] @customDirective(orderBy: "name")
}
INCONSISTENT_OBJECT_VALUE_TYPE_FIELD
INCONSISTENT_OBJECT_VALUE_TYPE_FIELD
What it does
Checks that object value types (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, omitted fields may be unresolvable. You can include the same types as shown below or check out Solutions for unresolvable fields.
Examples
The following example violates the rule:type Product {
id: ID! @shareable
name: String @shareable
price: Float
}
type Product {
id: ID! @shareable
name: String @shareable
}
Use instead:
type Product @shareable {
id: ID!
name: String
price: Float
}
type Product @shareable {
id: ID!
name: String
price: Float
}
INCONSISTENT_RUNTIME_TYPES_FOR_SHAREABLE_RETURN
INCONSISTENT_RUNTIME_TYPES_FOR_SHAREABLE_RETURN
What it does
Checks that a @shareable
field returns consistent sets of runtime types in all subgraphs in which it's defined.
Rationale
Each subgraph's resolver for a @shareable
field should behave identically. Otherwise, requests might return inconsistent results depending on which subgraph resolves the field. Learn more.
Examples
The following example violates the rule:type Product {
id: ID!
name: String
details: Details @shareable
}
type Details {
size: String
}
type Product {
id: ID!
name: String
details: Details @shareable
}
type Details {
weight: Float
}
Use instead:
type Product {
id: ID!
name: String
details: Details @shareable
}
type Details {
size: String
}
type Product {
id: ID!
name: String
details: Details @shareable
}
type Details {
size: String
}
INCONSISTENT_TYPE_SYSTEM_DIRECTIVE_LOCATIONS
INCONSISTENT_TYPE_SYSTEM_DIRECTIVE_LOCATIONS
What it does
Checks that a type system directive definition is declared with consistent locations across subgraphs.
Rationale
To ensure consistent expectations, it's best that all definitions declare the same locations. Learn more.
Examples
The following example violates the rule:directive @customDirective(message: String!) on OBJECT | FIELD_DEFINITION
directive @customDirective(message: String!) on FIELD_DEFINITION
Use instead:
directive @customDirective(message: String!) on OBJECT | FIELD_DEFINITION
directive @customDirective(message: String!) on OBJECT | FIELD_DEFINITION
INCONSISTENT_TYPE_SYSTEM_DIRECTIVE_REPEATABLE
INCONSISTENT_TYPE_SYSTEM_DIRECTIVE_REPEATABLE
What it does
Checks that a type system directive 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
. Learn more.
Examples
The following example violates the rule:directive @customDirective on OBJECT
directive @customDirective repeatable on OBJECT
Use instead:
directive @customDirective repeatable on OBJECT
directive @customDirective repeatable on OBJECT
INCONSISTENT_UNION_MEMBER
INCONSISTENT_UNION_MEMBER
What it does
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. Learn more.
Examples
The following example violates the rule:type Product {
id: ID!
name: String
}
type Service {
id: ID!
description: String
}
union SearchResult = Product | Service
type Product {
id: ID!
name: String
}
union SearchResult = Product
Use instead:
type Product {
id: ID!
name: String
}
type Service {
id: ID!
description: String
}
union SearchResult = Product | Service
type Product {
id: ID!
name: String
}
type Service {
id: ID!
description: String
}
union SearchResult = Product | Service
Overridden and unused elements
OVERRIDE_DIRECTIVE_CAN_BE_REMOVED
OVERRIDE_DIRECTIVE_CAN_BE_REMOVED
What it does
Checks that a field with the @override
directive no longer exists in a source subgraph.
Rationale
If a field with the @override
directive no longer exists in a source subgraph, the directive can be safely removed.
Examples
The following example violates the rule:type Product @key(fields: "id") {
id: ID!
inStock: Boolean! @override(from: "Subgraph B")
}
type Product @key(fields: "id") {
id: ID!
name: String!
}
Use instead:
type Product @key(fields: "id") {
id: ID!
inStock: Boolean!
}
type Product @key(fields: "id") {
id: ID!
name: String!
}
OVERRIDDEN_FIELD_CAN_BE_REMOVED
OVERRIDDEN_FIELD_CAN_BE_REMOVED
What it does
Checks if a field has been overridden by another subgraph.
Rationale
You should consider removing overridden fields to avoid confusion.
Examples
The following example violates the rule:type Product @key(fields: "id") {
id: ID!
inStock: Boolean! @override(from: "Subgraph B")
}
type Product @key(fields: "id") {
id: ID!
name: String!
inStock: Boolean!
}
Use instead:
type Product @key(fields: "id") {
id: ID!
name: String!
inStock: Boolean!
}
type Product @key(fields: "id") {
id: ID!
name: String!
}
OVERRIDE_MIGRATION_IN_PROGRESS
OVERRIDE_MIGRATION_IN_PROGRESS
What it does
Checks if a field migration is in progress.
Rationale
You should complete a field migration.
Examples
The following example violates the rule:type Product @key(fields: "id") {
id: ID!
inStock: Boolean! @override(from: "Subgraph B", label: "percent(50)")
}
type Product @key(fields: "id") {
id: ID!
name: String!
inStock: Boolean!
}
After completing the migration, use instead:
type Product @key(fields: "id") {
id: ID!
name: String!
inStock: Boolean!
}
type Product @key(fields: "id") {
id: ID!
name: String!
}
UNUSED_ENUM_TYPE
UNUSED_ENUM_TYPE
What it does
Checks if an enum type is defined but no field or argument in any subgraph references it.
Rationale
If the enum is defined, it should be used or removed.
Examples
The following example violates the rule:enum ProductStatus {
AVAILABLE
SOLD_OUT
}
type Product {
id: ID!
name: String
}
type Order {
id: ID!
product: Product
status: String
}
Use instead:
enum ProductStatus {
AVAILABLE
SOLD_OUT
}
type Product {
id: ID!
name: String
status: ProductStatus
}
type Order {
id: ID!
product: Product
status: ProductStatus
}
Directives
DIRECTIVE_COMPOSITION
DIRECTIVE_COMPOSITION
What it does
Checks for issues when composing custom directives.
MERGED_NON_REPEATABLE_DIRECTIVE_ARGUMENTS
MERGED_NON_REPEATABLE_DIRECTIVE_ARGUMENTS
What it does
Checks if a non-repeatable
directive has been applied to the same schema element in different subgraphs with different arguments. Learn more.
Rationale
Arguments should be consistent across a non-repeatable
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 repeatable
directive.
Examples
The following example violates the rule:type Product {
id: ID!
name: String
}
type Query {
products: [Product] @customDirective(orderBy: ["name"])
}
type Product {
id: ID!
name: String
}
type Query {
products: [Product] @customDirective(orderBy: ["price"])
}
Use instead:
type Product {
id: ID!
name: String
}
type Query {
products: [Product] @customDirective(orderBy: ["name", "price"])
}
type Product {
id: ID!
name: String
}
type Query {
products: [Product] @customDirective(orderBy: ["name", "price"])
}
NO_EXECUTABLE_DIRECTIVE_INTERSECTION
NO_EXECUTABLE_DIRECTIVE_INTERSECTION
What it does
Checks for executable directive definitions with no shared locations across subgraphs.
Rationale
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.
Examples
The following example violates the rule:directive @log(message: String!) on QUERY
directive @log(message: String!) on FIELD
Use instead:
directive @log(message: String!) on QUERY | FIELD
directive @log(message: String!) on QUERY | FIELD
FROM_SUBGRAPH_DOES_NOT_EXIST
FROM_SUBGRAPH_DOES_NOT_EXIST
What it does
Checks that the source subgraph specified by @override
directive exists.
Rationale
The @override
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.
Examples
The following example violates the rule:type Product @key(fields: "id") {
id: ID!
inStock: Boolean! @override(from: "Subgraph B")
}
# Subgraph B doesn't exist
Use instead:
type Product @key(fields: "id") {
id: ID!
inStock: Boolean! @override(from: "Subgraph B")
}
type Product @key(fields: "id") {
id: ID!
inStock: Boolean!
}
Other rules
These rules define conventions for the entire schema and directive usage outside of composition.
Schema
These rules apply to the entire schema.
DOES_NOT_PARSE
DOES_NOT_PARSE
What it does
Checks for malformed GraphQL schemas. To resolve, check your schema for syntax errors.
ALL_ELEMENTS_REQUIRE_DESCRIPTION
ALL_ELEMENTS_REQUIRE_DESCRIPTION
What it does
Checks that each element in the schema includes a description.
Rationale
Descriptions document your GraphQL schema so consumers of your graph can easily discover fields and learn how to use them. See examples.
Examples
The following example violates the rule:type User {
username: String!
}
Use instead:
"Represents a user"
type User {
"A username must be [8-64] characters."
username: String!
}
DEFINED_TYPES_ARE_UNUSED
DEFINED_TYPES_ARE_UNUSED
What it does
Checks that every type defined in a schema is 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:type SomeUnusedType { # Also fails the TYPE_SUFFIX rule!
name: String!
}
type AnActuallyUsedType {
name: String!
}
type Query {
hello: String!
title: AnActuallyUsedType
}
Use instead:
type Book {
title: String!
}
type Query {
books: [Book!]!
}
QUERY_DOCUMENT_DECLARATION
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.
Examples
The following example violates the rule:type Query {
users: [User!]!
}
query GetUsers {
# Don't define operations in a schema document
users {
id
}
}
Directives
CONTACT_DIRECTIVE_MISSING
CONTACT_DIRECTIVE_MISSING
What it does
Checks that subgraph schemas always provide owner contact details via the @contact
directive.
Rationale
You can use the @contact
directive 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. Learn more.
Examples
This example shows correct@contact
directive usage:"Annotate a schema with contact information for the subgraph owner"
directive @contact(
"Contact title of the subgraph owner"
name: String!
"URL where the subgraph's owner can be reached"
url: String
"Other relevant notes can be included here; supports markdown links"
description: String
) on SCHEMA
extend schema
@contact(
name: "Products Team"
url: "https://myteam.slack.com/archives/teams-chat-room-url"
description: "Send urgent issues to [#oncall](https://yourteam.slack.com/archives/oncall)."
)
DEPRECATED_DIRECTIVE_MISSING_REASON
DEPRECATED_DIRECTIVE_MISSING_REASON
What it does
Checks that the @deprecated
directive always includes a reason
argument.
Rationale
The reason
argument can help graph consumers understand which field to use instead of the @deprecated
one.
Examples
The following example violates the rule:type Product {
title: String @deprecated
name: String!
}
Use instead:
type Product {
title: String @deprecated(reason: "Use Product.name instead")
name: String!
}
TAG_DIRECTIVE_USES_UNKNOWN_NAME
TAG_DIRECTIVE_USES_UNKNOWN_NAME
What it does
Checks that the @tag
directive uses an approved value for its name
argument. You specify approved values in GraphOS Studio.
Rationale
This directive is used most commonly with GraphOS contracts. Validating @tag
names ensures contracts work as intended.
Custom rules
The schema linter is configurable with the predefined rules documented above. Custom rule creation isn't currently supported but is under consideration.