模式检查器规则
带有例子的GraphOS检查器规则参考
本参考列出了您可以强制执行的规则GraphOS模式检查,以及GraphOS为每个违规规则返回的代码。
命名规则
这些规则强制执行命名约定。规则按它们对应的模式部分进行分类。
字段
操作
检查所有字段名称是否使用camelCase
。理由
camelCase
是GraphQL中字段名称的约定。示例
以下示例违反了此规则
type User {FirstName: String! # PascalCase}
使用以下替代
type User {firstName: String # camelCase}
操作
检查一个字段名称不应以以下动词开头:get
、list
、post
、put
、patch
。理由
字段不应以动词开头,除非是突变。对于突变字段,最好使用描述所执行具体动作的动词,例如create
、delete
或edit
。示例
以下示例违反了此规则
type Query {getUsers: [User!]!}
使用以下替代
type Query {users: [User!]!}
类型
这些规则适用于出现在GraphQL模式中的所有类型,包括:
- 对象
- 接口
- 输入
- 枚举
- 联合
操作
检查所有类型名称是否使用PascalCase
。理由
PascalCase
是GraphQL中类型的约定。示例
以下示例违反了此规则
type streamingService { # camelCaseid: ID!}
使用以下替代
type StreamingService { # PascalCaseid: ID!}
操作
检查类型名称是否不以前缀Type
开头。理由
避免使用冗余的Type
前缀以缩短模式定义并提高可读性。示例
以下示例违反了此规则
type TypeBook {title: String!}
使用以下替代
type Book {title: String!}
操作
检查类型名称是否不使用后缀Type
。理由
避免使用冗余的Type
后缀以缩短模式定义并提高可读性。示例
以下示例违反了此规则
type BookType {title: String!}
使用以下替代
type Book {title: String!}
对象
操作
检查对象类型名称是否不以前缀Object
开头。理由
避免使用冗余的Object
前缀以缩短模式定义并提高可读性。示例
以下示例违反了此规则
type ObjectBook {title: String!}
使用以下替代
type Book {title: String!}
操作
检查对象类型名称是否不使用后缀Object
。理由
避免使用冗余的Object
后缀以缩短模式定义并提高可读性。示例
以下示例违反了此规则
type BookObject {title: String!}
使用以下替代
type Book {title: String!}
接口
操作
检查接口类型名称是否不以前缀Interface
开头。理由
避免使用重复的Interface
前缀来缩短您的模式定义并提高可读性。示例
以下示例违反了此规则
interface InterfaceBook {title: Stringauthor: String}
使用以下替代
interface Book {title: Stringauthor: String}
操作
检查接口类型名称不使用后缀Interface
。理由
避免使用重复的Interface
后缀来缩短您的模式定义并提高可读性。示例
以下示例违反了此规则
interface BookInterface {title: Stringauthor: String}
使用以下替代
interface Book {title: Stringauthor: String}
输入和参数
操作
检查参数名称使用camelCase
。理由
camelCase
是GraphQL中参数名称的一种约定。示例
以下示例违反了此规则
type Mutation {createBlogPost(BlogPostContent: BlogPostContent!): Post # PascalCase}
使用以下替代
type Mutation {createBlogPost(blogPostContent: BlogPostContent!): Post # camelCase}
操作
检查输入类型名称使用后缀Input
。理由
以Input
结尾的输入类型名称将输入类型与如对象之类的"输出"类型区分开来。示例
以下示例违反了此规则
input BlogPostDetails {title: String!content: String!}
使用以下替代
input BlogPostDetailsInput {title: String!content: String!}
枚举
操作
检查枚举值使用SCREAMING_SNAKE_CASE
。理由
SCREAMING_SNAKE_CASE
是GraphQL中枚举值的一种约定。示例
以下示例违反了此规则
enum Amenity {public_park # snake_case}
使用以下替代
enum Amenity {PUBLIC_PARK # SCREAMING_SNAKE_CASE 😱}
操作
检查枚举类型名称不以下缀Enum
开头。理由
避免使用重复的Enum
前缀来缩短您的模式定义并提高可读性。示例
以下示例违反了此规则
enum EnumResidence {HOUSEAPARTMENTCONDO}
使用以下替代
enum Residence {HOUSEAPARTMENTCONDO}
操作
检查枚举类型名称不使用后缀Enum
。理由
避免使用重复的Enum
后缀来缩短您的模式定义并提高可读性。示例
以下示例违反了此规则
enum ResidenceEnum {HOUSEAPARTMENTCONDO}
使用以下替代
enum Residence {HOUSEAPARTMENTCONDO}
操作
如果枚举类型用作输入参数,则检查其名称使用后缀Input
。理由
以Input
结尾的输入参数将输入与如对象之类的"输出"类型区分开来。示例
以下示例违反了此规则
enum Role {EDITORVIEWER}type Query {users(role: Role): [User!]!}
使用以下替代
enum RoleInput {EDITORVIEWER}type Query {users(role: RoleInput): [User!]!}
操作
如果枚举用作非输入字段的返回类型,则检查其名称不使用后缀Input
。理由
在输出类型上包括后缀Input
是误导性的。示例
以下示例违反了此规则
enum RoleInput {EDITORVIEWER}type Query {userRole(userId: ID!): RoleInput}
使用以下替代
enum Role {EDITORVIEWER}type Query {userRole(userId: ID!): Role}
指令
操作
检查指令名称使用camelCase
。理由
camelCase
是GraphQL中指令名称的一种约定。示例
以下示例违反了此规则
directive @SpecialField on FIELD_DEFINITION # PascalCase
使用以下替代
directive @specialField on FIELD_DEFINITION # camelCase
组合规则自2.4
ⓘ 注意
组合规则仅适用于图。您可以从或更高版本更新图版本。您可以从设置页面在中更新图版本。
组合规则指出了可能改善用于子图的模式,这些模式用于组合一个超级图模式。
不一致元素
这些规则识别字段、类型、参数等在各个子图中存在不一致性。可能导致组合失败。
兼容性
在某些情况下,不一致规则也指出了检查的类型兼容性。两种类型兼容,如果其中一种是非空版本、列表版本、子类型,或者它们的组合是另一种类型。
例如,下面示例子图中的price
字段不一致且不兼容,因为它们使用了完全不相同的类型(Float
与String
):
type Product {id: ID!name: Stringprice: Float}
type Product {id: ID!name: Stringprice: String}
下面示例子图中的price
字段不一致但兼容,因为它们都使用Float
,但一个是可空的,另一个是非可空的列表。
type Product {id: ID!name: Stringprice: Float}
type Product {id: ID!name: Stringprice: [Float]!}
操作
检查一个字段或指令定义的参数在所有子图中是否存在。示例
以下示例违反了此规则
type Product {id: ID!name: Stringprice(currency: Currency): Float}
type Product {id: ID!name: Stringprice(currency: Currency, taxIncluded: Boolean): Float}
使用以下替代
type Product {id: ID!name: Stringprice(currency: Currency, taxIncluded: Boolean): Float}
type Product {id: ID!name: Stringprice(currency: Currency, taxIncluded: Boolean): Float}
示例
因为子图 A 的 price
字段期望一个非空 Currency
参数类型,而子图 B 允许一个可为空的 Currency
参数类型,以下示例违反了规则:
type Product {id: ID!name: Stringprice(currency: Currency!): Float}enum Currency {USDEURGBPJPYAUDCAD}
type Product {id: ID!name: Stringprice(currency: Currency): Float}enum Currency {USDEURGBPJPYAUDCAD}
使用以下替代
type Product {id: ID!name: Stringprice(currency: Currency!): Float}enum Currency {USDEURGBPJPYAUDCAD}
type Product {id: ID!name: Stringprice(currency: Currency!): Float}enum Currency {USDEURGBPJPYAUDCAD}
操作
检查所有子图中字段是否有完全相同的类型。此警告/错误指示字段类型兼容但不一致。理由
不一致的类型可能导致数据检索和处理的差异,从而导致客户端行为意外。示例
以下示例违反了此规则
type Product {id: ID!name: Stringprice: Money}type Money {amount: Float!currency: Currency!}enum Currency {USDEURGBPJPYAUDCAD}
type Product {id: ID!name: Stringprice: Money!}type Money {amount: Float!currency: Currency!}enum Currency {USDEURGBPJPYAUDCAD}
使用以下替代
type Product {id: ID!name: Stringprice: Money!}type Money {amount: Float!currency: Currency!}enum Currency {USDEURGBPJPYAUDCAD}
type Product {id: ID!name: Stringprice: Money!}type Money {amount: Float!currency: Currency!}enum Currency {USDEURGBPJPYAUDCAD}
操作
检查子图中定义的参数定义(字段、输入字段或指令定义)是否始终包含或始终不包含默认值。理由
不一致的默认值可能导致数据检索和处理的差异,从而导致客户端行为意外。示例
以下示例违反了此规则
type Product {id: ID!name: Stringweight(kg: Float = 1.0): Float}
type Product {id: ID!name: Stringweight(kg: Float): Float}
使用以下替代
type Product {id: ID!name: Stringweight(kg: Float = 1.0): Float}
type Product {id: ID!name: Stringweight(kg: Float = 1.0): Float}
操作
检查跨子图中类型的描述是否一致。理由
不一致的类型描述可能导致对类型值的一致性预期导致客户端行为意外。示例
以下示例违反了此规则
"""A type representing a product."""type Product {id: ID!name: String}
"""An object representing a product."""type Product {id: ID!name: String}
使用以下替代
"""A type representing a product."""type Product {id: ID!name: String}
"""A type representing a product."""type Product {id: ID!name: String}
操作
检查对象是否在对象被定义的所有子图中始终一致地声明为 实体(具有@key
)。理由
如果在某些子图中对象仅声明为 实体),则联邦架构将没有该实体的完整信息。示例
以下示例违反了此规则
type Product @key(fields: "id") {id: ID!name: String}
type Product {id: ID!stock: Int}
使用以下替代
type Product @key(fields: "id") {id: ID!name: String}
type Product @key(fields: "id") {id: ID!stock: Int}
操作
检查所有声明该枚举的子图中定义的输入枚举类型的值是否一致。示例
以下示例违反了此规则
enum ProductStatus {AVAILABLESOLD_OUTBACK_ORDER}input ProductInput {name: String!status: ProductStatus!}
enum ProductStatus {AVAILABLESOLD_OUT}input ProductInput {name: String!status: ProductStatus!}
使用以下替代
enum ProductStatus {AVAILABLESOLD_OUTBACK_ORDER}input ProductInput {name: String!status: ProductStatus!}
enum ProductStatus {AVAILABLESOLD_OUTBACK_ORDER}input ProductInput {name: String!status: ProductStatus!}
操作
检查输出的枚举类型在声明枚举的所有子图中是否一致定义。示例
以下示例违反了此规则
enum OrderStatus {CREATEDPROCESSINGCOMPLETED}type Order {name: String!status: OrderStatus!}
enum OrderStatus {CREATEDCOMPLETED}type Order {name: String!status: OrderStatus!}
使用以下替代
enum OrderStatus {CREATEDPROCESSINGCOMPLETED}type Order {name: String!status: OrderStatus!}
enum OrderStatus {CREATEDPROCESSINGCOMPLETED}type Order {name: String!status: OrderStatus!}
操作
检查可执行指令在所有子图中的声明是否具有一致的位置。示例
以下示例违反了此规则
directive @log(message: String!) on QUERY
directive @log(message: String!) on FIELD
使用以下替代
directive @log(message: String!) on QUERY | FIELD
directive @log(message: String!) on QUERY | FIELD
操作
检查可执行指令是否在所有子图中声明。示例
以下示例违反了此规则
directive @modify(field: String!) on FIELD
# 🦗🦗🦗
使用以下替代
directive @modify(field: String!) on FIELD
directive @modify(field: String!) on FIELD
操作
检查可执行指令在被定义的所有子图中是否标记为可重复使用。理由
除非在所有子图中将可执行指令定义为可重复使用,否则它将不会在父图中被标记为可重复使用。示例
以下示例违反了此规则
directive @validateLength(max: Int!) repeatable on FIELD
directive @validateLength(max: Int!) on FIELD
使用以下替代
directive @validateLength(max: Int!) repeatable on FIELD
directive @validateLength(max: Int!) repeatable on FIELD
操作
检查输入对象定义的字段是否在声明该输入对象的所有子图中定义。示例
以下示例违反了此规则
input ProductInput {name: Stringprice: Float}input OrderInput {product: ProductInput}
input ProductInput {name: String}input OrderInput {product: ProductInput}
使用以下替代
input ProductInput {name: Stringprice: Float}input OrderInput {product: ProductInput}
input ProductInput {name: Stringprice: Float}input OrderInput {product: ProductInput}
操作
检查接口的值类型字段(在任何子图中都没有@key
)在声明该类型的所有子图中是否都进行了定义。示例
以下示例违反了此规则
interface Product {id: ID!name: Stringcost: Float}type DigitalProduct implements Product {id: ID!name: Stringcost: Floatsize: Int}
interface Product {id: ID!name: String# cost is not defined in the interface}type PhysicalProduct implements Product {id: ID!name: Stringcost: Floatweight: Float}
使用以下替代
interface Product {id: ID!name: Stringcost: Float}type DigitalProduct implements Product {id: ID!name: Stringcost: Floatsize: Int}
interface Product {id: ID!name: Stringcost: Float}type PhysicalProduct implements Product {id: ID!name: Stringcost: Floatweight: Float}
操作
检查非重复
指令是否被应用于跨不同子图具有不同参数的 schema 元素。 理由
不一致的指令参数使用可能导致客户端应用中的误解和潜在问题。示例
以下示例违反了此规则
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")}
使用以下替代
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")}
操作
检查对象的值类型(在任何子图中都没有@key
)在声明该类型的所有子图中都声明相同的字段。 示例
以下示例违反了此规则
type Product {id: ID! @shareablename: String @shareableprice: Float}
type Product {id: ID! @shareablename: String @shareable}
使用以下替代
type Product @shareable {id: ID!name: Stringprice: Float}
type Product @shareable {id: ID!name: Stringprice: Float}
操作
检查@shareable
字段在其定义的所有子图中返回一致的运行时类型集合。示例
以下示例违反了此规则
type Product {id: ID!name: Stringdetails: Details @shareable}type Details {size: String}
type Product {id: ID!name: Stringdetails: Details @shareable}type Details {weight: Float}
使用以下替代
type Product {id: ID!name: Stringdetails: Details @shareable}type Details {size: String}
type Product {id: ID!name: Stringdetails: Details @shareable}type Details {size: String}
操作
检查类型系统 指令定义在整个子图中声明位置的一致性。示例
以下示例违反了此规则
directive @customDirective(message: String!) on OBJECT | FIELD_DEFINITION
directive @customDirective(message: String!) on FIELD_DEFINITION
使用以下替代
directive @customDirective(message: String!) on OBJECT | FIELD_DEFINITION
directive @customDirective(message: String!) on OBJECT | FIELD_DEFINITION
操作
检查类型系统 指令定义在整个声明该指令并将其可重复
的子图中标有可重复标记,并在超级图中将可重复
。示例
以下示例违反了此规则
directive @customDirective on OBJECT
directive @customDirective repeatable on OBJECT
使用以下替代
directive @customDirective repeatable on OBJECT
directive @customDirective repeatable on OBJECT
操作
检查联合定义中的一个成员是否在声明该联合的所有子图中都有定义。示例
以下示例违反了此规则
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
使用以下替代
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
覆盖和未使用的元素
示例
以下示例违反了此规则
type Product @key(fields: "id") {id: ID!inStock: Boolean! @override(from: "Subgraph B")}
type Product @key(fields: "id") {id: ID!name: String!}
使用以下替代
type Product @key(fields: "id") {id: ID!inStock: Boolean!}
type Product @key(fields: "id") {id: ID!name: String!}
操作
检查一个字段是否被另一个子图覆盖。理由
您应该考虑移除被覆盖的字段以避免混淆。示例
以下示例违反了此规则
type Product @key(fields: "id") {id: ID!inStock: Boolean! @override(from: "Subgraph B")}
type Product @key(fields: "id") {id: ID!name: String!inStock: Boolean!}
使用以下替代
type Product @key(fields: "id") {id: ID!name: String!inStock: Boolean!}
type Product @key(fields: "id") {id: ID!name: String!}
操作
检查字段迁移是否正在进行。理由
您应该完成字段迁移。示例
以下示例违反了此规则
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!}
完成后,请使用以下方法
type Product @key(fields: "id") {id: ID!name: String!inStock: Boolean!}
type Product @key(fields: "id") {id: ID!name: String!}
操作
检查是否定义了一个枚举类型,但没有子图中引用它的字段或参数。理由
如果枚举已定义,应该使用或移除它。示例
以下示例违反了此规则
enum ProductStatus {AVAILABLESOLD_OUT}type Product {id: ID!name: String}
type Order {id: ID!product: Productstatus: String}
使用以下替代
enum ProductStatus {AVAILABLESOLD_OUT}type Product {id: ID!name: Stringstatus: ProductStatus}
type Order {id: ID!product: Productstatus: ProductStatus}
指令
操作
检查自定义指令组合时的问题。示例
以下示例违反了此规则
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"])}
使用以下替代
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"])}
理由
指令必须仅在它们被声明属于的位置中使用。如果相同的可执行指令在不同的子图中以不同的位置定义,这可能是子图拥有者需要就指令的使用进行沟通的迹象。示例
以下示例违反了此规则
directive @log(message: String!) on QUERY
directive @log(message: String!) on FIELD
使用以下替代
directive @log(message: String!) on QUERY | FIELD
directive @log(message: String!) on QUERY | FIELD
示例
以下示例违反了此规则
type Product @key(fields: "id") {id: ID!inStock: Boolean! @override(from: "Subgraph B")}
# Subgraph B doesn't exist
使用以下替代
type Product @key(fields: "id") {id: ID!inStock: Boolean! @override(from: "Subgraph B")}
type Product @key(fields: "id") {id: ID!inStock: Boolean!}
其他规则
这些规则定义了整个模式和指令的使用规范,但组合外。
模式
这些规则适用于整个模式。
操作
检查输入变量GraphQL 模式是否正确。要解决此问题,请检查您的模式是否存在语法错误。操作
检查模式中定义的每个类型是否至少使用一次。理由
未使用的类型会浪费资源,因此应该在将它们从模式中重构后立即删除。示例
以下示例违反了此规则
type SomeUnusedType { # Also fails the TYPE_SUFFIX rule!name: String!}type AnActuallyUsedType {name: String!}type Query {hello: String!title: AnActuallyUsedType}
使用以下替代
type Book {title: String!}type Query {books: [Book!]!}
操作
检查模式是否没有定义操作(例如查询和突变)。理由
操作应在客户端定义,而不是在模式中定义。模式定义类型、字段及其关系。操作指定要获取或更改的数据。由于要获取或更改的内容是客户端的关切,这种分离允许构建清洁和模块化的架构,从而使得管理和发展 API 更容易。示例
以下示例违反了此规则
type Query {users: [User!]!}query GetUsers {# Don't define operations in a schema documentusers {id}}
指令
操作
检查子图模式是否始终通过@contact
指令提供所有者联系信息。示例
以下示例显示了正确使用@contact
指令的示例:
"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 SCHEMAextend 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
指令是否始终包含一个reason
参数。理由
原因参数
可以帮助图表消费者了解应使用哪个字段而不是@已弃用
的那个。示例
以下示例违反了此规则
type Product {title: String @deprecatedname: String!}
使用以下替代
type Product {title: String @deprecated(reason: "Use Product.name instead")name: String!}
自定义规则
模式检查器可以配置如下文所述的预定义规则。目前不支持自定义规则的创建,但正在考虑中。