GraphQL 初探

{"type":"doc","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"對於 API 而言,GraphQL 被視爲一種革命性的新思路、新技術。GraphQL 改變了前後端團隊的交互方式、顛覆了前後端團隊的通信方式,使得他們可以更順暢而高效地協作。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"正如Samer Buna在其著作 《GraphQL in Action》 中的序言中所說的那樣:","attrs":{}}]},{"type":"blockquote","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"早在 2015 年,Facebook 首次宣佈 GraphQL 項目時,我第一次聽說了 GraphQL,那個時候 GraphQL 就深深的吸引了我。並且,學習 GraphQL 是我做過的最好的時間投資之一。","attrs":{}}]}],"attrs":{}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"最近,在不斷的瞭解、學習、使用 GraphQL 之後,我也像 Samer Buna 一樣,被 GraphQL 深深的吸引了。","attrs":{}}]},{"type":"heading","attrs":{"align":null,"level":2},"content":[{"type":"text","text":"GraphQL簡介","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"關於 GraphQL 的介紹,網上已經有非常多的資料了,這裏不再過多描述,具體可以參考 ","attrs":{}},{"type":"link","attrs":{"href":"https://graphql.cn/","title":null,"type":null},"content":[{"type":"text","text":"GraphQL.org","attrs":{}}]},{"type":"text","text":",","attrs":{}},{"type":"link","attrs":{"href":"https://docs.github.com/en/graphql","title":null,"type":null},"content":[{"type":"text","text":"GitHub GraphQL Docs","attrs":{}}]},{"type":"text","text":",以及 《GraphQL in Action》。","attrs":{}}]},{"type":"heading","attrs":{"align":null,"level":2},"content":[{"type":"text","text":"GraphQL vs. REST","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"關於 GraphQL 和 REST 之間的詳細對比,我認爲可以參考 ","attrs":{}},{"type":"link","attrs":{"href":"https://www.apollographql.com/blog/graphql/basics/graphql-vs-rest/","title":null,"type":null},"content":[{"type":"text","text":"GraphQL vs. REST","attrs":{}}]},{"type":"text","text":" 以及 ","attrs":{}},{"type":"link","attrs":{"href":"https://www.howtographql.com/basics/1-graphql-is-the-better-rest/","title":null,"type":null},"content":[{"type":"text","text":"GraphQL is the better REST","attrs":{}}]},{"type":"text","text":",此處不再贅述了。","attrs":{}}]},{"type":"heading","attrs":{"align":null,"level":2},"content":[{"type":"text","text":"GraphQL 的優勢","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"在 《GraphQL in Action》 中,作者對 GraphQL 的優缺點做了很全面的介紹;在 ","attrs":{}},{"type":"link","attrs":{"href":"https://graphql.org/","title":null,"type":null},"content":[{"type":"text","text":"GraphQL 的社區網站","attrs":{}}]},{"type":"text","text":" 上,也對 GraphQL 的優點做了全面的介紹;甚至,可以用搜索引擎搜索出一堆 GraphQL 相關的文章和資源……","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"但是,作爲一個剛接觸 GraphQL 的門外漢,我還是希望寫一寫自己學習 GraphQL 之後的感想,尤其是我覺的 GraphQL 那些能讓我激動無比的特性。","attrs":{}}]},{"type":"heading","attrs":{"align":null,"level":4},"content":[{"type":"text","text":"GraphQL 的強類型系統","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"在 GraphQL 中,類型系統用於描述 GraphQL Server 的能力並用於判斷一個查詢是否有效。類型系統還描述了查詢參數的輸入類型,並在 GraphQL Runtime 中檢查參數值的有效性。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"一個 GraphQL 服務是通過定義","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"類型","attrs":{}}],"attrs":{}},{"type":"text","text":"和","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"類型","attrs":{}}],"attrs":{}},{"type":"text","text":"上的","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"字段","attrs":{}}],"attrs":{}},{"type":"text","text":"來創建的,然後給每個","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"類型","attrs":{}}],"attrs":{}},{"type":"text","text":"上的每個","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"字段","attrs":{}}],"attrs":{}},{"type":"text","text":"提供","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"解析函數","attrs":{}}],"attrs":{}},{"type":"text","text":"。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"例如,在 ","attrs":{}},{"type":"link","attrs":{"href":"https://docs.github.com/en/graphql/overview/explorer","title":null,"type":null},"content":[{"type":"text","text":"Github GraphQL Server","attrs":{}}]},{"type":"text","text":" 中,使用 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"viewer","attrs":{}}],"attrs":{}},{"type":"text","text":" 字段來描述當前登錄用戶的信息,而 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"viewer","attrs":{}}],"attrs":{}},{"type":"text","text":" 的","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"類型","attrs":{}}],"attrs":{}},{"type":"text","text":"爲 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"User","attrs":{}}],"attrs":{}},{"type":"text","text":"。","attrs":{}}]},{"type":"codeblock","attrs":{"lang":"javascript"},"content":[{"type":"text","text":"type Query {\n viewer: User!\n}\n\ntype User {\n // ...\n location: String\n login: String!\n name: String\n // ...\n}","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"一旦啓動某個 GraphQL Server,它就能接收 GraphQL 查詢,並驗證和執行該查詢。GraphQL Server 首先會檢查該查詢以確保它只引用了已定義的","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"類型","attrs":{}}],"attrs":{}},{"type":"text","text":"和","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"字段","attrs":{}}],"attrs":{}},{"type":"text","text":",然後運行指定的","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"解析函數","attrs":{}}],"attrs":{}},{"type":"text","text":"來生成結果。例如,我們可以在 ","attrs":{}},{"type":"link","attrs":{"href":"https://docs.github.com/en/graphql/overview/explorer","title":null,"type":null},"content":[{"type":"text","text":"Github GraphQL Server","attrs":{}}]},{"type":"text","text":" 中運行如下的查詢:","attrs":{}}]},{"type":"codeblock","attrs":{"lang":"javascript"},"content":[{"type":"text","text":"query { \n viewer { \n login\n name\n }\n}","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"從如上的例子可以看出,GraphQL 實際上是一種強類型的 API。與使用普通的 REST API 相比,強類型系統是 GraphQL 最吸引我的地方之一。在我看來,正是 GraphQL 強類型的特性,開創了 API 的新天地。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"在我看來,REST API 缺乏","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"類型系統","attrs":{}}],"attrs":{}},{"type":"text","text":"特性在開發中會引入很多的問題,工作中,我經常會遇到如下的情況:","attrs":{}}]},{"type":"bulletedlist","content":[{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"上游服務修改了 REST API 的字段類型而未周知下游消費者導致下游服務異常甚至崩潰","attrs":{}}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"服務端未返回特定字段導致客戶端崩潰","attrs":{}}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"客戶端請求參數異常,導致服務端的錯誤率上升","attrs":{}}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"到處找藉口文檔,好不容易找到了忽然發現,接口文檔和接口的返回並不一致,文檔太滯後了","attrs":{}}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"……","attrs":{}}]}]}],"attrs":{}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"如果有一個類型系統進行約束,則情況會變的更好一點。因此,針對於 json 格式的 REST API 而言,開始出現類似 ","attrs":{}},{"type":"link","attrs":{"href":"https://swagger.io/tools/swagger-ui/","title":null,"type":null},"content":[{"type":"text","text":"Swagger UI","attrs":{}}]},{"type":"text","text":" 這樣的工具,利用 ","attrs":{}},{"type":"link","attrs":{"href":"https://json-schema.org/","title":null,"type":null},"content":[{"type":"text","text":"Json Schema","attrs":{}}]},{"type":"text","text":" 來強化其類型系統。雖然如此,但是,在我看來,基於 Json Schema 的 REST API 類型系統僅僅是一個規範和建議而已,團隊中的其他人不使用也沒辦法。但是,GraphQL 的類型系統是其根基,所有人必須遵守,這就在大家對接口描述形成統一認識上發揮着重要作用。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"同樣的道理,對於 ","attrs":{}},{"type":"link","attrs":{"href":"https://github.com/Tencent/rapidjson/","title":null,"type":null},"content":[{"type":"text","text":"rapidjson","attrs":{}}]},{"type":"text","text":" 而言,從 v1.1 版本開始,也加入了 JSON Schema 功能,使其可以在解析或生成 JSON 時進行校驗,以避免類型錯誤的 json 而導致的解析時崩潰問題的發生。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"弱類型語言引入類型系統也變得越來越流行,例如微軟的 ","attrs":{}},{"type":"link","attrs":{"href":"https://www.typescriptlang.org/","title":null,"type":null},"content":[{"type":"text","text":"TypeScript","attrs":{}}]},{"type":"text","text":",PHP7 的 ","attrs":{}},{"type":"link","attrs":{"href":"https://www.php.net/manual/en/language.types.declarations.php","title":null,"type":null},"content":[{"type":"text","text":"強類型模式","attrs":{}}]},{"type":"text","text":",還有 Facebook 開發的 ","attrs":{}},{"type":"link","attrs":{"href":"https://hacklang.org/","title":null,"type":null},"content":[{"type":"text","text":"Hack語言","attrs":{}}]},{"type":"text","text":"……","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"弱類型,看似無拘無束,實則充滿了混亂與危險;強類型,看似畫地爲牢,實則是另一種自由。在 GraphQL 類型系統的幫助下,我們可以精確的描述我們的接口數據,並且可以做到數據描述和接口完全對應。這種能力對於大型、多人協作式團隊而言,至關重要。尤其是微服務盛行的今天,我認爲,GraphQL 的強類型系統爲微服務交互提供了新的活力。","attrs":{}}]},{"type":"heading","attrs":{"align":null,"level":4},"content":[{"type":"text","text":"我的地盤聽我的,僅要我所需","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"在 GraphQL 中,GraphQL Runtime 確定 GraphQL Server 可以提供的能力,而消費端具體要獲取哪些數據則完全由消費者說了算。對於下游消費者而言真正做到了:我的地盤,聽我的!只要我所要的,不多也不少!","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"之所以說 GraphQL 是革命性的新技術,我覺得就在於此。GraphQL 改變了服務之間交互的方式,讓通信雙方在交互的過程中更加平等,改變了之前響應數據完全由 Server 端說了算的局面。如今,在 GraphQL 中,客戶端(消費端)可以以更加平等的地位,更加積極的參與到整個的數據交互過程。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"藉助於 GraphQL 的類型系統,客戶端可以更加自由的根據自己的需求來獲得自己的所需,而無需收到 Server 端的限制。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"在我的個人博客站點中,我採用 ","attrs":{}},{"type":"link","attrs":{"href":"https://docs.github.com/en/actions","title":null,"type":null},"content":[{"type":"text","text":"github action","attrs":{}}]},{"type":"text","text":" 來編譯併發布站點內容。站點中,會有很多基於 ","attrs":{}},{"type":"link","attrs":{"href":"https://www.gitbook.com/","title":null,"type":null},"content":[{"type":"text","text":"gitbook","attrs":{}}]},{"type":"text","text":" 的書籍,並且每一本書都對應一個 github 倉庫。爲了加快站點的發佈速度,對於每一本書籍而言,都會提前編譯並且將編譯產物置於 latest release 中。在發佈主站內容時,我利用 ","attrs":{}},{"type":"link","attrs":{"href":"https://github.com/wangwei1237/wangwei1237.github.io_src/blob/master/build_books.sh","title":null,"type":null},"content":[{"type":"text","text":"build_books.sh","attrs":{}}]},{"type":"text","text":" 腳本來獲取書籍的 release 產物。如果採用REST API,則相對比較簡單,直接請求如下的 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"end point","attrs":{}}],"attrs":{}},{"type":"text","text":" 即可:","attrs":{}}]},{"type":"codeblock","attrs":{"lang":"shell"},"content":[{"type":"text","text":"https://api.github.com/repos/wangwei1237/${BOOKS[i]}/releases/latest","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"這種情況下,我只需要對應的 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"assets.browser_download_url","attrs":{}}],"attrs":{}},{"type":"text","text":"字段,但是使用 REST API 我沒辦法控制接口的返回數據,即便我只需要某一個字段,接口還是會返給我所有的數據。而如果使用 GraphQL,我就可以自己說了算了:","attrs":{}}]},{"type":"codeblock","attrs":{"lang":"javascript"},"content":[{"type":"text","text":"// GraphQL Query\nquery getBooksAssetUrl($repo: String!, $owner: String!) { \n repository(name: $repo, owner: $owner) {\n latestRelease {\n releaseAssets(first: 1) {\n nodes {\n downloadUrl\n }\n }\n }\n }\n}\n\n// Query Variables\n{\n \"repo\": \"monolith-to-microservices\",\n \"owner\": \"wangwei1237\"\n}\n\n// Query Response\n{\n \"data\": {\n \"repository\": {\n \"latestRelease\": {\n \"releaseAssets\": {\n \"nodes\": [\n {\n \"downloadUrl\": \"https://github.com/wangwei1237/monolith-to-microservices/releases/download/v1.1.1/monolith-to-microservices.tar.gz\"\n }\n ]\n }\n }\n }\n }\n}","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"因此,在這種情況下,GraphQL 完美的避免了不必要的數據傳輸(overfetching),尤其是對於如下的場景:流量較大,網絡較差,針對不同的客戶端返回不同數據……,這個特性尤爲重要。更重要的是,這個特性改變了通信雙方的話語權。","attrs":{}}]},{"type":"heading","attrs":{"align":null,"level":4},"content":[{"type":"text","text":"精確預測響應數據","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"GraphQL 不但改變了通信雙方的話語權,還使得客戶端可以精確的預測服務端的響應。在使用 REST API 時,我們需要藉助各種 API 文檔才能","attrs":{}},{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"大概","attrs":{}},{"type":"text","text":"預測請求的響應,在開發中,這確實是一件非常討厭的事情。無法精確的預測請求的響應,這回直接導致代碼的容錯性大大折扣。誰能確保所有的接口的接口文檔都會詳細的窮舉所有情況下的接口響應呢?據我的經驗而言,很難……","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"當我請求一個沒有 release 產物的的倉庫時,根據 GraphQL 的類型系統,我會預測到,此時 GraphQL 會返回 null,如下所示:","attrs":{}}]},{"type":"codeblock","attrs":{"lang":"javascript"},"content":[{"type":"text","text":"// latestRelease type\nlatestRelease: Release\n\n// Query Variables\n{\n \"repo\": \"wangwei1237.github.io\",\n \"owner\": \"wangwei1237\"\n}\n\n// Query Response\n{\n \"data\": {\n \"repository\": {\n \"latestRelease\": null\n }\n }\n}","attrs":{}}]},{"type":"heading","attrs":{"align":null,"level":4},"content":[{"type":"text","text":"GraphQL API 的版本控制","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"對於 API 的版本控制而言,GraphQL 借鑑了其他語言中的 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"@deprecated註解","attrs":{}}],"attrs":{}},{"type":"text","text":"。如果經受過 REST API 的版本控制之痛,經受過那些 v1,v2,……,那麼你也會像我一樣,喜歡 GraphQL 提供的這一特性。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"摘錄Samer Buna在 《GraphQL in Action》 中的例子如下:","attrs":{}}]},{"type":"codeblock","attrs":{"lang":"javascript"},"content":[{"type":"text","text":"type Query {\n hello: String\n currentTime: String\n sumNumbersInRange(begin: Int!, end: Int!): Int! @deprecated(reason: \"use new fields numbersInRange\")\n numbersInRange(begin: Int!, end: Int!): NumbersInRange!\n taskMainList: [Task!]\n taskInfo(taskId: ID!): Task!\n}\n\n\"\"\"Aggregate info on a range numbers\"\"\"\ntype NumbersInRange {\n sum: Int!\n count: Int!\n avg: Float!\n}","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"對於 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"sumNumbersInRange","attrs":{}}],"attrs":{}},{"type":"text","text":" 字段,使用 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"@deprecated","attrs":{}}],"attrs":{}},{"type":"text","text":" 標誌該字段是廢棄字段,並且利用 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"reason","attrs":{}}],"attrs":{}},{"type":"text","text":" 參數來將該廢棄字段和推薦的新字段關聯起來。如果配合 IDE 的代碼掃描能力,當在接口中請求 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"sumNumbersInRange","attrs":{}}],"attrs":{}},{"type":"text","text":" 的時候,IDE 給出對應的廢棄提示和原因,那麼經過一段時間的迭代,標記爲 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"@deprecated","attrs":{}}],"attrs":{}},{"type":"text","text":" 的字段就會慢慢消失在產品代碼中。這會使得 API 的版本管理更加容易,也使得客戶端更加容易理解 API 的進化和向後兼容。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"而使用 REST API 的時候,在沒有這種類似機制的幫助下,我經常會使用了上游服務準備 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"deprecated","attrs":{}}],"attrs":{}},{"type":"text","text":" 的字段,因此,在這種情況下,準備廢棄的字段不得不爲了兼容而永久的保留在了服務之中。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"我見過一個 REST API,這個 API 經過好幾年的發展,每次請求都會返回 10K+ 的數據,並且裏面有很多字段的含義基本一致,只不過是有些字段爲了兼容某個特定的需求而增加。這個 API 中的字段誰也不敢動,能做的只能是隨着需求的增加而不斷的增加該 API 的規模。開發這個 API 是一件非常可怕的事情。","attrs":{}}]},{"type":"heading","attrs":{"align":null,"level":2},"content":[{"type":"text","text":"客觀看待 GraphQL 的缺點","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"新生之物,其貌必醜。雖然 GraphQL 已經發展了幾年,但是實際上而言 GraphQL 還算是一個比較新穎的技術。根據 《GraphQL in Action》 中的第 1.3 節的介紹,到目前爲止,GraphQL 仍然有很多的問題需要解決,例如:","attrs":{}}]},{"type":"bulletedlist","content":[{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"安全性問題","attrs":{}}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"緩存問題","attrs":{}}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"學習門檻問題","attrs":{}}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"……","attrs":{}}]}]}],"attrs":{}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"雖然如此,我還是認爲 GraphQL 開創了 API 交互的新天地,代表着新的技術的發展方向。並且我也發現,目前整個 GraphQL 社區也在做很多的工作來解決目前的一些問題。例如 Facebook 爲了解決緩存問題而開發的 dataloader 庫,","attrs":{}},{"type":"link","attrs":{"href":"https://www.apollographql.com/","title":null,"type":null},"content":[{"type":"text","text":"apollo graphql","attrs":{}}]},{"type":"text","text":" 在推動 GraphQL 工業落地方面所做的各種努力,……","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"正如我在 ","attrs":{}},{"type":"link","attrs":{"href":"https://wangwei1237.gitee.io/2021/05/01/how-to-improve-the-work-efficiency/","title":null,"type":null},"content":[{"type":"text","text":"如何提升工作效率","attrs":{}}]},{"type":"text","text":" 一文中說的那樣:","attrs":{}}]},{"type":"blockquote","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"我們必須認識到,短期看,新技術會解決我們當前的問題,但是新技術必然會引入新的問題,我們需要與時俱進,在新的場景下不斷解決新技術帶來的問題。這有這樣,才能不斷的提升工作效率。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"我們不能因爲堵車,就回退到自行車的時代;我們不能因爲有假幣,就回退到物物交換的時代;……","attrs":{}}]}],"attrs":{}},{"type":"heading","attrs":{"align":null,"level":2},"content":[{"type":"text","text":"是否馬上使用 GraphQL","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"即便如此,在確定是否要使用 GraphQL 技術時,需要做認真的分析,且不可爲了追新而採用 GraphQL。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"如果所提供服務的下游消費者較少並且範圍也較少,那麼還是推薦使用 REST API。但是如果提供的服務是類似 Github Open API 這樣的規模較大的服務,或者使用該服務的下游消費者範圍較大、數量較多,我認爲採用 GraphQL 確實是上上之選。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"總之,越是涉及到團隊交互多、團隊協作多的服務,我越是建議採用 GraphQL。因爲協作最複雜的地方在於 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"communication","attrs":{}}],"attrs":{}},{"type":"text","text":",而 GraphQL 就是革新多方協作的一種新技術。就像萬維網徹底改變了人類世界的交流方式一樣,我想,GraphQL 正在徹底改變 API 的交流方式,尤其是在微服務架構中。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/f7/f739f971a1af25b58c88a113e4d765e0.png","alt":"","title":null,"style":[{"key":"width","value":"75%"},{"key":"bordertype","value":"none"}],"href":null,"fromPaste":true,"pastePass":true}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"heading","attrs":{"align":null,"level":2},"content":[{"type":"text","text":"學習資源","attrs":{}}]},{"type":"bulletedlist","content":[{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"link","attrs":{"href":"https://spec.graphql.cn/","title":null,"type":null},"content":[{"type":"text","text":"https://spec.graphql.cn/","attrs":{}}]}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"link","attrs":{"href":"https://graphql.org/","title":null,"type":null},"content":[{"type":"text","text":"https://graphql.org/","attrs":{}}]},{"type":"text","text":" or ","attrs":{}},{"type":"link","attrs":{"href":"https://graphql.cn/","title":null,"type":null},"content":[{"type":"text","text":"https://graphql.cn","attrs":{}}]}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"link","attrs":{"href":"https://graphql.org/swapi-graphql/","title":null,"type":null},"content":[{"type":"text","text":"https://graphql.org/swapi-graphql/","attrs":{}}]}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"link","attrs":{"href":"https://docs.github.com/en/graphql","title":null,"type":null},"content":[{"type":"text","text":"https://docs.github.com/en/graphql","attrs":{}}]}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"link","attrs":{"href":"https://docs.github.com/en/graphql/overview/explorer","title":null,"type":null},"content":[{"type":"text","text":"https://docs.github.com/en/graphql/overview/explorer","attrs":{}}]}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"link","attrs":{"href":"https://www.apollographql.com/docs/","title":null,"type":null},"content":[{"type":"text","text":"https://www.apollographql.com/docs/","attrs":{}}]}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"link","attrs":{"href":"https://www.howtographql.com/","title":null,"type":null},"content":[{"type":"text","text":"https://www.howtographql.com","attrs":{}}]}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"link","attrs":{"href":"https://wangwei1237.gitee.io/shares/GraphQL_in_Action.pdf","title":null,"type":null},"content":[{"type":"text","text":"https://wangwei1237.gitee.io/shares/GraphQL_in_Action.pdf","attrs":{}}]}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"link","attrs":{"href":"https://github.com/graphql/graphql-playground","title":null,"type":null},"content":[{"type":"text","text":"https://github.com/graphql/graphql-playground","attrs":{}}]}]}]}],"attrs":{}},{"type":"heading","attrs":{"align":null,"level":2},"content":[{"type":"text","text":"參考文獻","attrs":{}}]},{"type":"numberedlist","attrs":{"start":null,"normalizeStart":1},"content":[{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":1,"align":null,"origin":null},"content":[{"type":"text","text":"Samer Buna. GraphQL in Action. Manning Publications, March 9, 2021. ","attrs":{}},{"type":"link","attrs":{"href":"https://wangwei1237.gitee.io/2021/06/15/Preliminary-Exploration-of-the-GraphQL/#fnref1","title":null,"type":null},"content":[{"type":"text","text":"↩︎","attrs":{}}]}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":2,"align":null,"origin":null},"content":[{"type":"link","attrs":{"href":"https://graphql.org/","title":null,"type":null},"content":[{"type":"text","text":"https://graphql.org/","attrs":{}}]},{"type":"text","text":", ","attrs":{}},{"type":"link","attrs":{"href":"https://graphql.cn/","title":null,"type":null},"content":[{"type":"text","text":"https://graphql.cn/","attrs":{}}]},{"type":"text","text":". ","attrs":{}},{"type":"link","attrs":{"href":"https://wangwei1237.gitee.io/2021/06/15/Preliminary-Exploration-of-the-GraphQL/#fnref2","title":null,"type":null},"content":[{"type":"text","text":"↩︎","attrs":{}}]}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":3,"align":null,"origin":null},"content":[{"type":"link","attrs":{"href":"https://docs.github.com/en/graphql","title":null,"type":null},"content":[{"type":"text","text":"Github Docs: GitHub GraphQL API","attrs":{}}]},{"type":"text","text":" ","attrs":{}},{"type":"link","attrs":{"href":"https://wangwei1237.gitee.io/2021/06/15/Preliminary-Exploration-of-the-GraphQL/#fnref3","title":null,"type":null},"content":[{"type":"text","text":"↩︎","attrs":{}}]}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":4,"align":null,"origin":null},"content":[{"type":"link","attrs":{"href":"https://www.apollographql.com/blog/graphql/basics/graphql-vs-rest/","title":null,"type":null},"content":[{"type":"text","text":"GraphQL vs. REST","attrs":{}}]},{"type":"text","text":". ","attrs":{}},{"type":"link","attrs":{"href":"https://wangwei1237.gitee.io/2021/06/15/Preliminary-Exploration-of-the-GraphQL/#fnref4","title":null,"type":null},"content":[{"type":"text","text":"↩︎","attrs":{}}]}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":5,"align":null,"origin":null},"content":[{"type":"link","attrs":{"href":"https://www.howtographql.com/basics/1-graphql-is-the-better-rest/","title":null,"type":null},"content":[{"type":"text","text":"GraphQL is the better REST","attrs":{}}]},{"type":"text","text":" ","attrs":{}},{"type":"link","attrs":{"href":"https://wangwei1237.gitee.io/2021/06/15/Preliminary-Exploration-of-the-GraphQL/#fnref5","title":null,"type":null},"content":[{"type":"text","text":"↩︎","attrs":{}}]}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":6,"align":null,"origin":null},"content":[{"type":"link","attrs":{"href":"https://www.prisma.io/blog/top-5-reasons-to-use-graphql-b60cfa683511","title":null,"type":null},"content":[{"type":"text","text":"Reasons to use GraphQL | Top 5 Reasons Why and How to use GraphQL","attrs":{}}]},{"type":"text","text":" ","attrs":{}},{"type":"link","attrs":{"href":"https://wangwei1237.gitee.io/2021/06/15/Preliminary-Exploration-of-the-GraphQL/#fnref6","title":null,"type":null},"content":[{"type":"text","text":"↩︎","attrs":{}}]}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":7,"align":null,"origin":null},"content":[{"type":"link","attrs":{"href":"https://www.cloudbees.com/blog/graphql-as-an-api-gateway-to-micro-services","title":null,"type":null},"content":[{"type":"text","text":"GraphQL as an API Gateway to Microservices","attrs":{}}]},{"type":"text","text":" ","attrs":{}},{"type":"link","attrs":{"href":"https://wangwei1237.gitee.io/2021/06/15/Preliminary-Exploration-of-the-GraphQL/#fnref7","title":null,"type":null},"content":[{"type":"text","text":"↩︎","attrs":{}}]}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":8,"align":null,"origin":null},"content":[{"type":"link","attrs":{"href":"https://www.infoq.com/news/2021/03/netflix-graphql-microservices/","title":null,"type":null},"content":[{"type":"text","text":"Netflix Embraces GraphQL Microservices for Rapid Application Development","attrs":{}}]},{"type":"text","text":" ","attrs":{}},{"type":"link","attrs":{"href":"https://wangwei1237.gitee.io/2021/06/15/Preliminary-Exploration-of-the-GraphQL/#fnref8","title":null,"type":null},"content":[{"type":"text","text":"↩︎","attrs":{}}]}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":9,"align":null,"origin":null},"content":[{"type":"link","attrs":{"href":"https://netflixtechblog.com/beyond-rest-1b76f7c20ef6","title":null,"type":null},"content":[{"type":"text","text":"Beyond REST——Rapid Development with GraphQL Microservices","attrs":{}}]},{"type":"text","text":" ","attrs":{}},{"type":"link","attrs":{"href":"https://wangwei1237.gitee.io/2021/06/15/Preliminary-Exploration-of-the-GraphQL/#fnref9","title":null,"type":null},"content":[{"type":"text","text":"↩︎","attrs":{}}]}]}]}]}]}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章