1. 概述
當創建用於支持我們應用程序的 Web 服務時,我們可能會選擇使用 REST 或 GraphQL 作為通信模式。雖然兩者都可能使用 HTTP 上的 JSON,但它們具有不同的優勢和劣勢。
在本教程中,我們將比較 GraphQL 與 REST。我們將創建一個產品數據庫示例,並比較在執行相同客户端操作時,這兩種解決方案之間的差異。
2. 示例服務
我們的示例服務將允許我們:
- 創建草稿狀態的產品
- 更新產品詳情
- 獲取產品列表
- 獲取單個產品的詳細信息,包括其訂單
讓我們從使用 REST 創建應用程序開始。
3. REST
REST (Representational State Transfer) 是一種用於分佈式超媒體系統的 架構風格。REST 的主要數據元素稱為 資源。在本示例中,資源是 “產品”。
3.1. 創建產品
為了創建產品,我們將使用 API 的 POST 方法:
curl --request POST 'http://localhost:8081/product' \
--header 'Content-Type: application/json' \
--data '{
"name": "Watch",
"description": "Special Swiss Watch",
"status": "Draft",
"currency": "USD",
"price": null,
"imageUrls": null,
"videoUrls": null,
"stock": null,
"averageRating": null
}'因此,系統將創建一個新的產品。
3.2. 更新產品
在 RESTful 架構中,我們通常使用 PUT 方法來更新產品:
curl --request PUT 'http://localhost:8081/product/{product-id}' \
--header 'Content-Type: application/json' \
--data '{
"name": "Watch",
"description": "Special Swiss Watch",
"status": "Draft",
"currency": "USD",
"price": 1200.0,
"imageUrls": [
"https://graphqlvsrest.com/imageurl/product-id"
],
"videoUrls": [
"https://graphqlvsrest.com/videourl/product-id"
],
"stock": 10,
"averageRating": 0.0
}'因此,對象將會更新。
3.3. 獲取產品列表
列出產品通常是一個 GET 操作,我們可以使用查詢字符串來指定分頁:
curl --request GET 'http://localhost:8081/product?size=10&page=0'第一個響應對象是:
{
"id": 1,
"name": "T-Shirt",
"description": "Special beach T-Shirt",
"status": Published,
"currency": "USD",
"price": 30.0,
"imageUrls": ["https://graphqlvsrest.com/imageurl/1"],
"videoUrls": ["https://graphqlvsrest.com/videourl/1"],
"stock": 10,
"averageRating": 3.5
}3.4. 獲取包含訂單的單個產品
為了獲取產品及其訂單,我們通常會先通過之前的 API 獲取產品列表,然後通過對 訂單 資源進行調用來查找相關訂單:
curl --request GET 'localhost:8081/order?product-id=1'第一個響應對象是:
{
"id": 1,
"productId": 1,
"customerId": "de68a771-2fcc-4e6b-a05d-e30a8dd0d756",
"status": "Delivered",
"address": "43-F 12th Street",
"creationDate": "Mon Jan 17 01:00:18 GST 2022"
}當我們通過產品 ID查詢訂單時,將 ID 作為查詢參數提供給 GET 操作是有意義的。然而,我們也需要注意到,我們需要為我們感興趣的每個產品執行一次此操作,再加上原始操作以檢索所有產品。這與 N + 1 查詢問題相關。
4. GraphQL
GraphQL 是一種用於 API 的查詢語言,它提供了一個框架來使用現有的數據服務來滿足這些查詢。
GraphQL 的基本構建塊是 查詢和變體。查詢負責獲取數據,而變體則用於創建和更新。
查詢和變體都定義一個 模式。模式定義了可能的客户端請求和響應。
讓我們使用 GraphQL 服務器重新實現我們的示例。
4.1. 創建產品
讓我們使用一個變異(mutation)命名為 saveProduct:
curl --request POST 'http://localhost:8081/graphql' \
--header 'Content-Type: application/json' \
--data \
'{
"query": "mutation {saveProduct (
product: {
name: \"Bed-Side Lamp\",
price: 24.0,
status: \"Draft\",
currency: \"USD\"
}){ id name currency price status}
}"
}'在 saveProduct 函數中,括號內的所有內容都是 輸入 類型 模式。 括號之後的大括號描述了服務器返回的 字段。
當運行 mutation 時,我們應該期望收到包含所選字段的響應。
{
"data": {
"saveProduct": {
"id": "12",
"name": "Bed-Side Lamp",
"currency": "USD",
"price": 24.0,
"status": "Draft"
}
}
}此請求與我們使用 REST 版本時所做的 POST 請求非常相似,但現在我們可以稍微自定義響應。
4.2. 更新產品
同樣,我們還可以使用另一個變異(mutation)updateProduct 來修改產品:
curl --request POST 'http://localhost:8081/graphql' \
--header 'Content-Type: application/json' \
--data \
'{"query": "mutation {updateProduct(
id: 11
product: {
price: 14.0,
status: \"Publish\"
}){ id name currency price status }
}","variables":{}}'我們接收到選定的字段在響應中:
{
"data": {
"updateProduct": {
"id": "12",
"name": "Bed-Side Lamp",
"currency": "USD",
"price": 14.0,
"status": "Published"
}
}
}如我們所見,GraphQL 提供了對響應格式的靈活性。
4.3. 獲取產品列表
為了從服務器獲取數據,我們將使用一個查詢:
curl --request POST 'http://localhost:8081/graphql' \
--header 'Content-Type: application/json' \
--data \
'{
"query": "query {products(size:10,page:0){id name status}}"
}'在這裏,我們還描述了我們希望看到的搜索結果頁面:
{
"data": {
"products": [
{
"id": "1",
"name": "T-Shirt",
"status": "Published"
},
...
]
}
}4.4. 通過 GraphQL 獲取單個產品及其訂單
使用 GraphQL,我們可以請求 GraphQL 服務器將產品和訂單合併在一起:
curl --request POST 'http://localhost:8081/graphql' \
--header 'Content-Type: application/json' \
--data \
'{
"query": "query {product(id:1){ id name orders{customerId address status creationDate}}}"
}'在本次查詢中,我們檢索具有<em>id</em>值為 1 的產品及其訂單。這使我們能夠在單個操作中發出請求。讓我們查看響應:
{
"data": {
"product": {
"id": "1",
"name": "T-Shirt",
"orders": [
{
"customerId": "de68a771-2fcc-4e6b-a05d-e30a8dd0d756",
"status": "Delivered",
"address": "43-F 12th Street",
"creationDate": "Mon Jan 17 01:00:18 GST 2022"
},
...
]
}
}
}如我們所見,響應中包含產品的詳細信息及其對應的訂單。
為了通過 REST 方式實現這一點,我們需要發送多條請求——首先是獲取產品的信息,然後是獲取其對應的訂單。
5. 比較
這些示例展示了使用 GraphQL 如何減少客户端與服務器之間的流量,並允許客户端為響應提供一些格式規則。
需要注意的是,這些 API 的後端數據源仍然可能需要執行相同的操作來修改或檢索數據,但客户端與服務器之間接口的豐富性允許客户端使用 GraphQL 完成更少的工作。
讓我們進一步比較這兩種方法。
5.1. 靈活和動態
GraphQL 允許靈活和動態的查詢:
- 客户端應用程序可以僅請求所需字段
- 可以使用 別名 來請求具有自定義鍵的字段
- 客户端可以使用查詢來管理結果排序
- 客户端可以更好地與 API 中的任何更改解耦,因為沒有單一響應對象結構的響應對象需要遵循
5.2. 成本較低的操作
每個服務器請求都有往返時間及數據包大小的成本。
在 RESTful 架構中,為了實現所需功能,我們可能需要發送多個請求。這些多個請求會產生較高的成本。 響應數據包也可能包含不必要的數據,這些數據可能並非客户端應用程序所需要。
GraphQL 傾向於避免這些高成本的操作。 我們通常可以使用 GraphQL 在單個請求中獲取所需的所有數據。
5.3. 何時使用 REST?
GraphQL 不是 REST 的替代品。兩者甚至可以在同一個應用程序中共存。託管 GraphQL 端點的複雜性可能會根據用例而有所不同,值得考慮。
我們可能更喜歡 REST 的情況是:
- 我們的應用程序本質上是資源驅動型的,操作與單個資源實體直接且完全相關
- 需要 Web 緩存,因為 GraphQL 本身不支持它
- 我們需要文件上傳,因為 GraphQL 本身不支持它
6. 結論
在本文中,我們通過一個實際示例比較了 REST 和 GraphQL。
我們瞭解到如何通常使用每種方法。
然後,我們討論了兩種方法都沒有明顯的優勢。我們的需求將成為選擇它們之間的驅動力。偶爾,兩者也可以共存。