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

RobbieCck 8年前發布 | 43K 次閱讀 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

 

 本文由用戶 RobbieCck 自行上傳分享,僅供網友學習交流。所有權歸原作者,若您的權利被侵害,請聯系管理員。
 轉載本站原創文章,請注明出處,并保留原始鏈接、圖片水印。
 本站是一個以用戶分享為主的開源技術平臺,歡迎各類分享!