联合和接口
抽象架构类型
联合体和接口是抽象GraphQL类型,允许模式字段返回多种对象类型之一。
联合体类型
当你定义一个联合体类型时,声明哪些对象类型包含在联合体中:
union Media = Book | Movie
一个字段可以有一个联合体(或该联合体的列表)作为其返回类型。在这种情况下,它可以返回联合体中包含的任何对象类型:
type Query {allMedia: [Media] # This list can include both Book and Movie objects}
联合体中包含的所有类型必须是对象类型(不是标量,输入类型等)。包含的类型不需要共享任何字段。
示例
以下模式定义了一个SearchResult
联合体类型,可以返回Book
或Author
:
union SearchResult = Book | Authortype Book {title: String!}type Author {name: String!}type Query {search(contains: String): [SearchResult!]}
The SearchResult
联合体允许Query.search
返回包含Book
和Author
的列表。
查询联合体
GraphQL客户端不知道如果字段的返回类型是联合类型时,该字段会返回哪种对象类型。为了处理这种情况,可以查询包含多个可能类型的子字段。
以上是有效的查询:
query GetSearchResults {search(contains: "Shakespeare") {# Querying for __typename is almost always recommended,# but it's even more important when querying a field that# might return one of multiple types.__typename... on Book {title}... on Author {name}}}
这个查询使用了内联片段来获取结果对象的title
(如果是Book
),或它的name
(如果是Author
)。Web 客户端可以通过传递可能的类型选项来了解这种多态关系。
以上查询的有效结果如下:
{"data": {"search": [{"__typename": "Book","title": "The Complete Works of William Shakespeare"},{"__typename": "Author","name": "William Shakespeare"}]}}
解决联合类型
要完全解析一个联合类型,Apollo Server需要指定返回联合类型的哪种类型。Apollo Server。要实现这一点,您需要在Resolver映射中为联合类型定义一个__resolveType
函数。
该__resolveType
函数负责确定对象的对应GraphQL类型,并以字符串形式返回该类型的名称。它可以使用任何逻辑来完成此操作,例如:
- 检查联合类型中是否包含特定类型的唯一字段
- 使用
instanceof
,如果JavaScript对象的类型与其GraphQL对象类型相关
以下是上述SearchResult
__resolveType
函数的基本实现:
const resolvers = {SearchResult: {__resolveType(obj, contextValue, info){// Only Author has a name fieldif(obj.name){return 'Author';}// Only Book has a title fieldif(obj.title){return 'Book';}return null; // GraphQLError is thrown},},Query: {search: () => { ... }},};const server = new ApolloServer({typeDefs,resolvers,});const { url } = await startStandaloneServer(server);console.log(`🚀 Server ready at: ${url}`);
如果__resolveType
函数返回的任何值都不是有效的类型名称,则相关操作会产生GraphQL错误。
接口类型
接口指定一个多对象类型可以包含的字段集:
interface Book {title: String!author: Author!}
如果一个对象类型实现了接口,那么它必须包含该接口的所有字段:
type Textbook implements Book {title: String! # Must be presentauthor: Author! # Must be presentcourses: [Course!]!}
一个字段可以作为接口(或该接口的列表)作为其返回类型。在这种情况下,它可以返回实现了该接口的任何对象类型:
type Query {books: [Book!]! # Can include Textbook objects}
示例
以下模式定义了一个Book接口,以及两个实现它的对象类型:
interface Book {title: String!author: Author!}type Textbook implements Book {title: String!author: Author!courses: [Course!]!}type ColoringBook implements Book {title: String!author: Author!colors: [String!]!}type Query {books: [Book!]!}
在这个模式中,Query.books返回一个可以包含Textbook和ColoringBook的列表。
查询接口
如果一个字段的返回类型是接口,那么客户端可以查询该接口的任何包含的字段:
query GetBooks {books {titleauthor}}
客户端还可以查询接口中不包含的子字段:
query GetBooks {books {# Querying for __typename is almost always recommended,# but it's even more important when querying a field that# might return one of multiple types.__typenametitle... on Textbook {courses {# Only present in Textbookname}}... on ColoringBook {colors # Only present in ColoringBook}}}
此 查询 使用 内联片段 来获取一本 Book
的课程(如果是 Textbook
)或其 colors
(如果是 ColoringBook
)。可以通过传递 possibleTypes 选项来告知网页客户端这种多态关系。
以上查询的有效结果如下:
{"data": {"books": [{"__typename": "Textbook","title": "Wheelock's Latin","courses": [{"name": "Latin I"}]},{"__typename": "ColoringBook","title": "Oops All Water","colors": ["Blue"]}]}}
解决一个接口
与联合类型一样, Apollo Server 需要接口来定义一个 __resolveType
函数以确定返回哪种实现 对象类型。
以下是上面定义的 Book
接口的 __resolveType
函数的例子:
const resolvers = {Book: {__resolveType(book, contextValue, info){// Only Textbook has a courses fieldif(book.courses){return 'Textbook';}// Only ColoringBook has a colors fieldif(book.colors){return 'ColoringBook';}return null; // GraphQLError is thrown},},Query: {books: () => { ... }},};