­

新一代數據查詢語言GraphQL來啦!

  • 2019 年 12 月 5 日
  • 筆記

本文作者:IMWeb 孫世吉 原文出處:IMWeb社區 未經同意,禁止轉載

1. GraphQL來啦!

當Facebook構建移動應用的時候,它需要的是一個強大的數據獲取API:

  • 足夠強大,滿足Facebook自身複雜業務的需求;
  • 足夠簡單,對開發者和使用者來說很容易上手與使用;

GraphQL就是為了滿足這一個需求而產生的,Facebook從2012年開始完善,與2015年展開GraphQL的開源的進程,並形成一個圍繞GraphQL的社區

GraphQL每天都為Fackbook接收、處理著上百億的請求,為Fackbook提供了強大的基礎數據平台的支援,滋養大量業務與產品。

2. 為什麼是GraphQL?

回到2012年,Fackbook開始重構他們的本地移動應用。當時他們的iOS和android移動應用實際上是就是他們Web應用的內容再加上一個本地瀏覽器的殼。這看上去給他們帶來了「一次開發,多端應用」的好處,但是隨著Fackbook移動應用越來越複雜,這樣的做法直接帶來了極差的性能和時常發生的程式崩潰,所以他們開始開發真正的本地應用。

而當時 Facebook 現有的伺服器主要功能還是只提供 HTML ,數據介面並不能直接復用,服務模式就是請求一個 URL ,返回一堆 HTML。而本地移動應用,為了給應用提供需要的數據,填充數據模型,顯示視圖,要解決的問題是怎麼去請求,準備,傳遞這些數據。

Facebook考量了兩種實現方案,包括RESTful服務資源FQL表

  • RESTful:對於Facebook這種複雜的應用,可能需要定義很多端點,這些數據介面可能只是返回欄位有所不同,造成重複工作,同時難以表達複雜的邏輯;
  • FQL:FQL是Facebook類似於SQL的API,它功能強大、格式明確,但是查詢的語言非常難以理解,例如一些數據表JOIN等操作。

Facebook工程師們希望能夠在移動應用和服務端的查詢達到一致,最後使用的模型可以類似於NSObjects或者JSON那樣的結構。

幾個工程師開始了現在的 GraphQL,一種用對象,屬性來表示數據關係,有點像圖形的方式來表達想要的數據。所以最終GraphQL給了產品設計人員和開發人員重新思考移動應用數據獲取的機會,它將開發的重點轉移到了客戶端應用中,這裡是設計師和開發人員最關注的地方。

3. 什麼是GraphQL?

GraphQL是一種API查詢語言,是一個對自定義類型系統執行查詢的服務端運行環境

一個GraphQL查詢是一個被發往服務端的字元串,該查詢在服務端被解釋和執行後返回JSON數據給客戶端。

3.1 定義數據模型

首先讓人一目了然的是GraphQL查詢可以直接映射到返回的數據,它們的結構非常相似。這帶來個好處就是你很簡單就從查詢預測到即將返回的數據,相反的知道需要的數據也可以很簡單寫出相應的查詢語句。更重要的是,這使得GraphQL更加容易學習和應用。

// 下面是一個簡單的GraphQL查詢,獲取id為1001的用戶的名字和頭像  {    user (id: 1001){      name,      photo    }  }
// 對應的結果  {      "user": {          "name": "shiji",          "photo": "https://ss1.baidu.com/6ONXsjip0QIZ8tyhnq/it/u=2639867671,3554518423&fm=58"      }  }

3.2 分層的

GraphQL另外一個重要的方面就是它自然而成的分層結構。GraphQL很自然的以對象和屬性來表示數據之間的關係,這也是GraphQL的命名由來。 你可以簡單的把GraphQL查詢看成三部分。

  • 由{}包裹的對象
  • 對象由屬性列表組成
  • ()包裹的查詢條件
// 獲取用戶資訊的同時獲取該位用戶的朋友資訊列表,包括姓名、性別和地址資訊  {    user (id: 1001){ // 第一層      name,      photo,      age,      friends {  // 第二層          name,          sex,          addr {  // 第三層              country,              city          }      }    }  }

3.3 強類型

每一級GraphQL查詢都關聯著一個特殊的類型,而每一種類型都描述了一組可用的欄位集合。 GraphQL服務通過定義類型和屬性來創建,然後為在這些類型上的每個屬性創建函數。跟SQL類似,這使得GraphQL在執行查詢之前可以提供描述性的錯誤資訊。

// 對應上一個GraphQL查詢,GraphQL 服務需要建立以下自定義類型  type Query {      user: User  }  type User {      name: String,      photo: String,      age: Integer,      addr: Address,      friends: [User]  }  type Address {      country: String,      city: String  }

GraphQL 的類型系統分為標量類型(Scalar Types,標量類型)和其他高級數據類型,標量類型即可以表示最細粒度數據結構的數據類型,可以和 JavaScript的原始類型對應。 GraphQL 規範目前規定支援的標量類型有:

  • Int : 整數,對應JavaScript的Number
  • Float:浮點數,對應JavaScript的Number
  • String:字元串,對應Javascript的String
  • Boolean: 布爾值,對應JavaScript的Boolean
  • ID:序列化後唯一的字元串,對應JavaScript的Symbol

高級數據類型包括:ObjectInterfaceUnionEnumInput ObjectListNon-Null 這裡不做詳述,請參考官方指引 Schemas and Types

3.4 協議而非存儲

GraphQL並不直接提供後端存儲的能力,它不綁定任何的資料庫或者存儲引擎,它可以利用你已有的程式碼和技術來進行數據源管理。當然這對改造現有的業務會帶來相應的成本。 也就是說GraphQL提供給你了組織與管理數據源的能力,但是數據具體是存在文件系統還是資料庫它並不關注。

3.5 自檢性

一個GraphQL服務可以直接查詢出它所支援的類型,也就是說你不需要花時間寫API文檔,也不需要花時間理解API。GraphQL直接幫助開發者快速學習和探索API。

// GraphQL查詢  {      __schema {          queryType {              name,              fields {                  name              }          }      }  }
// 查詢結果  {      "data": {          "__schema": {              "queryType": {                  "name": "User",                  "fields": [{                      "name": "name"                  },{                      "name": "photo"                  },{                      "name": "age"                  },{                      "name": "addr"                  },{                      "name": "friends"                  }]              }          }      }  }

每個 GraphQL 根域都會自動加上一個 __schema 域,這個域有一個子域叫 queryType。我們可以通過查詢這些域來了解 GraphQL 伺服器支援那些查詢

3.6 無需版本的

返回數據的模型完全由客戶端的查詢決定,所以服務端變得更簡單、更容易一般化。 當你添加新的產品功能時,額外的欄位可以被添加到服務中,同時並不會影響到現有的業務;當你淘汰老功能的時候,遺棄對應的服務欄位依舊可以繼續工作。這種漸進式、向後兼容的過程去除了遞增版本號的需要。在Fackbook中使用相同版本的GraphQL API 支援了跨域三年的Fackbook應用。

4. GraphQL vs RESTful

之前談過Fackbook不使用RESTful自研發GraphQL的原因,這裡再詳細講一下。 RESTful API的問題在於: 1、缺乏可拓展性。 一個剛開始簡單的用戶介面可能只返回少部分資訊,例如用戶名、頭像等。隨著API的不斷發展,可能需要返回更多的資訊,例如年齡、昵稱、簽名等。很多時候客戶端只是需要其中的部分資訊,但是介面依舊傳輸了所有的資訊,這個情況增加了網路傳輸量,特別對於移動應用來說特別不友好,同時需要客戶端自行提取需要的數據。而建立兩個功能大致相同只是返回欄位有所區別的API則增加了後端實現的複雜度,或者是需要增加業務邏輯判斷,或者是增加了維護的難度。

2、複雜的數據需求需要做多次API調用。 例如客戶端要顯示文章的內容,可能要調用文章介面、評論介面、用戶資訊介面。為構成對一個資源的完整視圖,需要做多次單獨調用,這樣的數據獲取方式非常不靈活。

而GraphQL給客戶端帶來了自主選擇的權利。

  • RESTful:服務端決定有哪些數據獲取方式,客戶端只能挑選使用,如果數據過於冗餘也只能默默接收再對數據進行處理;而數據不能滿足需求則需要請求更多的介面。
  • GraphQL:給客戶端自主選擇數據內容的能力,客戶端完全自主決定獲取資訊的內容,服務端負責精確的返回目標數據

舉個例子:我們要獲取指定id的文章相關資訊,包括標題、作者、發布時間以及前兩條評論;同時載入當前用戶資訊。 RESTful:

// 兩趟查詢,難以拓展  GET /user/111  GET /article/1001?comment=2

GraphQL:

// 一趟查詢,易於擴展  {    article (id: 1001){      title,      author,      time,      comments (first: 2)        nickname,        time,        content      }    },    user (id: 111){      nickname,      photo,      sign    }  }

獲取的數據結果如下所示,就是這麼簡單粗暴的完成了API訂製。 我們不僅按照我們的需求完成數據過濾,同時僅僅使用一次請求就獲取了所有你想要的數據。

{      "article": {          "title": "新一代API查詢語言GraphQL",          "author": "shiji",          "time": 1481127981218,          "comments": [{              "nickname": "狗剩子",              "time": 1481127981218,              "content": "樓主寫的真好"          }, {              "nickname": "不明真相的吃瓜群眾",              "time": 1481127981218,              "content": "這瓜真好吃"          }]      },      "user": {          "nickname": "shiji",          "photo": "https://ss1.baidu.com/6ONXsjip0QIZ8tyhnq/it/u=2639867671,3554518423&fm=58",          "sign": "我的地盤我做主——動感地帶"      }  }

當然GraphQL給伺服器端程式碼帶來了不公平的額外複雜度和管理,GraphQL非常適合於客戶對底層數據具有複雜規模的需求,客戶端完全可以自主訂製個性化API。

5. GraphQL存在的問題

1、改造成本,要使用GraphQL對數據源進行管理,相當於要對整個服務端進行一次換血。你需要考慮的不僅僅是需要針對現有數據源建立一套GraphQL的類型系統,同時需要改造服務端暴露數據的方式,這對業務久遠的產品無疑是一場災難,讓人望而卻步。 2、實踐方案,GraphQL在前端如何直接與視圖層、狀態管理方案結合,目前也只有React/Relay這個官方方案。也就是說,如果你不是使用Node+React這個技術棧,引入GraphQL看上去帶來了額外的成本和風險。 3、查詢性能,GraphQL查詢的每個欄位如果都有自己的resolve方法,可能導致一次查詢操作對資料庫跑了大量了query,資料庫里一趟select+join就能完成的事情在這裡看來會產生大量的資料庫查詢操作,雖然網路層面的請求數被優化了,但是資料庫查詢可能會成為性能瓶頸。

6. GraphQL安全性?

或許有人有疑問,感覺 GraphQL 把我所擁有的資源全部都暴露了,別人不只一覽全局,能夠了解你所有的數據結構,而且還能一次把所有數據都拉取下來,這也太可怕了! 事實上,GraphQL 提供的資源不一定要和你資料庫一樣,因為它只是扮演中間層的角色,雖然也可能很像。所以,你完全可以控制你期望暴露給用戶的資源。

7. 推薦閱讀

教程: 官方教程

簡單應用: 官方Quick Start 搭建一個簡單的GraphQL服務 GitHub GraphQL API

其他: GitHub 16年9月開放新的GraphQL API From RESTful to GraphQL 響應式GraphQL結構