文档字符串(Documentation strings)

GraphQL的架构定义语言(SDL)支持启用markdown的文档字符串。这些可以帮助数据图的使用者发现字段并学习如何使用它们。
以下代码段显示了如何同时使用单行字符串文字和多行块:

  1. "Description for the type"
  2. type MyObjectType {
  3. """
  4. Description for field
  5. Supports **multi-line** description for your [API](http://example.com)!
  6. """
  7. myField: String!
  8. otherField(
  9. "Description for argument"
  10. arg: Int
  11. )
  12. }

有据可查的架构提供了增强的开发体验,因为GraphQL开发工具(例如 Apollo VS Code扩展 和GraphQL Playground)会自动完成字段名称以及提供时的描述。此外,Apollo Graph Manager在使用其指标报告和客户端感知功能时,会在字段使用情况和性能详细信息的旁边显示说明。

命名约定(Documentation strings)

GraphQL规范很灵活,没有强加特定的命名准则。但是,建立一组约定以确保整个组织的一致性很有帮助。
我们建议以下内容:

  • 字段名称应使用camelCase。许多GraphQL客户端是用JavaScript,Java,Kotlin或Swift编写的,所有这些都建议camelCase使用变量名。
  • 类型名称应使用PascalCase。这与上述语言中定义类的方式匹配。
  • 枚举名称应使用PascalCase
  • 枚举值应使用ALL_CAPS,因为它们类似于常量。

这些约定有助于确保大多数客户端不需要定义额外的逻辑即可转换服务器返回的结果。

查询驱动的架构设计(Query-driven schema design)

如果您的数据存储区包含客户尚不需要的字段或关系,请从您的模式中将其忽略。向架构中添加新字段比删除某些客户端正在使用的现有字段更加容易和安全。

If your data store includes a field or relationship that your clients don’t need yet, omit it from your schema. It’s easier and safer to add a new field to a schema than it is to remove an existing field that some of your clients are using.

查询驱动模式的示例

假设我们正在创建一个Web应用程序,该应用程序列出了我们所在地区的近期活动。我们希望该应用程序显示每个事件的名称,日期和位置,以及天气预报。
在这种情况下,我们希望Web应用程序能够执行具有类似于以下内容的结构的查询:

  1. query EventList {
  2. upcomingEvents {
  3. name
  4. date
  5. location {
  6. name
  7. weather {
  8. temperature
  9. description
  10. }
  11. }
  12. }
  13. }

因为我们知道这是对我们的客户有帮助的数据结构,所以可以通知我们架构的结构:

  1. type Query {
  2. upcomingEvents: [Event]
  3. }
  4. type Event {
  5. name: String
  6. date: String
  7. location: Location
  8. }
  9. type Location {
  10. name: String
  11. weather: WeatherInfo
  12. }
  13. type WeatherInfo {
  14. temperature: Float
  15. description: String
  16. }

如前所述,可以使用来自不同数据源(或多个数据源)的数据填充这些类型中的每一个。例如,该Event类型的namedate可能会填充有我们后端数据库中的数据,而该WeatherInfo类型的可能会填充有来自第三方Weather API的数据。

设计mutation(Designing mutations)

在GraphQL中,推荐每个mutation的响应都包含该mutation修改的数据。这使客户端无需发送后续查询即可获取最新的持久化数据。

一个模式,支持更新emailUser会包括以下内容:

  1. type Mutation {
  2. # This mutation takes id and email parameters and responds with a User
  3. updateUserEmail(id: ID!, email: String!): User
  4. }
  5. type User {
  6. id: ID!
  7. name: String!
  8. email: String!
  9. }
  1. mutation updateMyUser {
  2. updateUserEmail(id: 1, email: "jane@example.com"){
  3. id
  4. name
  5. email
  6. }
  7. }

After the GraphQL server executes the mutation and stores the new email address for the user, it responds to the client with the following:

  1. {
  2. "data": {
  3. "updateUserEmail": {
  4. "id": "1",
  5. "name": "Jane Doe",
  6. "email": "jane@example.com"
  7. }
  8. }
  9. }

构建mutation返回值(Structuring mutation responses)

此外,由于mutation会修改数据,因此比query引起错误的可能性要大得多。mutation甚至可能导致部分错误,在该错误中,成功修改了一条数据而没有成功修改另一条数据。无论错误的类型如何,以一致的方式将错误传达回客户端都是很重要的。

Additionally, mutations are much more likely than queries to cause errors, because they modify data. A mutation might even result in a partial error, in which it successfully modifies one piece of data and fails to modify another. Regardless of the type of error, it’s important that the error is communicated back to the client in a consistent way.

为了帮助解决这两个问题,建议MutationResponse您在架构中定义一个接口,以及实现该接口的对象类型的集合(one for each of your mutations)。

下面是什么MutationResponse接口的样子:

  1. interface MutationResponse {
  2. code: String!
  3. success: Boolean!
  4. message: String!
  5. }

这是实现对象类型的样子:

  1. type UpdateUserEmailMutationResponse implements MutationResponse {
  2. code: String!
  3. success: Boolean!
  4. message: String!
  5. user: User
  6. }

我们的updateUserEmail指定UpdateUserEmailMutationResponse为返回类型(而不是User),其响应的结构如下:

  1. {
  2. "data": {
  3. "updateUser": {
  4. "code": "200",
  5. "success": true,
  6. "message": "User email was successfully updated",
  7. "user": {
  8. "id": "1",
  9. "name": "Jane Doe",
  10. "email": "jane@example.com"
  11. }
  12. }
  13. }
  14. }
  • code是代表数据传输状态的字符串。可以将其视为HTTP状态代码。
  • success是一个布尔值,指示mutation是否成功。这允许客户端进行粗略检查,以了解是否存在故障。
  • message是描述突变结果的人类可读字符串。它打算在产品的UI中使用。
  • user通过实现类型添加UpdateUserEmailMutationResponse来将新更新的用户返回给客户端。

如果某个 mutation 修改了多种类型(例如我们前面的“喜欢”博客文章的示例),则其实现类型可以为每个被修改的类型包括一个单独的字段:

  1. type LikePostMutationResponse implements MutationResponse {
  2. code: String!
  3. success: Boolean!
  4. message: String!
  5. post: Post
  6. user: User
  7. }

Because our hypothetical likePost mutation modifies fields on both a Post and a User, its response object includes fields for both of those types. A response has the following structure:

  1. {
  2. "data": {
  3. "likePost": {
  4. "code": "200",
  5. "success": true,
  6. "message": "Thanks!",
  7. "post": {
  8. "id": "123",
  9. "likes": 5040
  10. },
  11. "user": {
  12. "likedPosts": ["123"]
  13. }
  14. }
  15. }
  16. }

遵循此模式可为客户端提供有关每个请求的操作结果的有用的详细信息。有了这些信息,开发人员可以更好地应对其客户端代码中的操作失败。