新一代數據查詢語言GraphQL來啦!
1. GraphQL來啦!
當非死book構建移動應用的時候,它需要的是一個強大的數據獲取API:
- 足夠強大,滿足非死book自身復雜業務的需求;
- 足夠簡單,對開發者和使用者來說很容易上手與使用;
GraphQL就是為了滿足這一個需求而產生的,非死book從2012年開始完善,與2015年展開GraphQL的開源的進程,并形成一個 圍繞GraphQL的社區 。
GraphQL每天都為Fackbook接收、處理著上百億的請求,為Fackbook提供了強大的基礎數據平臺的支持,滋養大量業務與產品。
2. 為什么是GraphQL?
回到2012年,Fackbook開始重構他們的本地移動應用。當時他們的iOS和android移動應用實際上是就是他們Web應用的內容再加上一個本地瀏覽器的殼。這看上去給他們帶來了“ 一次開發,多端應用 ”的好處,但是隨著Fackbook移動應用越來越復雜,這樣的做法直接帶來了極差的性能和時常發生的程序崩潰,所以他們開始開發真正的本地應用。
而當時 非死book 現有的服務器主要功能還是只提供 HTML ,數據接口并不能直接復用,服務模式就是請求一個 URL ,返回一堆 HTML。而本地移動應用,為了給應用提供需要的數據,填充數據模型,顯示視圖,要解決的問題是怎么去請求,準備,傳遞這些數據。
非死book考量了兩種實現方案,包括 RESTful服務資源 和 FQL表 。
- RESTful:對于非死book這種復雜的應用,可能需要定義很多端點,這些數據接口可能只是返回字段有所不同,造成重復工作,同時難以表達復雜的邏輯;
- FQL:FQL是非死book類似于SQL的API,它功能強大、格式明確,但是查詢的語言非常難以理解,例如一些數據表JOIN等操作。
非死book工程師們希望能夠在移動應用和服務端的查詢達到一致,最后使用的模型可以類似于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
高級數據類型包括: Object 、 Interface 、 Union 、 Enum 、 Input Object 、 List 、 Non-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 提供的資源不一定要和你數據庫一樣,因為它只是扮演 中間層 的角色,雖然也可能很像。所以,你完全可以控制你期望暴露給用戶的資源。
來自:http://imweb.io/topic/58499c299be501ba17b10a9e