
掌握API建模:基本概念和實踐
name
books {
id
title
reviews {
text
date
user {
id
name
}
}
}
}
使用 GraphQL,用戶不能簡單地運行他們想要的任何查詢。必須仔細設計 GraphQL API;這不僅僅是將其放在 REST API 或數據庫之上。
對于復雜查詢,REST API 可能更易于設計,因為您可以針對特定需求建立多個終端節點,并且可以微調特定查詢以有效地檢索數據。
這一點可能有些爭議,因為多次網絡調用仍然會消耗大量時間。但是,如果您不小心,幾個大型查詢可能會極大地拖垮您的服務器。從這個意義上說,GraphQL 的最大優勢也可能成為其最大的弱點。
在 GraphQL API 中,Dataloader 等工具允許您批處理和緩存數據庫調用。但在某些情況下,即使這樣還不夠,唯一的解決方案是通過計算最大執行成本或查詢深度來阻止查詢。
graphql-query-complexity 等庫有助于限制查詢的大小和復雜性,以防止出現性能問題,并保護您的 GraphQL 服務器免受資源耗盡和 DoS 攻擊。graphql-query-complexity 在復雜查詢影響服務器資源之前拒絕它們:
const { ApolloServer } = require('apollo-server');const { getComplexity, simpleEstimator } = require('graphql-query-complexity');
const server = new ApolloServer({
typeDefs,
resolvers,
plugins: [
{
requestDidStart: () => ({
didResolveOperation({ request, document }) {
const complexity = getComplexity({
schema: server.schema,
query: document,
variables: request.variables,
estimators: [simpleEstimator({ defaultComplexity: 1 })],
});
if (complexity > 100) {
throw new Error('Query too complex!');
}
},
}),
},
],
});
server.listen();
上述代碼估計了查詢的復雜度,并拒絕了超過定義閾值 100 的查詢,從而防止了潛在的性能問題。
請務必記住,GraphQL 是用于開發 API 的 REST 替代方案,而不是替代品。
使用 GraphQL 的主要好處是能夠發送一個只指定所需信息的查詢,并精確接收這些信息。然而,通過在 URL 中傳遞您想要使用的字段的名稱,然后自行實現解析和返回數據的邏輯,您也可以使用 REST 達到同樣的效果:
GET /books/1492030716?fields=title,pageCount
雖然這看起來更容易實現,但缺點是每個 REST 端點都需要自定義邏輯來解析和篩選字段,這增加了處理嵌套數據時的復雜性,而使用 GraphQL 更容易實現。
值得慶幸的是,JSON 架構有助于定義 API 響應的結構,從而實現驗證和一致性,并且提供多種語言版本。如果希望在 REST 中使用架構和強類型的好處,可以使用 JSON 架構;許多庫實現并支持 JSON 架構。但是,JSON Schema 缺乏 GraphQL 提供的內省和實時靈活性。
如果您想在 REST API 中使用查詢語言,OData 是一個很好的替代方案。OData 是 Open Data Protocol 的簡稱,最初由微軟于 2007 年開發。它是一種開放協議,能夠以簡單、標準的方式創建和使用可查詢且可互操作的 RESTful API。OData 提供了豐富的查詢功能,并且由于其開源方法和可擴展性,正在迅速獲得認可。
有許多有效的替代方案,特別是對于使用 GraphQL 可能有點矯枉過正的小型應用程序和項目。出于同樣的原因,您也會遇到這樣的情況:實現這些庫會很復雜,而使用 GraphQL 會更容易,它本身支持所有這些功能。但是,GraphQL 也會使事情復雜化,我們接下來將對此進行討論。
不建議在簡單的應用程序中使用 GraphQL。例如,在每次都以相同方式使用幾個字段的應用程序中,使用 GraphQL 會增加更多復雜性,因為以下原因:
從維護的角度來看,這尤其有害,因為您需要隨著后端引入的每個新更改來更新模式,并且還需要為每個查詢和變更維護解析器。但是,即使使用 GraphQL 是合理的,也可能會遇到一些復雜情況。其中兩個例子是錯誤處理和文件上傳。
在 REST 中,檢查響應狀態是了解請求是否成功執行、是否存在服務器錯誤或是否未找到資源的唯一方法。但是,當 GraphQL 中發生錯誤時,您會收到類似于以下內容的內容:
{ "data": null,
"errors": [
{
"message": "Validation error...",
"locations": [
{
"line": 5,
"column": 6
}
]
}
]
}
您必須解析此消息以了解是否存在錯誤,不同的錯誤可能具有略有不同的格式或一些自定義字段。
一些庫,如 Apollo Client,有助于處理錯誤,但這并不像在 REST API 中那么容易。
文件上傳不是 GraphQL 規范的一部分,因此由您來實施。一些選項包括:
第三種選擇可能是最好的。但是,這意味著要添加另一個依賴項來管理你的項目,并且它可能不適用于所有編程語言。
REST 中的錯誤處理比 GraphQL 中的錯誤處理更容易。RESTful API 遵循有關資源的 HTTP 規范,并提供不同的 HTTP 狀態代碼來指示 API 請求的狀態。
與此同時,GraphQL 會為每個 API 請求返回狀態,包括錯誤狀態。這使得錯誤管理變得困難,并且難以與監控工具集成。雖然 Apollo 客戶端庫通過內置機制來規范化錯誤,以區分 HTTP 狀態錯誤和 GraphQL 特定錯誤,從而簡化了這一過程,但它仍然沒有解決與監控工具相關的挑戰。
自省是客戶端動態查詢架構并接收詳細信息(包括其類型和活動)的能力。
攻擊者可以執行精確的攻擊,并通過暴露與底層數據模型相關的私有數據來訪問未經授權的信息。一些緩解技術在生產中禁用內省,實施速率限制,并使用字段級授權來阻止對敏感架構部分的訪問。
在 GraphQL 中,速率限制(Rate Limiting)有助于在一定時間內限制操作的數量,從而防止濫用并保護系統免受拒絕服務攻擊(Denial-of-Service Attacks)。如果沒有有效的速率限制,攻擊者可以發送大量的 API 請求來淹沒服務器,導致 API 垃圾信息泛濫和服務耗盡或不可用。
Query depth 是單個查詢中嵌套字段的數量,它顯示客戶端可以瀏覽數據圖表的深度。當客戶端發送非常深或嵌套的查詢時,可能會出現安全挑戰,這可能會導致資源耗盡和服務器性能停機。
攻擊者可以創建復雜的嵌套查詢,從而導致拒絕服務問題。我們可以通過限制任何傳入查詢的最大深度來輕松防止惡意使用查詢深度功能。
當無效的用戶輸入從 GraphQL 服務器發送到客戶端的響應數據中以竊取個人信息或代表客戶端執行未經授權的操作時,就會發生跨站點腳本 (XSS)。
XSS 攻擊可能會產生多種后果,但常見的后果是將敏感數據(cookie、會話信息等)傳輸給攻擊者或將受害者重新路由到另一個網站。輸入驗證和輸出編碼有助于減少 XSS 攻擊。
設計不當的模式可能會意外地將敏感信息暴露給未經授權的用戶。如果輸入驗證不當,還可能導致注入攻擊。為了發現和修復模式及驗證過程中的漏洞,必須定期進行安全評估和測試。
此外,通過使用類型系統,服務器和客戶端可以在發出無效查詢時輕松通知開發人員,而不是依賴于運行時檢查。
gRPC?(Google 遠程過程調用)
tRPC(鍵入的 RPC)
Falcor
OData (開放數據協議)
通過不安全的通道傳輸敏感信息會使其面臨被篡改的風險。如果沒有使用適當的加密方法,例如 HTTPS,攻擊者可能會在數據傳輸過程中攔截并獲取對敏感數據的未經授權訪問。管理不善的數據傳輸可能會導致數據泄露、身份盜竊或其他惡意活動。
CORS 是指一個域上托管的 Web 應用程序嘗試向另一個域上托管的 API 發出請求。默認情況下,Web 瀏覽器實施同源策略,通過阻止跨域請求來遏制任何潛在的安全問題。
使用 CORS,服務器管理員可以指定哪個源可以訪問他們的資源。任何錯誤的配置都可能導致攻擊者發出未經授權的請求并泄露私人信息。
會話管理是指對用戶交互過程中的有狀態信息進行維護。對用戶會話的不當管理可能會導致諸如會話劫持等漏洞。會話令牌(session tokens)的生成、存儲或傳輸方面存在問題可能會泄露敏感的用戶數據,從而威脅到用戶會話的完整性和保密性。
薄弱或設計不佳的身份驗證和授權系統可能會導致未經授權的訪問、數據泄露和潛在的 API 功能濫用。
在本指南中,我們介紹了使用 GraphQL 可能導致性能挑戰、架構復雜性和復雜查詢問題的一些實例。我們還提供了指南,以幫助決定在您的項目中是實施 GraphQL 還是 REST 架構。
GraphQL 是一個強大的工具,與 REST 相比具有多項優勢。但是,如果您更喜歡簡單性和更快的性能,則選擇 REST 架構可能是更好的選擇。
請記住,此處概述的注意事項可能并不普遍適用,但值得考慮它們以確定它們與您的具體情況的相關性。
原文來源:https://blog.logrocket.com/graphql-vs-rest-api-why-you-shouldnt-use-graphql/