首發於衆成翻譯前端
Coursera 的客戶端開發人員鍾情於 GraphQL 的靈活性,類型安全性和良好的社區支持,咱們對 GraphQL 的喜好衆~所~周~知。然而,咱們並無過多討論後端開發人員是如何看待 GraphQL 的,由於他們大多數實際上並不須要考慮 GraphQL。git
在過去一年中,咱們構建了一系列的工具來將全部的 REST API 轉換爲 GraphQL,在咱們的後端開發人員繼續編寫他們熟悉的 API 的同時,讓客戶端開發人員能夠經過 GraphQL 訪問全部數據。github
在這篇文章中,咱們將介紹一下咱們引入 GraphQL 的歷程,而且會着重介紹咱們實踐過程當中成功和失敗的點。後端
Coursera 使用 REST API 構建基於資源的 API(好比課程 API,教師 API,成績 API等)。這些都很容易進行構建和測試,而且對後端提供了很好的關注點分離。然而,隨着咱們的產品規模和 API 數量的增加,咱們開始面對許多關於性能,文檔和通用性的問題。在許多頁面上,咱們不得不執行四五次後端請求來獲取渲染頁面須要的數據。api
我還記得當 Facebook 首次推出 GraphQL 時,咱們團隊都興奮不已——咱們立即意識到 GraphQL 能夠解決咱們的不少問題,讓咱們能夠在單次的請求中獲取全部數據,併爲咱們的 API 提供結構化文檔。然而,儘管咱們很想開始爲全部資源編寫 GraphQL,再也不在客戶端上使用 REST,但這不切實際,由於:安全
通過一番調研,咱們找到了一個很好的方式來幫助咱們使用 GraphQL——咱們決定在咱們的 REST API 之上添加一個 GraphQL 代理層。這種方式實際上很常見,而且~都~記錄~詳實,因此這裏我再也不贅述。服務器
封裝 REST API 的過程很簡單——咱們構建了一些實用程序來執行下游的 REST 請求,從而在解析器中獲取數據,並制定了一些關於如何將現有模型轉換爲 GraphQL 的規則。網絡
首先,咱們構建了少許的 GraphQL 解析器,而後在生產環境中啓動一個 GraphQL 服務器,以調用下游 REST 接口請求咱們的資源。一旦咱們完成了這項工做(使用 GraphiQL 驗證全部內容),咱們就會在提早準備的演示頁面上展現這些數據,幾天以內 GraphQL 就能暫時調用成功了。架構
若是我從這個項目中獲得什麼教訓,那就是不要高興得太早了。app
咱們的 GraphQL 服務器完美工做了好幾天。可是忽然之間,就在咱們即將給團隊演示這個 demo 以前,每個 GraphQL 查詢都開始執行失敗。這讓咱們沒有一點點防備,由於上一次確認它能正常工做以後,咱們並未對 GraphQL 服務器作過任何更改。
通過一番調查,咱們才發現因爲一個無關的 bug,致使咱們下游的課程目錄服務接口被回滾到了之前的版本,而咱們在 GraphQL 服務中構建的 schema 如今已經不一樣步了。咱們本能夠手動更新 schema 並修復咱們的 demo,可是咱們很快意識到,因爲咱們的 GraphQL schema 擴展了1,000多個不一樣的資源,由50多個服務提供支持,手動同步全部的更新是不可能的。 若是你在微服務架構中有多個數據源,那麼問題就在於它們什麼時候同步,而不是是否會同步。
因此咱們從頭開始,試圖找到一個更精確的方案來實現單一數據源——咱們將 REST API 視爲數據源是有依據的,由於咱們的 GraphQL 的 schema 是基於它們構建的。爲此,咱們須要自動且準確地構建咱們的 GraphQL 層,以正確反映當前運行在咱們的架構中的業務資源,而不是咱們以前作的那樣。
幸運的是(或許還帶有一點遠見),咱們的 REST 框架能給咱們創建自動化層所需的一切:
一旦咱們發現不一樣步的地方,就會觸發構建一個 GraphQL schema,咱們在 GraphQL 服務器上設置了一個定時任務,每五分鐘 ping 一次下游服務,並請求全部資源信息。 而後,咱們就能夠在 Pegasus Schemas 和 GraphQL 類型之間編寫1:1轉換層。
接下來,咱們利用以前解析器的大部分邏輯,簡單地定義了 GraphQL 查詢和 REST 請求之間的轉換,而且可以生成一個功能完善的 GraphQL 服務器,時間不超過五分鐘。
咱們採用 GraphQL 的主要緣由之一就是但願能在單次服務器往返中獲取咱們的頁面須要的全部數據。可是,咱們最初的方案僅提供了 REST API 返回的模型與 GraphQL 返回的模型之間的一對一映射。這樣並無將咱們的資源真正地連接在一塊兒,咱們仍然會使用盡量多的 GraphQL 查詢來獲取數據,就像使用 REST API 同樣。儘管使用 GraphQL 替代 REST 獲取用戶數據能帶來極致的開發體驗,但若是在獲取更多數據以前必須等待前一個查詢返回,實際上並不會得到性能的提高。
咱們的 REST API 每個都相互獨立 ——它們不須要知道任何其餘 API 的存在。然而,使用 GraphQL,則模型和資源之間須要相互關聯。
自動創建資源之間的連接並不可行,因此咱們定義了一個簡單的註解,開發人員能夠添加資源來指定它們之間的關係。例如,一個課程資源應該有一個教師字段表明教授該門課程的教師。爲了獲取這些數據,咱們能夠經過 id 來查詢教師信息,這裏的 id 可使用課程中已經提供的 InstructorIds 字段。咱們將其稱爲「向前關聯」,由於咱們能夠經過 id 獲取的數據知道確切的教師信息。
當咱們想要從某個資源跳轉到另外一個沒有明確連接的資源的狀況下,咱們增長了經過反向查詢獲取數據的功能——例如,經過課程信息獲取用戶的註冊信息,咱們能夠調用 byCourseId
來查找 userEnrollments.v1
的資源,這將會在指定的課程資源中返回匹配的用戶註冊數據。
代碼的語法如這般:
courseAPI.addRelation( "instructors" -> ReverseRelation( resourceName = "instructors.v1", finderName = "byCourseId", arguments = Map("courseId" -> "$id", "version" -> "$version"))
一旦這些聯繫就位,咱們的 GraphQL schema 就會開始合併在一塊兒——它們並不是是能經過 GraphQL 獲取的多塊小數據,而是由全部 Coursera 的數據和資源組成的網絡。
咱們的 GraphQL 服務器已經在 Coursera 生產環境上運行了6個多月 ,儘管並不是一路順風,但咱們切身感覺到了 GraphQL 帶來的諸多好處。得益於 GraphQL 額外提供的類型安全檢查,開發人員更容易檢測數據和編寫查詢,咱們的站點更加可靠,而且使用 GraphQL 加載數據的頁面運行得更快。
還有比較重要的一點,遷移到 GraphQL 的過程並不會大幅下降開發人員的生產力。儘管咱們的前端開發人員不得不學習如何使用 GraphQL,但咱們不須要重寫任何後端 API 或運行復雜的遷移流程才能使用 GraphQL——開發人員建立新的項目能夠直接使用。
總的來講,咱們很高興 GraphQL 爲咱們的開發人員(也是咱們的終極用戶)提供了很大的幫助,而且對 GraphQL 生態的將來感到很是興奮。