

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

# GraphQL 和 AWS AppSync 架構
<a name="graphql-overview"></a>

**注意**  
本指南假設使用者具備 REST 架構樣式的工作知識。建議您在使用 GraphQL 和 之前，先檢閱此主題和其他前端主題 AWS AppSync。

GraphQL 是 APIs的查詢和處理語言。GraphQL 提供靈活且直覺的語法來描述資料需求和互動。它可讓開發人員詢問確切的需求，並取得可預測的結果。它也可以在單一請求中存取許多來源，減少網路呼叫次數和頻寬需求，從而節省應用程式的電池壽命和 CPU 週期。

資料更新會隨著變動而變得簡單，讓開發人員能夠描述資料應如何變更。GraphQL 也可透過訂閱快速設定即時解決方案。所有這些功能結合強大的開發人員工具，使得 GraphQL 對管理應用程式資料至關重要。

GraphQL 是 REST 的替代方案。RESTful 架構目前是用戶端與伺服器通訊最熱門的解決方案之一。它以 URL 公開的資源 （資料） 概念為中心。這些 URLs 可用於透過 CRUD （建立、讀取、更新、刪除） 操作存取和操作資料，其形式為 HTTP 方法`POST`，例如 `GET`、 和 `DELETE`。REST 的優點是學習和實作相對簡單。您可以快速設定 RESTful APIs來呼叫各種服務。

不過，技術變得越來越複雜。隨著應用程式、工具和服務開始為全球受眾擴展，對快速、可擴展的架構的需求至關重要。REST 在處理可擴展的操作時有許多缺點。如需範例，請參閱此[使用案例](https://aws.amazon.com/blogs/architecture/what-to-consider-when-modernizing-apis-with-graphql-on-aws/)。

在下列各節中，我們將檢閱有關 RESTful APIs的一些概念。然後，我們將介紹 GraphQL 及其運作方式。

如需 GraphQL 的詳細資訊以及遷移到 的好處 AWS，請參閱 [GraphQL 實作的決策指南](https://aws.amazon.com/graphql/guide/)。

**Topics**
+ [什麼是 API](what-is-an-api.md)
+ [什麼是 REST](what-is-rest.md)
+ [什麼是 GraphQL](what-is-graphql.md)
+ [比較 REST 和 GraphQL](comparing-rest-graphql.md)
+ [為什麼透過 REST 使用 GraphQL](why-use-graphql.md)
+ [GraphQL API 的元件](api-components.md)
+ [GraphQL 的其他屬性](graphql-properties.md)

# 什麼是 API？
<a name="what-is-an-api"></a>

應用程式程式設計界面 (API) 定義了您必須遵循的規則，以便與其他軟體系統通訊。開發人員公開或建立 APIs，以便其他應用程式可以透過程式設計方式與其應用程式通訊。例如，時間表應用程式會公開 API，詢問員工的全名和日期範圍。當它收到此資訊時，它會在內部處理員工的時間表，並傳回在該日期範圍內工作的時數。

您可以將 Web API 視為 Web 上用戶端和資源之間的閘道。

## 用戶端
<a name="what-is-a-client"></a>

用戶端是想要從 Web 存取資訊的使用者。用戶端可以是使用 API 的人員或軟體系統。例如，開發人員可以撰寫程式，從天氣系統存取天氣資料。或者，當您直接造訪天氣網站時，您可以從瀏覽器存取相同的資料。

## Resources
<a name="what-is-a-resource"></a>

資源是不同應用程式提供給其用戶端的資訊。資源可以是影像、影片、文字、數字或任何類型的資料。將資源提供給用戶端的機器也稱為 伺服器。Organizations 使用 APIs來共用資源並提供 Web 服務，同時維護安全性、控制和身分驗證。此外，APIs可協助他們判斷哪些用戶端可以存取特定的內部資源。

# 什麼是 REST？
<a name="what-is-rest"></a>

在高階，表示式狀態轉移 (REST) 是一種軟體架構，會針對 API 的運作方式施加條件。REST 最初是建立做為準則，以管理網際網路等複雜網路上的通訊。您可以使用以 REST 為基礎的架構，大規模支援高效能和可靠的通訊。您可以輕鬆實作和修改它，為任何 API 系統提供可見性和跨平台可攜性。

API 開發人員可以使用數種不同的架構來設計 APIs。遵循 REST 架構樣式的 APIs 稱為 REST APIs。實作 REST 架構的 Web 服務稱為 RESTful Web 服務。RESTful API 一詞通常是指 RESTful Web APIs。不過，您可以交替使用 REST API 和 RESTful API。

以下是 REST 架構樣式的一些原則：

## 統一界面
<a name="uniform-interface"></a>

統一界面是任何 RESTful Web 服務設計的基礎。它表示伺服器以標準格式傳輸資訊。格式化的資源稱為 REST 中的表示法。此格式可能與伺服器應用程式上資源的內部表示方式不同。例如，伺服器可以將資料儲存為文字，但以 HTML 表示格式傳送。

統一界面會施加四個架構限制：

1.  請求應識別資源。他們會使用統一的資源識別符來執行此操作。

1.  用戶端在資源表示法中有足夠的資訊，可視需要修改或刪除資源。透過傳送進一步描述資源的中繼資料，伺服器符合此條件。

1.  用戶端會收到如何進一步處理表示的資訊。伺服器透過傳送包含有關用戶端最佳使用方式中繼資料的自我描述訊息來實現此目標。

1.  用戶端會收到完成任務所需的所有其他相關資源的相關資訊。伺服器會透過在 表示法中傳送超連結來達成此目的，以便用戶端動態探索更多資源。

## 無狀態
<a name="statelessness"></a>

在 REST 架構中，無狀態是指伺服器完成每個用戶端請求的通訊方法，與先前所有請求無關。用戶端可以按任何順序請求資源，而且每個請求都是無狀態的，或與其他請求隔離。此 REST API 設計限制表示伺服器每次都能完全了解並滿足請求。

## 分層系統
<a name="layered-system"></a>

在分層系統架構中，用戶端可以連線至用戶端和伺服器之間的其他授權中介裝置，而且仍會收到伺服器的回應。伺服器也可以將請求傳遞給其他伺服器。您可以設計 RESTful Web 服務在多個伺服器上執行，其中包含多個層級，例如安全性、應用程式和商業邏輯，共同滿足用戶端請求。這些圖層對用戶端來說是隱藏的。

## 快取能力
<a name="cacheability"></a>

RESTful Web 服務支援快取，這是在用戶端或媒介上儲存一些回應的程序，以改善伺服器回應時間。例如，假設您造訪的網站在每個頁面上都有通用的標頭和頁尾影像。每次您造訪新的網站頁面時，伺服器都必須重新傳送相同的映像。為了避免這種情況，用戶端會在第一次回應後快取或存放這些映像，然後直接從快取使用映像。RESTful Web 服務使用將自己定義為可快取或不可快取的 API 回應來控制快取。

## 什麼是 RESTful API？
<a name="what-is-a-restful-api"></a>

RESTful API 是兩個電腦系統用來透過網際網路安全地交換資訊的界面。大多數商業應用程式都必須與其他內部和第三方應用程式通訊，才能執行各種任務。例如，若要產生每月薪資單，您的內部帳戶系統必須與客戶的銀行系統共用資料，以自動開立發票並與內部時間表應用程式通訊。RESTful APIs支援此資訊交換，因為它們遵循安全、可靠且高效的軟體通訊標準。

## RESTful APIs如何運作？
<a name="how-do-restful-apis-work"></a>

RESTful API 的基本函數與瀏覽網際網路相同。當用戶端需要資源時，會使用 API 聯絡伺服器。API 開發人員說明用戶端應如何使用伺服器應用程式 API 文件中的 REST API。以下是任何 REST API 呼叫的一般步驟：

1.  用戶端會將請求傳送至伺服器。用戶端遵循 API 文件，以伺服器理解的方式格式化請求。

1.  伺服器會驗證用戶端，並確認用戶端有權提出該請求。

1.  伺服器會收到請求並在內部處理。

1.  伺服器會傳回回應給用戶端。回應包含告知用戶端請求是否成功的資訊。回應也包含用戶端請求的任何資訊。

REST API 請求和回應詳細資訊會因 API 開發人員設計 API 的方式而略有不同。

# 什麼是 GraphQL？
<a name="what-is-graphql"></a>

GraphQL 既是 APIs的查詢語言，也是執行這些查詢的執行時間。GraphQL 可讓用戶端請求所需的確切資料，在許多情況下為 REST 提供更靈活且更有效率的替代方案。與依賴預先定義端點的 REST 不同，GraphQL 使用單一端點，用戶端可以在其中以查詢和變動的形式指定其資料需求。

如需如何建構 [GraphQL API 的詳細資訊，請參閱 GraphQL API 的元件](https://docs.aws.amazon.com/appsync/latest/devguide/api-components.html)。 GraphQL APIs 

# 比較 REST 和 GraphQL
<a name="comparing-rest-graphql"></a>

APIs（應用程式程式設計界面） 在促進應用程式之間的資料交換方面扮演重要角色。如前所述，已出現兩種設計 APIs的重要方法：GraphQL 和 REST。雖然兩者都具有啟用用戶端-伺服器通訊的基本目的，但它們在實作和使用案例中有很大的差異。

GraphQL 和 REST 有幾個關鍵特性：

1. **Client-Server 模型**：兩者都使用用戶端-伺服器架構進行資料交換。

1. **無狀態**：兩個 都不會在請求之間維護用戶端工作階段資訊。

1. **HTTP 型**：兩者通常使用 HTTP 做為基礎通訊協定。

1. **資源導向設計**：兩者都圍繞資源設計其資料交換，這是指用戶端可以透過 API 存取和操作的任何資料或物件。

1. **資料格式彈性**：JSON 是兩者中最常使用的資料交換格式，但也支援其他格式，例如 XML 和 HTML。

1. **語言和資料庫無關**：兩者都可以使用任何程式設計語言或資料庫結構，使其具有高度互通性。

1. **快取支援**：兩者都支援快取，可讓用戶端和伺服器存放經常存取的資料，以提升效能。

在分享一些基本原則時，GraphQL 和 REST 在 API 設計和資料擷取方法方面有很大的差異：

1. **請求結構和資料擷取**

   REST 使用不同的 HTTP 方法 (GET、POST、PUT、DELETE) 對資源執行操作。這通常需要不同資源的多個端點，這可能會導致資料擷取的效率低下。例如，執行 GET 操作來擷取使用者的資料可能會導致資料過度擷取或擷取不足。若要取得正確的資料，可能會呼叫截斷或多個操作。

   GraphQL 對所有操作使用單一端點。它依賴查詢來擷取資料和變動來修改資料。用戶端可以使用查詢來擷取其在單一請求中所需的確切資料，從而透過將資料傳輸降至最低來降低網路負荷。

1. **伺服器端結構描述**

   REST 不需要伺服器端結構描述，但可以選擇性地定義結構描述，以實現高效的 API 設計和文件。

   GraphQL 使用強類型伺服器端結構描述來定義資料和資料服務。以 GraphQL 結構描述定義語言 (SDL) 編寫的結構描述包含每個物件的物件類型和欄位，以及定義每個欄位操作的伺服器端解析程式函數。

1. **版本控制**

   REST 通常在 URL 中包含版本控制，這可能會導致同時維護多個 API 版本。版本控制不是強制性的，但有助於防止中斷變更。

   GraphQL 透過要求回溯相容性來提升 API 的持續演變，而無需明確版本控制。刪除的欄位會傳回錯誤訊息，而棄用標籤則會逐步淘汰舊欄位並傳回警告訊息。

1. **錯誤處理** 

   REST 類型較弱，需要將錯誤處理內建到周圍的程式碼中。這可能不會自動識別與類型相關的錯誤 （例如，將數字剖析為文字）。

   相較之下，GraphQL 是強式輸入，需要全面的結構描述定義。這可讓您的服務自動識別許多具有高度詳細資訊的請求錯誤。

1. **使用案例**

   REST 更適合：
   + 具有較不複雜資料需求的小型應用程式。
   + 所有用戶端以類似方式使用資料和操作的情況。
   + 無需複雜資料查詢的應用程式。

   GraphQL 更適合：
   + 頻寬有限的案例，其中將請求和回應降至最低至關重要。
   + 具有多個資料來源且需要在單一端點合併的應用程式。
   + 用戶端請求有顯著差異且預期不同回應結構的案例。

   請注意，您可以在單一應用程式中使用 GraphQL 和 REST APIs 進行不同的功能領域。此外，您可以升級 RESTful API 以包含 GraphQL 功能，而無需完全重寫。如需範例，請參閱[如何為 AWS 資料來源建置 GraphQL 解析程式](https://aws.amazon.com/graphql/resolvers/)。

# 為什麼在 REST 上使用 GraphQL？
<a name="why-use-graphql"></a>

REST 是 Web APIs 的基石架構樣式之一。不過，隨著世界變得更加相互連結，開發強大且可擴展的應用程式的需求將成為更緊迫的問題。雖然 REST 通常用於建置 Web APIs，但已識別 RESTful 實作的經常性缺點如下：

1. **資料請求**：使用 RESTful APIs，您通常會透過端點請求所需的資料。當您的資料可能無法妥善封裝時，就會發生問題。您需要的資料可能位於多層抽象後面，而擷取資料的唯一方法是使用多個端點，這表示提出多個請求來擷取所有資料。

1. **過度擷取和低擷取**：若要將 新增至多個請求的問題，系統會嚴格定義每個端點的資料，這表示即使您在技術上不想要該 API，您仍會傳回為該 API 定義的任何資料。

   這可能會導致*過度擷取*，這表示我們的請求會傳回多餘的資料。例如，假設您正在請求公司人員資料，並想要知道特定部門的員工名稱。傳回資料的端點將包含名稱，但也可能包含其他資料，例如職稱或出生日期。由於 API 是固定的，您無法僅請求名稱，其餘資料會隨附於它。

   相反的情況是，我們未傳回足夠的資料稱為*擷取不足*。若要取得所有請求的資料，您可能需要向服務提出多個請求。視資料的結構方式而定，您可能會遇到效率不佳的查詢，導致 n\$11 問題等問題。

1. **緩慢的開發反覆運算**：許多開發人員會量身打造 RESTful APIs，以符合其應用程式的流程。不過，隨著應用程式的成長，前端和後端都可能需要大量的變更。因此，APIs可能不再符合資料形狀的效率或影響力。由於需要 API 修改，這會導致產品反覆運算速度變慢。

1. **大規模效能**：由於這些複合問題，許多領域會影響可擴展性。應用程式端的效能可能會受到影響，因為您的請求會傳回太多資料或太少 （導致更多請求）。這兩種情況都會對網路造成不必要的壓力，導致效能不佳。在開發人員方面，開發速度可能會降低，因為您的 APIs 是固定的，不再符合他們請求的資料。

GraphQL 的賣點是克服 REST 的缺點。以下是 GraphQL 提供給開發人員的一些重要解決方案：

1. **單一端點**：GraphQL 使用單一端點來查詢資料。您不需要建置多個 APIs來符合資料的形狀。這會導致較少的請求通過網路。

1. **擷取**：GraphQL 只需定義您需要的資料，即可解決擷取過多和不足的常年問題。GraphQL 可讓您將資料塑造成符合您的需求，因此您只會收到您要求的內容。

1. **抽象：**GraphQL APIs 包含一些元件和系統，這些元件和系統使用語言無關的標準來描述資料。換句話說，資料的形狀和結構是標準化的，因此前端和後端都知道如何透過網路傳送資料。這可讓兩端的開發人員使用 GraphQL 的系統，而不是在其周圍。

1. **快速反覆運算**：由於資料標準化，在開發的一端可能不需要變更。例如，前端呈現變更可能不會導致大量的後端變更，因為 GraphQL 允許輕鬆修改資料規格。您可以直接定義或修改資料的形狀，以符合應用程式的成長需求。這會導致較低的潛在開發工作。

這些只是 GraphQL 的一些優點。在接下來的幾個區段中，您將了解 GraphQL 的結構方式，以及讓它成為 REST 獨特替代方案的屬性。

# 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`) 解析。樹狀結構完全解決後，請求即完成，並將傳回用戶端。

# GraphQL 的其他屬性
<a name="graphql-properties"></a>

GraphQL 包含數個設計原則，可大規模維持簡單性和穩健性。

## 宣告式
<a name="declarative-property"></a>

GraphQL 是宣告式，這表示使用者只需宣告要查詢的欄位，即可描述 （塑造） 資料。回應只會傳回這些屬性的資料。例如，以下操作會擷取 DynamoDB 資料表中 ISBN 13 `id`值為 *9780199536061* 的`Book`物件：

```
{
  getBook(id: "9780199536061") {
    name
    year
    author
  }
}
```

回應將傳回承載 (`name`、 `year`和 `author`) 中的欄位，而不會傳回其他欄位：

```
{
  "data": {
    "getBook": {
      "name": "Anna Karenina",
      "year": "1878",
      "author": "Leo Tolstoy",
    }
  }
}
```

由於此設計原則，GraphQL 消除了 REST APIs 在複雜系統中處理過度和擷取不足的常年問題。這會導致更有效率的資料收集並改善網路效能。

## 階層
<a name="hierarchical-property"></a>

GraphQL 具有彈性，因為請求的資料可由使用者塑造以符合應用程式的需求。請求的資料一律遵循 GraphQL API 中定義的屬性類型和語法。例如，以下程式碼片段顯示名為 的新欄位範圍`getBook`操作`quotes`，其會傳回所有已存放的引號字串和連結至 *9780199536061* `Book` 的頁面：

```
{
  getBook(id: "9780199536061") {
    name
    year
    author
    quotes {
      description
      page
    }
  }
}
```

執行此查詢會傳回下列結果：

```
{
  "data": {
    "getBook": {
      "name": "Anna Karenina",
      "year": "1878",
      "author": "Leo Tolstoy",
      "quotes": [
         {
            "description": "The highest Petersburg society is essentially one: in it everyone knows everyone else, everyone even visits everyone else.",
            "page": 135
         },
         { 
            "description": "Happy families are all alike; every unhappy family is unhappy in its own way.",
            "page": 1
         },
         {        
            "description": "To Konstantin, the peasant was simply the chief partner in their common labor.",
            "page": 251
         }
      ]
    }
  }
}
```

如您所見，連結至所請求書籍`quotes`的欄位以 陣列的形式傳回，格式與查詢所述的格式相同。雖然此處未顯示，但 GraphQL 具有的額外優勢是，不特別了解其擷取資料的位置。 `Books`和 `quotes`可以單獨存放，但只要關聯存在，GraphQL 仍會擷取資訊。這表示您的查詢可以在單一請求中擷取多個獨立資料。

## 自我介紹
<a name="introspective-property"></a>

GraphQL 是自我記錄或自我檢查。它支援數個內建操作，可讓使用者檢視結構描述中的基礎類型和欄位。例如，以下`Foo`類型具有 `date`和 `description` 欄位：

```
type Foo {
	date: String
	description: String
}
```

我們可以使用 `_type`操作來尋找結構描述下方的輸入中繼資料：

```
{
  __type(name: "Foo") {
    name                   # returns the name of the type
    fields {               # returns all fields in the type
      name                 # returns the name of each field
      type {               # returns all types for each field
        name               # returns the scalar type
      }
    }
  }
}
```

這將傳回回應：

```
{
  "__type": {
    "name": "Foo",                     # The type name
    "fields": [
      {
        "name": "date",                # The date field
        "type": { "name": "String" }   # The date's type
      },
      {
        "name": "description",         # The description field
        "type": { "name": "String" }   # The description's type
      },
    ]
  }
}
```

此功能可用來了解特定 GraphQL 結構描述支援的類型和欄位。GraphQL 支援各種這些自我檢查操作。如需詳細資訊，請參閱[自我檢查](https://graphql.org/learn/introspection/)。

## 強式輸入
<a name="strong-typing-property"></a>

GraphQL 透過其類型和欄位系統支援強式輸入。當您在結構描述中定義某個項目時，它必須具有可在執行時間之前驗證的類型。它還必須遵循 GraphQL 的語法規格。此概念與其他語言的程式設計並無不同。例如，以下是先前 的`Foo`類型：

```
type Foo {
	date: String
	description: String
}
```

我們可以看到 `Foo`是將要建立的物件。在 執行個體中`Foo`，將會有一個 `date`和 `description` 欄位，兩者都是基本類型 `String` （純量）。總而言之，我們看到 `Foo`已宣告，且其欄位存在於其範圍內。這種類型檢查和邏輯語法的組合可確保您的 GraphQL API 簡潔且自我明顯。您可以在[此處](https://spec.graphql.org/)找到 GraphQL 的輸入和語法規格。