

本文属于机器翻译版本。若本译文内容与英语原文存在差异，则一律以英文原文为准。

# GraphQL 类型参考
<a name="type-reference"></a>

GraphQL 中的标量类型表示 GraphQL 架构中的基元叶值。这些是解析为单个值的最基本数据类型。与对象类型不同，标量类型不能有子字段。GraphQL 附带了一组默认的标量类型：
+ **Int**：一个 32 位有符号整数 
+ **Float**：一个有符号的双精度浮点值 
+ **String**：UTF-8 字符序列 
+ **Boolean**：true 或 false 值
+ **ID**：唯一标识符，通常用于重新获取对象或用作缓存键

这些标量类型可作为架构中更复杂类型的构建基块。它们用于定义包含简单单值的字段。除了这些内置标量外，AWS AppSync 还提供了用于不同使用案例的其他标量。

GraphQL 中的接口和联合是抽象类型，支持灵活且可扩展的架构设计。它们提供了对相关类型进行分组和启用多态查询的机制。GraphQL 中的接口是一种抽象类型，它定义了一组字段，类型必须包含这些字段才能实施接口。它通过指定实施类型必须具有的一组通用字段来充当对象的合约。如果要返回一个可以是几种不同类型的对象或字段，但仍有一些保证字段，那么接口很有用。相比之下，GraphQL 中的联合表示的类型可能是几种对象类型之一，但未定义这些类型之间的任何通用字段。如果要返回一个可以是多种类型的字段，而这些类型不一定共用一些通用字段，那么联合很有用。在字段可能返回不同类型数据的场景中，接口和联合都特别有用，使客户端能够根据返回的类型查询特定字段。

本节用作架构类型参考。

**主题**
+ [GraphQL 中的标量类型](https://docs.aws.amazon.com/appsync/latest/devguide/scalars.html)
+ [GraphQL 中的接口和联合](https://docs.aws.amazon.com/appsync/latest/devguide/interfaces-and-unions.html)

# GraphQL 中的标量类型
<a name="scalars"></a>

GraphQL 对象类型具有名称和字段，并且这些字段可以具有子字段。最终，对象类型的字段必须解析为*标量*类型，这些类型表示查询的叶节点。有关对象类型和标量的更多信息，请参阅 GraphQL 网站上的 [Schemas and types](https://graphql.org/learn/schema/)。

除了默认 GraphQL 标量集以外，AWS AppSync 还允许您使用以 *AWS* 前缀开头的**服务定义的标量**。AWS AppSync 不支持创建**用户定义的**（自定义）标量。您必须使用默认标量或 *AWS* 标量。

您不能将 *AWS* 作为自定义对象类型的前缀。

下一节是架构类型参考。

## 默认标量
<a name="graph-ql-base-scalars"></a>

GraphQL 定义了以下默认标量：

### 默认标量列表
<a name="graph-ql-base-scalars-list"></a>

`ID`  
对象的唯一标识符。该标量像 `String` 一样序列化，但并不意味着用户可读。

`String`  
UTF-8 字符序列。

`Int`  
-(231) 和 231-1 之间的整数值。

`Float`  
IEEE 754 浮点值。

`Boolean`  
一个布尔值，可以是 `true` 或 `false`。

## AWS AppSync 标量
<a name="graph-ql-aws-appsync-scalars"></a>

AWS AppSync 定义了以下标量：

### AWS AppSync 标量列表
<a name="graph-ql-aws-appsync-scalars-list"></a>

`AWSDate`  
格式为 `YYYY-MM-DD` 的扩展 [ISO 8601 日期](https://en.wikipedia.org/wiki/ISO_8601#Calendar_dates)字符串。

`AWSTime`  
格式为 `hh:mm:ss.sss` 的扩展 [ISO 8601 时间](https://en.wikipedia.org/wiki/ISO_8601#Times)字符串。

`AWSDateTime`  
格式为 `YYYY-MM-DDThh:mm:ss.sssZ` 的扩展 [ISO 8601 日期和时间](https://en.wikipedia.org/wiki/ISO_8601#Combined_date_and_time_representations)字符串。

**注意**  
`AWSDate`、`AWSTime` 和 `AWSDateTime` 标量可以选择包含[时区偏移](https://en.wikipedia.org/wiki/ISO_8601#Time_zone_designators)。例如，值 `1970-01-01Z`、`1970-01-01-07:00` 和 `1970-01-01+05:30` 对于 `AWSDate` 均有效。时区偏移必须是 `Z` (UTC) 或以小时和分钟以及秒（可选）为单位的偏移。例如 `±hh:mm:ss`。时区偏移中的秒字段被认为有效，即使它不是 ISO 8601 标准的一部分。

`AWSTimestamp`  
表示 `1970-01-01-T00:00Z` 之前或之后的秒数的整数值。

`AWSEmail`  
采用 [RFC 822](https://tools.ietf.org/html/rfc822) 定义的格式 `local-part@domain-part` 的电子邮件地址。

`AWSJSON`  
JSON 字符串。任何有效的 JSON 构造自动作为映射、列表或标量值解析并加载到解析器代码中，而不是作为文本输入字符串。不带引号的字符串或其他无效的 JSON 将导致 GraphQL 验证错误。

`AWSPhone`  
电话号码。该值存储为字符串。电话号码可以包含空格或连字符以分隔数字组。没有国家/地区代码的电话号码假定为符合[北美编号计划 (NANP)](https://en.wikipedia.org/wiki/North_American_Numbering_Plan) 的美国/北美号码。

`AWSURL`  
[RFC 1738](https://tools.ietf.org/html/rfc1738) 定义的 URL。例如，`https://www.amazon.com/dp/B000NZW3KC/` 或 `mailto:example@example.com`。URL 必须包含模式（`http`、`mailto`），并且不能在路径部分中包含两个正斜杠 (`//`)。

`AWSIPAddress`  
有效的 IPv4 或 IPv6 地址。IPv4 地址应采用四点表示法 (`123.12.34.56`)。IPv6 地址应采用以冒号分隔的无括号格式 (`1a2b:3c4b::1234:4567`)。您可以包含可选的 CIDR 后缀 (`123.45.67.89/16`) 以指示子网掩码。

## 架构用法示例
<a name="example-schema-usage"></a>

以下示例 GraphQL 架构将所有自定义标量作为“对象”，并显示基本 put、get 和 list 操作的解析器请求和响应模板。最后，该示例说明了在运行查询和变更时如何使用该架构。

```
type Mutation {
    putObject(
        email: AWSEmail,
        json: AWSJSON,
        date: AWSDate,
        time: AWSTime,
        datetime: AWSDateTime,
        timestamp: AWSTimestamp,
        url: AWSURL,
        phoneno: AWSPhone,
        ip: AWSIPAddress
    ): Object
}

type Object {
    id: ID!
    email: AWSEmail
    json: AWSJSON
    date: AWSDate
    time: AWSTime
    datetime: AWSDateTime
    timestamp: AWSTimestamp
    url: AWSURL
    phoneno: AWSPhone
    ip: AWSIPAddress
}

type Query {
    getObject(id: ID!): Object
    listObjects: [Object]
}

schema {
    query: Query
    mutation: Mutation
}
```

`putObject` 的请求模板可能如下所示。`putObject` 使用 `PutItem` 操作在 Amazon DynamoDB 表中创建或更新项目。请注意，该代码片段没有配置 Amazon DynamoDB 表以作为数据来源。这仅用作一个示例：

```
{
    "version" : "2017-02-28",
    "operation" : "PutItem",
    "key" : {
        "id": $util.dynamodb.toDynamoDBJson($util.autoId()),
    },
    "attributeValues" : $util.dynamodb.toMapValuesJson($ctx.args)
}
```

`putObject` 的响应模板返回以下结果：

```
$util.toJson($ctx.result)
```

`getObject` 的请求模板可能如下所示。`getObject` 使用 `GetItem` 操作为给定主键的项目返回一组属性。请注意，该代码片段没有配置 Amazon DynamoDB 表以作为数据来源。这仅用作一个示例：

```
{
    "version": "2017-02-28",
    "operation": "GetItem",
    "key": {
        "id": $util.dynamodb.toDynamoDBJson($ctx.args.id),
    }
}
```

`getObject` 的响应模板返回以下结果：

```
$util.toJson($ctx.result)
```

`listObjects` 的请求模板可能如下所示。`listObjects` 使用 `Scan` 操作返回一个或多个项目和属性。请注意，该代码片段没有配置 Amazon DynamoDB 表以作为数据来源。这仅用作一个示例：

```
{
    "version" : "2017-02-28",
    "operation" : "Scan",
}
```

`listObjects` 的响应模板返回以下结果：

```
$util.toJson($ctx.result.items)
```

以下是将该架构与 GraphQL 查询一起使用的一些示例：

```
mutation CreateObject {
    putObject(email: "example@example.com"
        json: "{\"a\":1, \"b\":3, \"string\": 234}"
        date: "1970-01-01Z"
        time: "12:00:34."
        datetime: "1930-01-01T16:00:00-07:00"
        timestamp: -123123
        url:"https://amazon.com"
        phoneno: "+1 555 764 4377"
        ip: "127.0.0.1/8"
    ) {
        id
        email
        json
        date
        time
        datetime
        url
        timestamp
        phoneno
        ip
    }
}

query getObject {
    getObject(id:"0d97daf0-48e6-4ffc-8d48-0537e8a843d2"){
        email
        url
        timestamp
        phoneno
        ip
    }
}

query listObjects {
    listObjects {
        json
        date
        time
        datetime
    }
}
```

# GraphQL 中的接口和联合
<a name="interfaces-and-unions"></a>

GraphQL 类型系统支持[接口](https://graphql.org/learn/schema/#interfaces)。接口会公开特定的字段组合，类型要实施接口，必须包含这些字段。

GraphQL 类型系统还支持[联合](https://graphql.org/learn/schema/#union-types)。联合与接口相同，只是联合未定义一组通用字段。如果可能的类型没有共享的逻辑层次结构，联合通常比接口更加常用。

下一节是架构类型参考。

## 接口示例
<a name="interfaces"></a>

我们可以表示一个 `Event` 接口，它表示任何种类的活动或人群聚集。一些可能的事件类型是 `Concert`、`Conference` 和 `Festival`。这些类型具有共同特征，包括名称、举行活动的地点以及开始和结束日期。这些类型也存在差异；`Conference` 提供演讲者和研讨会列表，而 `Concert` 包含表演乐队。

在架构定义语言 (SDL) 中，`Event` 接口定义如下所示：

```
interface Event {
        id: ID!
        name : String!
        startsAt: String
        endsAt: String
        venue: Venue
        minAgeRestriction: Int
}
```

每个类型都会实施 `Event` 接口，如下所示：

```
type Concert implements Event {
    id: ID!
    name: String!
    startsAt: String
    endsAt: String
    venue: Venue
    minAgeRestriction: Int
    performingBand: String
}

type Festival implements Event {
    id: ID!
    name: String!
    startsAt: String
    endsAt: String
    venue: Venue
    minAgeRestriction: Int
    performers: [String]
}

type Conference implements Event {
    id: ID!
    name: String!
    startsAt: String
    endsAt: String
    venue: Venue
    minAgeRestriction: Int
    speakers: [String]
    workshops: [String]
}
```

如果要表示多种类型的元素，接口就很有用。例如，我们可以搜索特定地点发生的所有事件。让我们在架构中添加 `findEventsByVenue` 字段，如下所示：

```
schema {
    query: Query
}

type Query {
    # Retrieve Events at a specific Venue
    findEventsAtVenue(venueId: ID!): [Event]
}

type Venue {
    id: ID!
    name: String
    address: String
    maxOccupancy: Int
}

type Concert implements Event {
    id: ID!
    name: String!
    startsAt: String
    endsAt: String
    venue: Venue
    minAgeRestriction: Int
    performingBand: String
}

interface Event {
    id: ID!
    name: String!
    startsAt: String
    endsAt: String
    venue: Venue
    minAgeRestriction: Int
}

type Festival implements Event {
    id: ID!
    name: String!
    startsAt: String
    endsAt: String
    venue: Venue
    minAgeRestriction: Int
    performers: [String]
}

type Conference implements Event {
    id: ID!
    name: String!
    startsAt: String
    endsAt: String
    venue: Venue
    minAgeRestriction: Int
    speakers: [String]
    workshops: [String]
}
```

`findEventsByVenue` 返回 `Event` 列表。由于 GraphQL 接口字段对于所有实施类型都是通用的，所以可以选择 `Event` 接口的任何字段（`id`、`name`、`startsAt`、`endsAt`、`venue` 和 `minAgeRestriction`）。此外，只要您指定了类型，就可以使用 GraphQL [片段](https://graphql.org/learn/queries/#fragments)访问任何实施类型的字段。

让我们看一下使用该接口的 GraphQL 查询示例。

```
query {
  findEventsAtVenue(venueId: "Madison Square Garden") {
    id
    name
    minAgeRestriction
    startsAt

    ... on Festival {
      performers
    }

    ... on Concert {
      performingBand
    }

    ... on Conference {
      speakers
      workshops
    }
  }
}
```

上一查询生成一个结果列表，默认情况下，服务器会根据开始日期将事件排序。

```
{
  "data": {
    "findEventsAtVenue": [
      {
        "id": "Festival-2",
        "name": "Festival 2",
        "minAgeRestriction": 21,
        "startsAt": "2018-10-05T14:48:00.000Z",
        "performers": [
          "The Singers",
          "The Screamers"
        ]
      },
      {
        "id": "Concert-3",
        "name": "Concert 3",
        "minAgeRestriction": 18,
        "startsAt": "2018-10-07T14:48:00.000Z",
        "performingBand": "The Jumpers"
      },
      {
        "id": "Conference-4",
        "name": "Conference 4",
        "minAgeRestriction": null,
        "startsAt": "2018-10-09T14:48:00.000Z",
        "speakers": [
          "The Storytellers"
        ],
        "workshops": [
          "Writing",
          "Reading"
        ]
      }
    ]
  }
}
```

由于结果是作为单个事件集合返回的，因此，使用接口表示相同特性对结果排序非常有帮助。

## 联合示例
<a name="unions"></a>

正如前面所述，联合不定义相同的字段集。搜索结果可能表示很多不同的类型。使用 `Event` 架构，您可以定义一个 `SearchResult` 联合，如下所示：

```
type Query {
    # Retrieve Events at a specific Venue
    findEventsAtVenue(venueId: ID!): [Event]
    # Search across all content
    search(query: String!): [SearchResult]
}

union SearchResult = Conference | Festival | Concert | Venue
```

此处，要查询 `SearchResult` 联合上的任何字段，您必须使用片段：

```
query {
  search(query: "Madison") {
    ... on Venue {
      id
      name
      address
    }

    ... on Festival {
      id
      name
      performers
    }

    ... on Concert {
      id
      name
      performingBand
    }

    ... on Conference {
      speakers
      workshops
    }
  }
}
```

## 在中键入分辨率 AWS AppSync
<a name="type-resolution-in-appsynclong"></a>

类型解析是一种机制，GraphQL 引擎通过这一机制将解析后的值识别为一种特定的对象类型。

回到联合搜索示例，假设我们的查询生成结果，结果列表中的每个项目必须将自身表示为 `SearchResult` 联合定义的可能类型之一（即，`Conference`、`Festival`、`Concert` 或 `Venue`）。

由于根据 `Venue` 或 `Conference` 识别 `Festival` 的逻辑取决于应用程序要求，必须给 GraphQL 引擎一点提示，这样它才能从原始结果中识别可能的类型。

使用 AWS AppSync，此提示由名为的元字段表示`__typename`，其值对应于已识别的对象类型名称。 `__typename`对于接口或联合返回类型是必需的。

## 类型解析示例
<a name="type-resolution-example"></a>

让我们重复使用之前的架构。如果您要跟随操作，可以导航到控制台，并在 **Schema (架构)** 页面之下添加以下内容：

```
schema {
    query: Query
}

type Query {
    # Retrieve Events at a specific Venue
    findEventsAtVenue(venueId: ID!): [Event]
    # Search across all content
    search(query: String!): [SearchResult]
}

union SearchResult = Conference | Festival | Concert | Venue

type Venue {
    id: ID!
    name: String!
    address: String
    maxOccupancy: Int
}

interface Event {
    id: ID!
    name: String!
    startsAt: String
    endsAt: String
    venue: Venue
    minAgeRestriction: Int
}

type Festival implements Event {
    id: ID!
    name: String!
    startsAt: String
    endsAt: String
    venue: Venue
    minAgeRestriction: Int
    performers: [String]
}

type Conference implements Event {
    id: ID!
    name: String!
    startsAt: String
    endsAt: String
    venue: Venue
    minAgeRestriction: Int
    speakers: [String]
    workshops: [String]
}

type Concert implements Event {
    id: ID!
    name: String!
    startsAt: String
    endsAt: String
    venue: Venue
    minAgeRestriction: Int
    performingBand: String
}
```

让我们为 `Query.search` 字段附加解析器。在该`Resolvers`部分中，选择**附加**，创建一个类型*为 NONE* 的新**数据源**，然后将其命名*StubDataSource*。在此示例中，我们假设已从外部源提取了结果，并将提取的结果硬编码到请求映射模板中。

在请求映射模板窗格中，输入以下内容：

```
{
    "version" : "2018-05-29",
    "payload":
    ## We are effectively mocking our search results for this example
    [
        {
            "id": "Venue-1",
            "name": "Venue 1",
            "address": "2121 7th Ave, Seattle, WA 98121",
            "maxOccupancy": 1000
        },
        {
            "id": "Festival-2",
            "name": "Festival 2",
            "performers": ["The Singers", "The Screamers"]
        },
        {
            "id": "Concert-3",
            "name": "Concert 3",
            "performingBand": "The Jumpers"
        },
        {
            "id": "Conference-4",
            "name": "Conference 4",
            "speakers": ["The Storytellers"],
            "workshops": ["Writing", "Reading"]
        }
    ]
}
```

如果应用程序返回类型名称以作为 `id` 字段的一部分，则类型解析逻辑必须解析 `id` 字段以提取类型名称，然后将 `__typename` 字段添加到每个结果中。您可以在响应映射模板中执行该逻辑，如下所示：

**注意**  
如果使用 Lambda 数据来源，您也可以将该任务作为 Lambda 函数的一部分执行。

```
#foreach ($result in $context.result)
    ## Extract type name from the id field.
    #set( $typeName = $result.id.split("-")[0] )
    #set( $ignore = $result.put("__typename", $typeName))
#end
$util.toJson($context.result)
```

运行以下查询：

```
query {
  search(query: "Madison") {
    ... on Venue {
      id
      name
      address
    }

    ... on Festival {
        id
      name
      performers
    }

    ... on Concert {
      id
      name
      performingBand
    }

    ... on Conference {
      speakers
      workshops
    }
  }
}
```

查询生成以下结果：

```
{
  "data": {
    "search": [
      {
        "id": "Venue-1",
        "name": "Venue 1",
        "address": "2121 7th Ave, Seattle, WA 98121"
      },
      {
        "id": "Festival-2",
        "name": "Festival 2",
        "performers": [
          "The Singers",
          "The Screamers"
        ]
      },
      {
        "id": "Concert-3",
        "name": "Concert 3",
        "performingBand": "The Jumpers"
      },
      {
        "speakers": [
          "The Storytellers"
        ],
        "workshops": [
          "Writing",
          "Reading"
        ]
      }
    ]
  }
}
```

类型解析逻辑会随应用程序而改变。例如，您可以使用另一种识别逻辑，检查某些字段或字段的组合是否存在。也就是说，您可以检测是否存在 `performers` 字段，以识别 `Festival`；或利用 `speakers` 和 `workshops` 字段的组合来识别 `Conference`。最终，由您定义要使用的逻辑。