

本文為英文版的機器翻譯版本，如內容有任何歧義或不一致之處，概以英文版為準。

# GraphQL API 的元件
<a name="api-components"></a>

標準 GraphQL API 由單一結構描述組成，可處理要查詢的資料形狀。您的結構描述會連結到一或多個資料來源，例如資料庫或 Lambda 函數。在兩者之間的 中，有一或多個解析程式可處理您請求的商業邏輯。每個元件在 GraphQL 實作中都扮演重要角色。以下各節將介紹這三個元件及其在 GraphQL 服務中扮演的角色。

![\[GraphQL API components: schema, resolvers, and data sources interconnected with AppSync.\]](http://docs.aws.amazon.com/zh_tw/appsync/latest/devguide/images/appsync-architecture-graphql-api.png)


**Topics**
+ [GraphQL 結構描述](schema-components.md)
+ [資料來源](data-source-components.md)
+ [解析程式](resolver-components.md)

# GraphQL 結構描述
<a name="schema-components"></a>

GraphQL 結構描述是 GraphQL API 的基礎。它可做為定義資料形狀的藍圖。它也是用戶端和伺服器之間的合約，定義如何擷取和/或修改您的資料。

GraphQL 結構描述是以*結構描述定義語言* (SDL) 撰寫。SDL 由具有已建立結構的類型和欄位組成：
+ **類型**：類型是 GraphQL 如何定義資料的形狀和行為。GraphQL 支援多種類型，將在本節稍後說明。結構描述中定義的每個類型都會包含自己的範圍。在範圍內，一個或多個欄位可以包含 GraphQL 服務中使用的值或邏輯。類型會填入許多不同的角色，最常見的是物件或純量 （基本值類型）。
+ **欄位**：欄位存在於 類型的範圍內，並保留從 GraphQL 服務請求的值。這些與其他程式設計語言的變數非常相似。您在欄位中定義的資料形狀將決定資料在請求/回應操作中的結構。這可讓開發人員預測傳回的內容，而不知道服務的後端如何實作。

若要視覺化結構描述的外觀，讓我們檢閱簡單 GraphQL 結構描述的內容。在生產程式碼中，您的結構描述通常位於名為 `schema.graphql`或 的檔案中`schema.json`。假設我們正在開發實作 GraphQL 服務的專案。此專案會儲存公司人員資料，而 `schema.graphql` 檔案會用來擷取人員資料，並將新人員新增至資料庫。程式碼可能如下所示：

------
#### [ schema.graphql ]

```
type Person {                                  
   id: ID!
   name: String                                  
   age: Int
}
type Query {                                   
  people: [Person]
}
type Mutation {
  addPerson(id: ID!, name: String, age: Int): Person
}
```

------

我們可以看到結構描述中定義了三種類型：`Person`、 `Query`和 `Mutation`。查看 `Person`，我們可以猜測這是公司員工執行個體的藍圖，這會讓此類型成為物件。在其範圍內，我們看到 `id`、 `name`和 `age`。這些是定義 之屬性的欄位`Person`。這表示我們的資料來源會將每個 `Person`的 `String` 儲存`name`為純量 （基本） 類型`age`和純量 `Int` （基本） 類型。`id` 可做為每個 的特殊唯一識別符`Person`。它也是以 `!`符號表示的必要值。

接下來的兩個物件類型行為不同。GraphQL 會為特殊物件類型保留一些關鍵字，以定義資料在結構描述中填入的方式。`Query` 類型會從來源擷取資料。在我們的範例中，我們的查詢可能會從資料庫擷取`Person`物件。這可能會提醒您 RESTful 術語中的`GET`操作。`Mutation` 將修改資料。在我們的範例中，我們的變動可能會將更多`Person`物件新增至資料庫。這可能會提醒您變更狀態的操作，例如 `PUT`或 `POST`。本節稍後將說明所有特殊物件類型的行為。

假設範例中`Query`的 會從資料庫擷取物件。如果我們查看 的欄位`Query`，就會看到一個名為 的欄位`people`。其欄位值為 `[Person]`。這表示我們希望擷取`Person`資料庫中的一些 執行個體。不過，新增括號表示我們想要傳回所有`Person`執行個體的清單，而不只是特定執行個體。

`Mutation` 類型負責執行變更狀態的操作，例如資料修改。變動負責對資料來源執行一些變更狀態的操作。在我們的範例中，我們的變動包含名為 的操作`addPerson`，將新`Person`物件新增至資料庫。變動會使用 `Person`，並預期輸入 `id`、 `name`和 `age` 欄位。

此時，您可能會想知道 操作的運作方式，`addPerson`如果沒有程式碼實作，因為它應該會執行一些行為，看起來很像具有函數名稱和參數的函數。目前，它無法運作，因為結構描述僅做為宣告。若要實作 的行為`addPerson`，我們必須新增解析程式。解析程式是程式碼的單位，每當呼叫其相關聯的欄位 （在此案例中為 `addPerson`操作） 時就會執行。如果您想要使用 操作，您必須在某個時間點新增解析程式實作。透過某種方式，您可以將結構描述操作視為函數宣告，並將解析程式視為定義。解析程式將在不同的章節中說明。

此範例僅顯示結構描述操作資料的最簡單方式。您可以利用 GraphQL 和 的功能來建置複雜、強大且可擴展的應用程式 AWS AppSync。在下一節中，我們將定義您可以在結構描述中使用的所有不同類型和欄位行為。

# GraphQL 類型
<a name="graphql-types"></a>

GraphQL 支援許多不同的類型。如上一節所示，類型會定義資料的形狀或行為。它們是 GraphQL 結構描述的基本建置區塊。

類型可以分類為輸入和輸出。輸入是允許做為特殊物件類型 (`Query`、 等） 的引數傳入的類型`Mutation`，而輸出類型會嚴格用於存放和傳回資料。類型及其分類的清單如下所示：
+ **物件**：物件包含描述實體的欄位。例如，物件可以是類似 的物件，`book`其中包含描述其特性的欄位`publishingYear`，例如 `authorName`、 等。它們是嚴格輸出類型。
+ **純量**：這些是基本類型，例如 int、string 等。它們通常會指派給欄位。使用 `authorName` 欄位做為範例，可以指派`String`純量來存放名稱，例如 "John Smith"。純量可以是輸入和輸出類型。
+ **輸入**：輸入可讓您將一組欄位做為引數傳遞。它們的結構與物件非常類似，但可以做為引數傳遞給特殊物件。輸入可讓您定義其範圍內的純量、列舉和其他輸入。輸入只能是輸入類型。
+ **特殊物件**：特殊物件會執行變更狀態的操作，並執行大量繁重的服務。有三種特殊物件類型：查詢、變動和訂閱。查詢通常會擷取資料；變動操作資料；訂閱會開啟和維護用戶端和伺服器之間的雙向連線，以進行持續通訊。特殊物件的功能不是輸入或輸出。
+ **列舉**：列舉是預先定義的法律值清單。如果您呼叫列舉，其值只能是其範圍中定義的值。例如，如果您有一個名為 的列舉，`trafficLights`描述流量訊號清單，它可以具有 `redLight`和 等值，`greenLight`但不能有 等值`purpleLight`。真正的交通燈只會有這麼多訊號，因此您可以使用列舉來定義它們，並在參考 時強制它們成為唯一的法律值`trafficLight`。列舉可以是輸入和輸出類型。
+ **聯集/介面**：聯集可讓您根據用戶端請求的資料，在請求中傳回一或多個物件。例如，如果您的`Book`類型具有 `title` 欄位，`Author`而類型具有 `name` 欄位，則可以在這兩種類型之間建立聯集。如果您的用戶端想要查詢資料庫以取得片語「Julius Caesar」，則聯集可以從 傳回 *Julius Caesar* (William Shakespeare 的播放），`Book``title`並從 傳回 *Julius Caesar* `Author` ( *Commentarii de Bello Gallico* 的作者）`name`。聯集只能是輸出類型。

  介面是 物件必須實作的一組欄位。這與 Java 等程式設計語言的界面略有相似，您必須在其中實作界面中定義的欄位。例如，假設您建立了名為 的界面`Book`，其中包含 `title` 欄位。假設您稍後建立了名為 的類型`Novel`，該類型已實作 `Book`。您的 `Novel`必須包含 `title` 欄位。不過，您的 `Novel` 也可以包含不在 介面中的其他欄位，例如 `pageCount`或 `ISBN`。介面只能是輸出類型。

以下各節將說明每種類型如何在 GraphQL 中運作。

## 物件
<a name="object-components"></a>

GraphQL 物件是您將在生產程式碼中看到的主要類型。在 GraphQL 中，您可以將物件視為不同欄位的群組 （類似於其他語言的變數），每個欄位都由可保留值的類型 （通常是純量或其他物件） 定義。物件代表可從您的服務實作擷取/控制的資料單位。

使用 `Type`關鍵字宣告物件類型。讓我們稍微修改結構描述範例：

```
type Person {
  id: ID!
  name: String
  age: Int
  occupation: Occupation
}

type Occupation {
  title: String
}
```

此處的物件類型為 `Person`和 `Occupation`。每個物件都有自己的欄位和自己的類型。GraphQL 的一項功能是能夠將欄位設定為其他類型。您可以在 中看到 `occupation` 欄位`Person`包含 `Occupation` 物件類型。我們可以建立此關聯，因為 GraphQL 只描述資料，而不是服務的實作。

## 純量
<a name="scalar-components"></a>

純量基本上是保留值的基本類型。在 中 AWS AppSync，純量有兩種類型：預設 GraphQL 純量和 AWS AppSync 純量。純量通常用於在物件類型中存放欄位值。預設 GraphQL 類型包括 `Int`、`Float`、`Boolean`、 `String`和 `ID`。讓我們再次使用上一個範例：

```
type Person { 
  id: ID!
  name: String
  age: Int
  occupation: Occupation
}

type Occupation {
  title: String
}
```

轉出 `name`和 `title` 欄位，兩者都保留純量。 `String` `Name`可能會傳回字串值，例如 "`John Smith`"，而標題可能會傳回類似 "`firefighter`" 的值。有些 GraphQL 實作也支援使用 `Scalar`關鍵字的自訂純量，並實作類型的行為。不過， AWS AppSync 目前**不支援**自訂純量。如需純量清單，請參閱 [中的純量類型 AWS AppSync](https://docs.aws.amazon.com//appsync/latest/devguide/scalars.html)。

## 輸入
<a name="input-components"></a>

由於輸入和輸出類型的概念，傳入引數時會有一些限制。通常需要傳入的類型會受到限制，特別是物件。您可以使用輸入類型來略過此規則。輸入是包含純量、列舉和其他輸入類型的類型。

輸入是使用`input`關鍵字定義：

```
type Person { 
  id: ID!
  name: String
  age: Int
  occupation: Occupation
}

type Occupation {
  title: String
}

input personInput { 
  id: ID!
  name: String
  age: Int
  occupation: occupationInput
}

input occupationInput {
  title: String
}
```

如您所見，我們可以有模擬原始類型的個別輸入。這些輸入通常會用在您的欄位操作中，如下所示：

```
type Person { 
  id: ID!
  name: String
  age: Int
  occupation: Occupation
}

type Occupation {
  title: String
}

input occupationInput {
  title: String
}

type Mutation {
  addPerson(id: ID!, name: String, age: Int, occupation: occupationInput): Person
}
```

請注意，我們仍以 `occupationInput` 代替 `Occupation`來建立 `Person`。

這只是輸入的一個案例。它們不一定需要 1：1 複製物件，而且在生產程式碼中，您很可能不會像這樣使用物件。利用 GraphQL 結構描述，最好只定義您需要輸入的內容做為引數。

此外，相同的輸入可用於多個操作，但我們不建議這樣做。每個操作理想情況下都應包含自己的唯一輸入複本，以防結構描述的需求變更。

## 特殊物件
<a name="special-object-components"></a>

GraphQL 會為特殊物件保留一些關鍵字，以定義結構描述如何擷取/處理資料的一些商業邏輯。結構描述中最多可以有其中一個關鍵字。它們可做為用戶端針對 GraphQL 服務執行之所有請求資料的進入點。

也會使用 `type`關鍵字定義特殊物件。雖然它們的使用方式與一般物件類型不同，但其實作非常類似。

------
#### [ Queries ]

查詢與 中的`GET`操作非常類似，因為它們執行唯讀擷取以從您的來源取得資料。在 GraphQL 中， `Query`會定義針對伺服器提出請求之用戶端的所有進入點。`Query` 您的 GraphQL 實作中一律會有 。

以下是我們在先前結構描述範例中使用的 `Query`和 修改過的物件類型：

```
type Person { 
  id: ID!
  name: String
  age: Int
  occupation: Occupation
}
type Occupation {
  title: String
}
type Query {                                   
  people: [Person]
}
```

我們的 `Query`包含一個名為 的欄位`people`，從資料來源傳回`Person`執行個體清單。假設我們需要變更應用程式的行為，現在我們只需要針對個別用途傳回`Occupation`執行個體的清單。我們可以直接將其新增至查詢：

```
type Query {                                   
  people: [Person]
  occupations: [Occupation]
}
```

在 GraphQL 中，我們可以將查詢視為請求的單一來源。如您所見，這可能會比 RESTful 實作更簡單，這些實作可能會使用不同的端點來達成相同的物件 (`.../api/1/people` 和 `.../api/1/occupations`)。

假設我們有此查詢的解析程式實作，我們現在可以執行實際的查詢。當 `Query`類型存在時，我們必須明確呼叫它，才能在應用程式的程式碼中執行。您可以使用 `query`關鍵字來完成此操作：

```
query getItems {
   people {
      name
   }
   occupations {
      title
   }
}
```

如您所見，此查詢稱為 `getItems`並傳回 `people`(`Person`物件清單） 和 `occupations`(`Occupation`物件清單）。在 中`people`，我們只會傳回每個 `name`的欄位`Person`，同時傳回每個 `title`的欄位`Occupation`。回應可能如下所示：

```
{
  "data": {
    "people": [
      {
        "name": "John Smith"
      },
      {
        "name": "Andrew Miller"
      },
      .
      .
      .
    ],
    "occupations": [
      {
        "title": "Firefighter"
      },
      {
        "title": "Bookkeeper"
      },
      .
      .
      .
    ]
  }
}
```

範例回應顯示資料如何遵循查詢的形狀。擷取的每個項目都會列在 欄位的範圍內。 `people`和 `occupations`會以個別的清單傳回物件。雖然有用，但修改查詢以傳回人員名稱和職業清單可能更為方便：

```
query getItems {
   people {
      name   
      occupation {
        title
      }
}
```

這是合法修改，因為我們的`Person`類型包含 類型的`occupation`欄位`Occupation`。在 範圍內列出時`people`，我們會傳回每個 `Person`的 ，`name`以及其`Occupation`與 相關聯的 `title`。回應可能如下所示：

```
}
  "data": {
    "people": [
      {
        "name": "John Smith",
        "occupation": {
          "title": "Firefighter"
        }
      },
      {
        "name": "Andrew Miller",
        "occupation": {
          "title": "Bookkeeper"
        }
      },
      .
      .
      .
    ]
  }
}
```

------
#### [ Mutations ]

變動類似於狀態變更的操作，例如 `PUT`或 `POST`。他們會執行寫入操作來修改來源中的資料，然後擷取回應。它們會定義資料修改請求的進入點。與查詢不同，根據專案的需求，變動可能會也可能不會包含在結構描述中。以下是結構描述範例中的變動：

```
type Mutation {
  addPerson(id: ID!, name: String, age: Int): Person
}
```

`addPerson` 欄位代表一個`Person`將 新增至資料來源的進入點。 `addPerson`是欄位名稱；`id`、 `name`和 `age`是參數； `Person`是傳回類型。回顧 `Person`類型：

```
type Person { 
  id: ID!
  name: String
  age: Int
  occupation: Occupation
}
```

我們已新增 `occupation` 欄位。不過，我們無法將此欄位`Occupation`直接設定為 ，因為物件無法做為引數傳入；它們是嚴格輸出類型。我們應該改為傳遞具有與 引數相同欄位的輸入：

```
input occupationInput {
  title: String
}
```

 我們也可以在建立新`Person`執行個體時，輕鬆更新我們的 `addPerson` 以包含此參數：

```
type Mutation {
  addPerson(id: ID!, name: String, age: Int, occupation: occupationInput): Person
}
```

以下是更新的結構描述：

```
type Person { 
  id: ID!
  name: String
  age: Int
  occupation: Occupation
}

type Occupation {
  title: String
}

input occupationInput {
  title: String
}

type Mutation {
  addPerson(id: ID!, name: String, age: Int, occupation: occupationInput): Person
}
```

請注意， `occupation`將從 傳入 `title` 欄位`occupationInput`，以完成 的建立，`Person`而不是原始`Occupation`物件。假設我們有 的解析程式實作`addPerson`，現在可以執行實際的變動。當 `Mutation`類型存在時，我們必須明確呼叫它，才能在應用程式的程式碼中執行。您可以使用 `mutation`關鍵字來完成此操作：

```
mutation createPerson {
  addPerson(id: ID!, name: String, age: Int, occupation: occupationInput) {
    name
    age
    occupation {
      title
    }
  }
}
```

此變動稱為 `createPerson`，而 `addPerson`是 操作。若要建立新的 `Person`，我們可以輸入 `id`、`age`、 `name`和 的引數`occupation`。在 範圍內`addPerson`，我們也可以看到其他欄位`age`，例如 `name`、 等。這是您的回應；這些是`addPerson`操作完成後將傳回的欄位。以下是範例的最後部分：

```
mutation createPerson {
  addPerson(id: "1", name: "Steve Powers", age: "50", occupation: "Miner") {
    id
    name
    age
    occupation {
      title
    }
  }
}
```

使用此變動，結果可能如下所示：

```
{
  "data": {
    "addPerson": {
      "id": "1",
      "name": "Steve Powers",
      "age": "50",
      "occupation": {
        "title": "Miner"
      }
    }
  }
}
```

如您所見，回應會以我們變動中定義的相同格式傳回我們請求的值。最佳實務是傳回所有修改過的值，以減少混淆，以及未來需要更多查詢。變動可讓您在其範圍內包含多個操作。它們將依變動中列出的順序依序執行。例如，如果我們建立另一個名為 的操作`addOccupation`，將任務標題新增至資料來源，我們可以在 之後的變動中呼叫此 `addPerson`。 `addPerson`會先處理，再處理 `addOccupation`。

------
#### [ Subscriptions ]

訂閱使用 [WebSockets](https://developer.mozilla.org/en-US/docs/Web/API/WebSockets_API/Writing_WebSocket_client_applications) 在伺服器及其用戶端之間開啟持久的雙向連線。一般而言，用戶端會訂閱或接聽伺服器。只要伺服器進行伺服器端變更或執行事件，訂閱的用戶端就會收到更新。當訂閱多個用戶端，且需要通知伺服器或其他用戶端發生變更時，此類型的通訊協定非常有用。例如，訂閱可用來更新社交媒體摘要。使用者 A 和使用者 B 可能有兩個使用者，每當他們收到直接訊息時，都會訂閱自動通知更新。用戶端 A 上的使用者 A 可以傳送直接訊息給用戶端 B 上的使用者 B。使用者 A 的用戶端會傳送直接訊息，由伺服器處理。然後，伺服器會在傳送自動通知至用戶端 B 時，將直接訊息傳送至使用者 B 的帳戶。

以下是`Subscription`我們可以新增至結構描述範例的 範例：

```
type Subscription {                                   
  personAdded: Person
}
```

只要將新的 `Person` 新增至資料來源， `personAdded` 欄位就會傳送訊息給訂閱的用戶端。假設我們有 的解析程式實作`personAdded`，現在可以使用訂閱。當 `Subscription`類型存在時，我們必須明確呼叫它，才能在應用程式的程式碼中執行。您可以使用 `subscription`關鍵字來完成此操作：

```
subscription personAddedOperation {
  personAdded {
    id
    name
  }
}
```

訂閱稱為 `personAddedOperation`，操作為 `personAdded`。 `personAdded`會傳回新`Person`執行個體的 `id`和 `name` 欄位。查看變動範例，我們使用`Person`此操作新增 ：

```
addPerson(id: "1", name: "Steve Powers", age: "50", occupation: "Miner")
```

如果用戶端已訂閱新新增 的更新`Person`，則在`addPerson`執行後可能會看到：

```
{
  "data": {
    "personAdded": {
      "id": "1",
      "name": "Steve Powers"
    }
  }
}
```

以下是訂閱提供的摘要：

訂閱是雙向通道，可讓用戶端和伺服器接收快速但穩定的更新。他們通常會使用 WebSocket 通訊協定，這會建立標準化且安全的連線。

訂閱可靈活運作，減少連線設定開銷。一旦訂閱，用戶端就可以在訂閱上長時間繼續執行。他們通常會允許開發人員量身打造訂閱的生命週期，並設定將請求哪些資訊，藉此有效率地使用運算資源。

一般而言，訂閱可讓用戶端一次進行多個訂閱。由於與 相關 AWS AppSync，訂閱僅用於從 AWS AppSync 服務接收即時更新。它們無法用於執行查詢或變動。

訂閱的主要替代方案是輪詢，它會以設定的間隔傳送查詢以請求資料。此程序通常比訂閱效率低，並對用戶端和後端造成很大壓力。

------

我們的結構描述範例中未提及的一件事，就是您的特殊物件類型也必須在`schema`根中定義。因此，當您在 中匯出結構描述時 AWS AppSync，可能如下所示：

------
#### [ schema.graphql ]

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

.
.
.

type Query {                                   
  # code goes here
}
type Mutation {                                   
  # code goes here
}
type Subscription {                                   
  # code goes here
}
```

------

## 列舉
<a name="enum-components"></a>

列舉或列舉是限制類型或欄位可能具有之法律引數的特殊純量。這表示每當列舉在結構描述中定義時，其關聯的類型或欄位將僅限於列舉中的值。列舉會序列化為字串純量。請注意，不同的程式設計語言可能會以不同的方式處理 GraphQL 列舉。例如，JavaScript 不支援原生列舉，因此列舉值可以改為映射至整數值。

列舉是使用 `enum`關鍵字來定義。範例如下：

```
enum trafficSignals {
  solidRed
  solidYellow
  solidGreen
  greenArrowLeft
  ...
}
```

呼叫`trafficLights`列舉時，引數只能是 `solidRed`、`solidYellow`、 `solidGreen`（等）。使用列舉來描述具有不同但有限選擇數量的物件是很常見的。

## 聯集/界面
<a name="union-interface-components"></a>

請參閱 [GraphQL 中的界面和聯集](https://docs.aws.amazon.com/appsync/latest/devguide/interfaces-and-unions.html)。 GraphQL

# GraphQL 欄位
<a name="graphql-fields"></a>

欄位存在於 類型的範圍內，並保留從 GraphQL 服務請求的值。這些與其他程式設計語言的變數非常相似。例如，以下是`Person`物件類型：

```
type Person {                                  
   name: String                                  
   age: Int
}
```

在此情況下，欄位分別為 `name`和 `age` 並保留 `String`和 `Int`值。像上述所示的物件欄位可以用作查詢和變動之欄位 （操作） 中的輸入。例如，請參閱`Query`下列內容：

```
type Query {                                   
  people: [Person]
}
```

`people` 欄位正在`Person`從資料來源請求 的所有執行個體。當您在 GraphQL 伺服器`Person`中新增或擷取 時，您可以預期資料會遵循您類型和欄位的格式，也就是說，結構描述中資料的結構會決定如何在回應中建構資料：

```
}
  "data": {
    "people": [
      {
        "name": "John Smith",
        "age": "50"
      },
      {
        "name": "Andrew Miller",
        "age": "60"
      },
      .
      .
      .
    ]
  }
}
```

欄位在建構資料中扮演重要角色。下面說明了幾個額外的屬性，可以套用到欄位以進行更多自訂。

## 清單
<a name="list-components"></a>

清單會傳回指定類型的所有項目。您可以使用括號 將清單新增至欄位的類型`[]`：

```
type Person { 
  name: String
  age: Int
}
type Query {                                   
  people: [Person]
}
```

在 中`Query`，週圍括號`Person`表示您想要`Person`從資料來源傳回 的所有執行個體做為陣列。在回應中，每個 的 `name`和 `age`值`Person`會以單一的分隔清單傳回：

```
}
  "data": {
    "people": [
      {
        "name": "John Smith",         # Data of Person 1
        "age": "50"
      },
      {
        "name": "Andrew Miller",      # Data of Person 2
        "age": "60"
      },
      .                               # Data of Person N
      .
      .
    ]
  }
}
```

您不限於特殊物件類型。您也可以在一般物件類型的欄位中使用清單。

## 非 Null
<a name="non-null-components"></a>

非 Null 表示回應中不可為 Null 的欄位。您可以使用 `!`符號將欄位設定為非空值：

```
type Person { 
  name: String!
  age: Int
}
type Query {                                   
  people: [Person]
}
```

`name` 欄位不能明確為 null。如果您要查詢資料來源，並為此欄位提供 null 輸入，則會擲回錯誤。

您可以結合清單和非 Null。比較這些查詢：

```
type Query {                                   
  people: [Person!]      # Use case 1
}

.
.
.

type Query {                                   
  people: [Person]!      # Use case 2
}

.
.
.

type Query {                                   
  people: [Person!]!     # Use case 3
}
```

在使用案例 1 中，清單不能包含 null 項目。在使用案例 2 中，清單本身無法設定為 null。在使用案例 3 中，清單及其項目不能為 null。不過，無論如何，您仍然可以傳回空白清單。

如您所見，GraphQL 中有許多移動元件。在本節中，我們展示了簡單結構描述的結構，以及結構描述支援的不同類型和欄位。在下一節中，您將探索 GraphQL API 的其他元件，以及它們如何使用結構描述。

# 資料來源
<a name="data-source-components"></a>

在上一節中，我們了解到結構描述會定義資料的形狀。不過，我們從未說明該資料的來源。在實際專案中，您的結構描述就像處理對伺服器提出之所有請求的閘道。提出請求時，結構描述會做為與用戶端互動的單一端點。結構描述將存取、處理資料，並將資料從資料來源轉送回用戶端。請參閱下列資訊圖表：

![\[GraphQL schema integrating multiple AWS 服務 for a single endpoint API architecture.\]](http://docs.aws.amazon.com/zh_tw/appsync/latest/devguide/images/aws-flow-infographic.png)


AWS AppSync 和 GraphQL 完美實作後端前端 (BFF) 解決方案。它們可串聯運作，透過抽象化後端來大規模降低複雜性。如果您的服務使用不同的資料來源和/或微服務，基本上您可以透過在單一結構描述 （超級圖） 中定義每個來源 （子圖） 的資料形狀，來抽象化一些複雜性。這表示您的 GraphQL API 不限於使用一個資料來源。您可以將任意數量的資料來源與 GraphQL API 建立關聯，並在程式碼中指定它們將如何與服務互動。

如您在資訊圖表中所見，GraphQL 結構描述包含用戶端請求資料所需的所有資訊。這表示一切都可以在單一請求中處理，而不是多個請求，就像 REST 的情況一樣。這些請求會經歷結構描述，這是服務的唯一端點。處理請求時，解析程式 （在下一節中說明） 會執行其程式碼來處理來自相關資料來源的資料。傳回回應時，與資料來源繫結的子圖形會填入結構描述中的資料。

AWS AppSync 支援許多不同的資料來源類型。在下表中，我們將說明每種類型、列出每種類型的一些優點，並提供實用連結以用於其他內容。


| 資料來源 | Description | 優勢 | 補充資訊 | 
| --- | --- | --- | --- | 
| Amazon DynamoDB | 「Amazon DynamoDB 是全受管的 NoSQL 資料庫服務，可提供快速且可預測的效能和無縫的可擴展性。DynamoDB 是全受管的 NoSQL 資料庫服務，可讓您卸下操作及擴展分散式資料庫的管理負擔，不再需要煩惱硬體佈建、設定和組態、複寫、軟體修補或叢集擴展。DynamoDB 也提供靜態加密，消除保護敏感資料所涉及的操作負擔和複雜性。」 |  [\[See the AWS documentation website for more details\]](http://docs.aws.amazon.com/zh_tw/appsync/latest/devguide/data-source-components.html)  |  [\[See the AWS documentation website for more details\]](http://docs.aws.amazon.com/zh_tw/appsync/latest/devguide/data-source-components.html)  | 
| AWS Lambda | 「AWS Lambda 是一種運算服務，可讓您執行程式碼，而無需佈建或管理伺服器。Lambda 在高可用性的運算基礎設施上執行您的程式碼，並執行所有運算資源的管理，包括伺服器與作業系統維護、容量佈建與自動擴展以及記錄。使用 Lambda，您只需要在 Lambda 支援的其中一種語言執行時間中提供程式碼。」 |  [\[See the AWS documentation website for more details\]](http://docs.aws.amazon.com/zh_tw/appsync/latest/devguide/data-source-components.html)  |  [\[See the AWS documentation website for more details\]](http://docs.aws.amazon.com/zh_tw/appsync/latest/devguide/data-source-components.html)  | 
| OpenSearch | 「Amazon OpenSearch Service 是一項受管服務，可讓您輕鬆地在 AWS 雲端中部署、操作和擴展 OpenSearch 叢集。Amazon OpenSearch Service 支援 OpenSearch 和舊版 Elasticsearch OSS （最高 7.10，軟體的最終開放原始碼版本）。在您建立叢集時，您可選擇要使用的搜尋引擎。**OpenSearch** 是一個完全開源的搜尋和分析引擎，適用於例如日誌分析、即時應用程式監控及點擊流分析等使用案例。如需詳細資訊，請參閱 [OpenSearch 文件](https://opensearch.org/docs/)。**Amazon OpenSearch Service** 會為您的 OpenSearch 叢集佈建所有資源並啟動它。它還會自動偵測和更換發生故障的 OpenSearch Service 節點，可降低與自我管理的基礎設施相關的營運成本。您可以使用單一 API 呼叫或在主控台中按幾下滑鼠來擴展叢集。」 |  [\[See the AWS documentation website for more details\]](http://docs.aws.amazon.com/zh_tw/appsync/latest/devguide/data-source-components.html)  |  [\[See the AWS documentation website for more details\]](http://docs.aws.amazon.com/zh_tw/appsync/latest/devguide/data-source-components.html)  | 
| HTTP 端點 | 您可以使用 HTTP 端點做為資料來源。 AWS AppSync 可以傳送請求到端點，其中包含參數和承載等相關資訊。HTTP 回應將公開給解析程式，它將在完成其操作後傳回最終回應 (s)。 |  [\[See the AWS documentation website for more details\]](http://docs.aws.amazon.com/zh_tw/appsync/latest/devguide/data-source-components.html)  |  [\[See the AWS documentation website for more details\]](http://docs.aws.amazon.com/zh_tw/appsync/latest/devguide/data-source-components.html)  | 
| Amazon EventBridge | 「EventBridge 是一種無伺服器服務，使用事件將應用程式元件連接在一起，讓您更輕鬆地建置可擴展的事件驅動型應用程式。使用它將事件從自製應用程式、 AWS 服務和第三方軟體等來源路由到整個組織的消費者應用程式。EventBridge 提供簡單且一致的方式來擷取、篩選、轉換和交付事件，讓您可以快速建立新的應用程式。」 |  [\[See the AWS documentation website for more details\]](http://docs.aws.amazon.com/zh_tw/appsync/latest/devguide/data-source-components.html)  |  [\[See the AWS documentation website for more details\]](http://docs.aws.amazon.com/zh_tw/appsync/latest/devguide/data-source-components.html)  | 
| 關聯式資料庫 | 「Amazon Relational Database Service (Amazon RDS) 是一種 Web 服務，可讓您更輕鬆地在 AWS 雲端中設定、操作和擴展關聯式資料庫。它為產業標準關聯式資料庫提供經濟實惠、可擴展的容量，並管理常見的資料庫管理任務。」 |  [\[See the AWS documentation website for more details\]](http://docs.aws.amazon.com/zh_tw/appsync/latest/devguide/data-source-components.html)  |  [\[See the AWS documentation website for more details\]](http://docs.aws.amazon.com/zh_tw/appsync/latest/devguide/data-source-components.html)  | 
| 無資料來源 | 如果您不打算使用資料來源服務，您可以將其設定為 none。雖然none資料來源仍明確分類為資料來源，但 不是儲存媒體。儘管如此，它在某些執行個體中仍然適用於資料處理和傳遞。 |  [\[See the AWS documentation website for more details\]](http://docs.aws.amazon.com/zh_tw/appsync/latest/devguide/data-source-components.html)  |  [\[See the AWS documentation website for more details\]](http://docs.aws.amazon.com/zh_tw/appsync/latest/devguide/data-source-components.html)  | 

**提示**  
如需資料來源如何與 互動的詳細資訊 AWS AppSync，請參閱[連接資料來源](https://docs.aws.amazon.com//appsync/latest/devguide/attaching-a-data-source.html)。

# 解析程式
<a name="resolver-components"></a>

從先前的章節中，您已了解結構描述和資料來源的元件。現在，我們需要解決結構描述和資料來源的互動方式。一切都從解析程式開始。

解析程式是程式碼單位，可處理在向服務提出請求時，該欄位的資料將如何解決。解析程式會連接到結構描述中類型內的特定欄位。它們最常用於實作查詢、變動和訂閱欄位操作的狀態變更操作。解析程式會處理用戶端的請求，然後傳回結果，可以是一組輸出類型，例如物件或純量：

![\[GraphQL schema with resolvers connecting to various AWS data sources for a single endpoint.\]](http://docs.aws.amazon.com/zh_tw/appsync/latest/devguide/images/aws-flow-infographic.png)


## 解析程式執行時間
<a name="resolver-components-runtime"></a>

在 中 AWS AppSync，您必須先指定解析程式的執行時間。解析程式執行時間表示執行解析程式的環境。它還指定解析程式將寫入的語言。 AWS AppSync 目前支援 APPSYNC\$1JS for JavaScript and Velocity Template Language (VTL)。請參閱[適用於 JavaScript 的解析程式和函數的 JavaScript 執行期功能](https://docs.aws.amazon.com/appsync/latest/devguide/resolver-util-reference-js.html)，或適用於 VTL 的[解析程式映射範本公用程式參考](https://docs.aws.amazon.com/appsync/latest/devguide/resolver-util-reference.html)。 JavaScript 

## 解析程式結構
<a name="resolver-components-structure"></a>

解析程式的程式碼化結構有兩種方式。有**單元**和**管道**解析程式。

### 單位解析程式
<a name="resolver-components-unit"></a>

單位解析程式由程式碼組成，可定義針對資料來源執行的單一請求和回應處理常式。請求處理常式會將內容物件做為引數，並傳回用來呼叫資料來源的請求承載。回應處理常式會從資料來源接收承載，其中包含執行請求的結果。回應處理常式會將承載轉換為 GraphQL 回應，以解析 GraphQL 欄位。

![\[GraphQL request flow showing request and response handlers interacting with a data source.\]](http://docs.aws.amazon.com/zh_tw/appsync/latest/devguide/images/unit-resolver-js.png)


### 管道解析程式
<a name="resolver-components-pipeline"></a>

實作管道解析程式時，會遵循一般結構：
+ 在**步驟之前**：當用戶端提出請求時，所使用結構描述欄位的解析程式 （通常是您的查詢、變動、訂閱） 會傳遞請求資料。解析程式會開始在步驟處理常式之前處理請求資料，這允許在資料通過解析程式之前執行一些預先處理操作。
+ **Function(s)**：在步驟執行之前，請求會傳遞至函數清單。清單中的第一個函數會針對資料來源執行。函數是解析程式程式碼的子集，其中包含自己的請求和回應處理常式。請求處理常式會取得請求資料，並對資料來源執行操作。回應處理常式會先處理資料來源的回應，再將其傳回清單。如果有多個函數，則請求資料會傳送至清單中要執行的下一個函數。清單中的函數將按照開發人員定義的順序序列執行。執行所有函數後，最終結果會在步驟後傳遞至 。
+ **後步驟**：後步驟是處理常式函數，可讓您在最終函數的回應上執行一些最終操作，然後再將其傳遞至 GraphQL 回應。

![\[GraphQL request flow diagram showing interactions between request, data sources, and response components.\]](http://docs.aws.amazon.com/zh_tw/appsync/latest/devguide/images/appsync-js-resolver-logic.png)


## Resolver 處理常式結構
<a name="resolver-components-handlers"></a>

處理常式通常是稱為 `Request`和 的函數`Response`：

```
export function request(ctx) {
    // Code goes here
}

export function response(ctx) {
    // Code goes here
}
```

在單位解析程式中，只有一組這些函數。在管道解析程式中，在步驟前後會有一組適用於 的集合，以及每個函數的額外集合。為了以視覺化方式呈現，讓我們檢閱簡單的`Query`類型：

```
type Query {
	helloWorld: String!
}
```

這是簡單的查詢，有一個欄位稱為`helloWorld`類型 `String`。假設我們一律希望此欄位傳回字串 "Hello World"。若要實作此行為，我們需要將解析程式新增至此欄位。在單位解析程式中，我們可以新增如下內容：

```
export function request(ctx) {
    return {}
}

export function response(ctx) {
    return "Hello World"
}
```

`request` 只能保留空白，因為我們並未請求或處理資料。我們也可以假設資料來源為 `None`，表示此程式碼不需要執行任何調用。回應只會傳回「Hello World」。若要測試此解析程式，我們需要使用查詢類型提出請求：

```
query helloWorldTest {
  helloWorld
}
```

這是名為 的查詢`helloWorldTest`，會傳回 `helloWorld` 欄位。執行時，`helloWorld`欄位解析程式也會執行並傳回回應：

```
{
  "data": {
    "helloWorld": "Hello World"
  }
}
```

傳回像這樣的常數是最簡單的方法。實際上，您將傳回輸入、清單等。以下是更複雜的範例：

```
type Book {
  id: ID!
  title: String
}

type Query {
  getBooks: [Book]
}
```

在這裡，我們會傳回 的清單`Books`。假設我們使用 DynamoDB 資料表來存放書籍資料。我們的處理常式可能如下所示：

```
/**
 * Performs a scan on the dynamodb data source
 */
export function request(ctx) {
  return { operation: 'Scan' };
}

/**
 * return a list of scanned post items
 */
export function response(ctx) {
  return ctx.result.items;
}
```

我們的請求使用內建掃描操作來搜尋資料表中的所有項目、將調查結果存放在內容中，然後將其傳遞給回應。回應接受了結果項目，並在回應中傳回它們：

```
{
  "data": {
    "getBooks": {
      "items": [
        {
          "id": "abcdefgh-1234-1234-1234-abcdefghijkl",
          "title": "book1"
        },
        {
          "id": "aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee",
          "title": "book2"
        },

        ...

      ]
    }
  }
}
```

## 解析程式內容
<a name="resolver-components-context"></a>

在解析程式中，處理常式鏈中的每個步驟都必須知道先前步驟的資料狀態。一個處理常式的結果可以儲存並傳遞給另一個處理常式做為引數。GraphQL 定義四個基本解析程式引數：


****  

| 解析程式基本引數 | Description | 
| --- | --- | 
| obj、root、parent 等等 | 父系的結果。 | 
| args | 提供給 GraphQL 查詢中 欄位的引數。 | 
| context | 提供給每個解析程式並保留重要內容資訊的值，例如目前登入的使用者或資料庫的存取權。 | 
| info | 值，其中包含與目前查詢相關的欄位特定資訊，以及結構描述詳細資訊。 | 

在 中 AWS AppSync， `[context](https://docs.aws.amazon.com/appsync/latest/devguide/resolver-context-reference-js.html)` (ctx) 引數可以保留上述所有資料。這是每個請求建立的物件，包含授權登入資料、結果資料、錯誤、請求中繼資料等資料。內容是程式設計人員操作來自請求其他部分的資料的簡單方法。再次使用此程式碼片段：

```
/**
 * Performs a scan on the dynamodb data source
 */
export function request(ctx) {
  return { operation: 'Scan' };
}

/**
 * return a list of scanned post items
 */
export function response(ctx) {
  return ctx.result.items;
}
```

請求會指定內容 (ctx) 做為引數；這是請求的狀態。它會對資料表中的所有項目執行掃描，然後將結果存放在 的內容中`result`。接著內容會傳遞至回應引數，該引數會存取 `result`並傳回其內容。

## 請求和剖析
<a name="resolver-ast"></a>

當您查詢 GraphQL 服務時，必須先執行剖析和驗證程序，才能執行。您的請求將會剖析並翻譯為抽象語法樹。樹狀目錄的內容是透過針對結構描述執行多個驗證演算法來驗證。在驗證步驟之後，會周遊和處理樹狀結構的節點。系統會叫用解析程式、將結果儲存在內容中，並傳回回應。例如，採取以下查詢：

```
query {
  Person {  //object type
    name  //scalar
    age   //scalar
  } 
}
```

我們會`Person`傳回 `name`和 `age` 欄位。執行此查詢時，樹狀結構看起來會像這樣：

![\[Hierarchical diagram showing query, Person, name, and age nodes connected by arrows.\]](http://docs.aws.amazon.com/zh_tw/appsync/latest/devguide/images/ast-1.png)


從樹狀目錄中，此請求似乎會在結構描述`Query`中搜尋 的根目錄。在查詢內， `Person` 欄位將會解析。從先前的範例中，我們知道這可能是來自 使用者的輸入、值清單等。 `Person` 很可能與保存所需欄位 (`name` 和 `age`) 的物件類型相關聯。找到這兩個子欄位後，它們會依指定的順序 (`name` 後面接著 `age`) 解析。樹狀結構完全解決後，請求即完成，並將傳回用戶端。