1. 概述
在為我們的應用程序創建 Web 服務時,我們可能會選擇使用 REST 或 GraphQL 作為通信模式。雖然兩者都可能使用 JSON 配合 HTTP,但它們在優勢和劣勢上存在差異。
在本教程中,我們將比較 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. 更新產品
在 REST 中,我們通常使用 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
}'
作為結果,對象 {product-id} 將會更新。
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. 獲取單個產品與訂單
要獲取產品及其訂單,我們可能需要獲取所有產品列表,然後調用 order 資源來查找相關訂單:
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"
}
由於我們正在查詢按 product-id 進行的訂單,因此為 GET 操作提供 id 作為查詢參數是很合理的。然而,我們需要注意,對於每個我們感興趣的產品,我們都需要執行此操作一次,再加上獲取所有產品的原始操作。這與 N + 1 Select Problem 相關。
4. GraphQL
GraphQL 是一個用於 API 的查詢語言,它還附帶了一個用於使用現有數據服務滿足這些查詢的框架。
GraphQL 的構建塊是 查詢和修改。查詢負責獲取數據,而修改用於創建和更新。
查詢和修改都定義一個 模式。模式定義了客户端請求和響應的可能。
讓我們使用 GraphQL 服務器重新實現我們的示例。
4.1. 創建產品
讓我們使用名為 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 函數中,括號內的所有內容都是 輸入 類型 模式。花括號內的內容描述了服務器返回的 字段。
當我們運行修改時,我們應該期望收到選擇的字段響應:
{
"data": {
"saveProduct": {
"id": "12",
"name": "Bed-Side Lamp",
"currency": "USD",
"price": 24.0,
"status": "Draft"
}
}
}
此請求與我們使用 REST 版本創建的 POST 請求非常相似,但我們現在可以稍微自定義響應。
4.2. 更新產品
同樣,我們可以使用另一個名為 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 服務器將產品和訂單合併:
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}}}"
}'
在該查詢中,我們檢索具有 ID 為 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"
},
...
]
}
}
}
正如我們所看到的,響應包含產品的詳細信息及其各自的訂單。
要實現這一點,我們必須發送兩個請求 - 第一個獲取產品,第二個獲取相應的訂單。
5. 比較
這些示例展示瞭如何使用 GraphQL 減少客户端與服務器之間的流量,並允許客户端為響應提供一些格式規則。
值得注意的是,這些 API 的數據源可能仍然需要執行相同的操作來修改或獲取數據,但客户端與服務器之間接口的豐富性允許客户端使用 GraphQL 完成更少的工作。
讓我們更詳細地比較這兩種方法。
5.1. 靈活且動態
GraphQL 允許靈活和動態的查詢:
- 客户端應用程序可以請求僅需要的字段
- 別名 可用於請求具有自定義鍵的字段
- 客户端可以使用查詢來管理結果排序
- 客户端可以更好地與 API 中的任何更改解耦,因為沒有單個響應對象結構的“版本”需要遵循
5.2. 成本更低的運算
每個服務器請求都有往返時間、payload 大小的代價。
在 REST 中,我們可能會發送多個請求才能實現所需的功能。 這些多個請求將是一個昂貴的運算。 響應 payload 可能會包含客户端應用程序不需要的數據。
GraphQL 傾向於避免昂貴的運算。 我們通常可以使用單個請求從 GraphQL 中獲取我們所需的所有數據。
5.3. 何時使用 REST?
GraphQL 不是 REST 的替代品。 兩個甚至可以在同一個應用程序中共存。 託管 GraphQL 端點的增加複雜性可能取決於用例而值得,取決於用例。
我們可能更喜歡 REST 時:
- 我們的應用程序是自然資源驅動的,其中操作與單個資源實體密切相關
- 需要 web 緩存,因為 GraphQL 沒有原生支持它
- 我們需要文件上傳,因為 GraphQL 沒有原生支持它
6. 結論
在本文中,我們通過一個實際示例比較了 REST 和 GraphQL。
我們看到了如何可能採用所學到的每種方法。
然後,我們討論了兩種方法都沒有明顯的優勢。我們的需求將成為選擇它們之間的驅動力。有時,兩者也可以共存。