在 GraphQL 中, 接口 () 也是一种命名字段集合, 定义规则与对象类型极其相似, 同样支持 字段名, 类型, 参数 三要素. 与 OOP 概念类似, GraphQL 中的接口只是对资源的抽象描述, 必须被其他对象类型实现, 才能正常使用. 我们来看个例子:
- interface NamedEntity {
- name: String
- }
- type User implements NamedEntity {
- id: ID!
- name: String
- }
上例中, NamedEntity 即为接口; User 是实现了该接口的对象类型. 注意, 实现类必须实现接口的所有字段, 字段名, 类型, 参数都必须与接口匹配. GraphQL 支持多继承:
- interface NamedEntity {
- name: String
- }
- interface Node {
- id: ID!
- }
- '''
- 非法示例:
- 没有实现 Node 接口的 id 字段
- '''
- type User implements NamedEntity & Node {
- name: String
- }
那么, 在什么场景下应该使用接口? 我们要理解, 接口是对资源的抽象描述, 不一定完备, 详尽, 但往往能表述多类相似资源的共同特征, 所以如果服务中存在一些具备共性的类型, 而用户需要关注共同的这一部分, 就应该使用接口. 比如, 我们有三种类型的商品: 饮料, 图书, 电器:
- type Drinks {
- id: ID!
- name: String!
- price: Float!
- '''
- 容量, 单位为 ml
- '''
- capacity: Float!
- }
- type Book {
- id: ID!
- name: String!
- price: Float!
- '''
- 作者
- '''
- author: [People]
- issn: String!
- }
- type ElectricalEquipment {
- id: ID!
- name: String!
- price: Float!
- '''
- 生产商
- '''
- manufacturer: Company
- }
上面三个商品对象有三个相似的属性: id,name,price, 是商品资源的不同类型. 现在, 我们需要实现根据名称搜索商品, 如果接口功能, 就必须使用近似的逻辑, 在三种类型节点上, 重复定义三次; 如果未来我们增加更多公共查询逻辑, 我们又得把逻辑重复三遍, 这显然不是一个好的设计. 这种情况下就适合使用接口, 将三个公共字段抽取出来, 再以接口类型对外暴露商品的聚合查询节点:
- interface Item {
- id: ID!
- name: String!
- price: Float!
- }
- type Drinks implements Item{ ... }
- type Book implements Item{ ... }
- type ElectricalEquipment implements Item{ ... }
- type Query {
- '''
- 商品列表, 支持按商品名查询
- '''
- items(name: Sring): [Item]
- }
而对于客户端来说, 针对接口节点的查询, 除了接口的属性外, 也支持查询实现类的字段:
- query {
- items(name: 'coka') {
- name
- ... on Drinks {
- capacity
- }
- }
- }
代码片段 ... on Drinks { capacity }, 被称为 Inline Fragments. 针对这段查询, 引擎会帮助判断如果解析结果为 Drinks 类型, 则返回 capacity 字段; 如果是其他类型的商品, 则不做进一步处理, 不会返回 capacity, 也不报错. 有兴趣的读者, 可以到 GraphQL 接口示例代码 看看.
注意, GraphQL 引擎没有检测类型的能力, 开发者必须为接口节点提供 resolveType 函数, 以在运行时指定解析结果的类型! 否则会报错:
Abstract type NamedEntity must resolve to an Object type at runtime for field Query.users with value { id: 1, name: \"foo\", age: 1, type: \"User\" }, received \"undefined\". Either the NamedEntity type should provide a \"resolveType\" function or each possible type should provide an \"isTypeOf\" function.
来源: https://juejin.im/post/5c18e9f85188252346022296