4. 接口
10m

概览

在 Airlock 中,用户可以是 房东房客。这两种用户类型共享一些通用属性。例如,它们都有名字和个人资料图片。它们还有一些特定于其角色的属性:只有 房东 有个人资料简介和房源,而只有 房客 有预订。

为了将此业务逻辑实现到我们的中,我们可以使用接口。

在本课程中,我们将

  • 了解如何在 中实现接口类型。
  • 了解如何解析接口类型

什么是接口?

一个 接口是抽象类型,它定义了一组通用的 ,然后,任意数量的 都可以包括这些字段。

接口通常用于表示具有某些共享行为的不同类型之间的一种重要关系。例如,Airlock 架构为房东房客 定义了不同的类型,但这两種類型之间的共享属性在 用户 接口中被捕获。

使用接口时,称为实现对象类型。我们还说该类型“实现了”接口。例如,Host类型实现User接口。

一个接口规定了,其指定的实现类型必须遵循。换句话说, 必须包括该接口上定义的所有。因此,我们建议创建有意义的接口,以避免随着的发展进行不必要的模式维护。

实现对象类型还可以定义任意数量的其他,这些不是接口的一部分。

定义一个接口

中,我们使用interface关键字定义一个接口,然后是接口的名称。在大括号之后,我们定义,就像我们以前在其他模式中使用所做的那样。

这是 Airlock 的User接口的样子:

server/schema.graphql
"Represents an Airlock user's common properties"
interface User {
id: ID!
"The user's first and last name"
name: String!
"The user's profile photo URL"
profilePicture: String!
}

任何实现我们的User接口的类型都必须使用这些确切的返回类型(包括可空性)定义这些确切。它还可以定义任意数量的其他字段。(稍后详细介绍...)

实现接口

定义一个接口后,它可以由架构中的其他类型实现。

要定义一个实现的 ,我们首先编写关键字 type,然后跟上类型名称。然后,我们添加关键字 implements,然后跟上接口的名称。接下来,我们将接口定义的所有 添加到类型定义。

Airlock 定义 HostGuest 类型的过程如下,它们都实现了上一部分的 User 接口:

server/schema.graphql
type Host implements User {
id: ID!
"The user's first and last name"
name: String!
"The user's profile photo URL"
profilePicture: String!
"The host's profile bio description, will be shown in the listing"
profileDescription: String!
}
type Guest implements User {
id: ID!
"The user's first and last name"
name: String!
"The user's profile photo URL"
profilePicture: String!
"The reservations guest has"
bookings: [Booking]!
}

请注意, HostGuest 类型都有 属于 User 接口的其他类型(Host.profileDescriptionGuest.bookings)。

返回一个接口

接口还可以用作架构中的返回类型。在 Airlock 架构中, Review.author 是返回 User 类型的域的完美示例。评论作者可以是任何用户,无论是房东还是客人。

server/schema.graphql
type Review {
# ... other fields
"User that wrote the review"
author: User!
}

注意: Query.me 也是一个很好的示例。我们将在下一课深入了解该域。

解析一个接口

A 返回一个接口,它可以返回已实现该接口的任何。但这就带来了一个问题:对于任何给定的,我们如何知道字段将返回哪种实现类型?

例如,在 Airlock 中Review.author 可能会返回一个Host对象,也可能返回一个Guest对象。我们如何知道返回的对象是房东还是客人?

为了处理此事,我们需要定义一个特殊函数,名为__resolveType

解析程序 __resolveType

函数__resolveType负责确定正要返回哪种实现。它返回一个字符串,其中包含相对应对象类型的名称。

例如,User接口的__resolveType函数应返回“Host”或“Guest”之一,因为它们是在架构中定义的两种实现

与我们的其他函数不同,__resolveType接收三个可选 objcontextinfo

__resolveType(obj, context, info) {
// logic to determine which type to return goes here
}

确定 返回取决于应用程序!在 Airlock 的示例中,accounts 数据库中的每个用户都有一个 role 属性,该属性设置为 “Host”或 “Guest”。这非常适合使用,因为实现此接口的两种类型也是 HostGuest

User: {
__resolveType(user) {
return user.role; // returns "Host" or "Guest"
},
},

注意:由于我们不会使用最后两个 contextinfo),我们将它们从函数调用中省略。我们还将第一个 obj重命名为 user,以更好地说明什么是对象。

在 GraphOS Studio 中测试

让我们使用 来试用 ,该查询解析了接口。

  1. 在网络浏览器中,在 GraphOS Studio 沙盒打开 https://127.0.0.1:4000

  2. 操作选项卡中,让我们开始构建我们的 。首先,我们将添加 featuredListings 及其 reviews。然后,我们可以添加 author ,我们知道它将解析为 User 接口。

    query GetFeaturedListings {
    featuredListings {
    reviews {
    author {
    # TODO!
    }
    }
    }
    }

当我们添加 author 到我们的 时,请注意 文档面板如何更新以向我们显示此接口的可能 实现。在这里,可以看到封装在 User 接口中的共享 以及特定于 HostGuest的域。

https://127.0.0.1:4000
Adding the author field to a query for reviews, and the available fields on the types that implement it: Host and Guest

现在,我们只看看 ,使其在 User 接口中 共享两种实现类型: idnameprofilePicture

  1. 添加 idnameprofilePicture

    query GetFeaturedListings {
    featuredListings {
    reviews {
    author {
    id
    name
    profilePicture
    }
    }
    }
    }
  2. 当我们运行 时,我们看到我们的数据正在返回。太棒了!响应应如下面的对象所示。

在 Airlock 代码库中查看

查看在 Airlock 代码库中定义的接口。你可以在 server/schema.graphql 文件中找到它们。

练习

你何时应在 GraphQL 模式中使用接口?

使用以下模式完成下面的代码挑战

type Query {
availableBooks: [Book]
borrowedBooks(userId: ID!): [Book]
}
interface Book {
isbn: ID!
title: String!
genre: String!
}
type PictureBook implements Book {
isbn: ID!
title: String!
genre: String!
numberOfPictures: Int
isInColor: Boolean
}
type YoungAdultNovel implements Book {
isbn: ID!
title: String!
genre: String!
wordCount: Int
numberOfChapters: Int
}
代码挑战!

Book 接口编写 __resolveType 解析器。若要确定 Book 的类型,可以使用该书的 hasPictures 属性。 PictureBook 类型的 hasPictures 属性被设为 true,而 YoungAdultNovel 类型的 hasPictures 属性未被设置为 true

要点

  • 接口定义了一组通用 ,任何数量的 都必须包括在内。
  • 我们建议创建有意义的接口来避免不必要的模式维护,随着 的演变。

接下来

到目前为止,我们已经了解了如何实现接口以及解决所有实现类型共有的。

在下一课中,我们将了解如何 只属于一个实现类型而不属于另一个实现类型的。为此,我们需要了解查询片段

上一页