GraphQL:一種更高效、強大和靈活的數據提供方式

在前幾天的 《StateOfJS: 2018年JavaScript生態圈趨勢報告》一文中,咱們看到了2018年在數據層GraphQL的發展勢頭猛烈,而且大部分用戶用過都說好,但如上圖數據顯示,目前國內的使用人數還不多,大部分人連聽都沒聽過,今天小肆就爲你們介紹一下,何爲GraphQL。

一. GraphQL爲什麼會出現?

當提起API設計的時候,你們一般會想到SOAP,RESTful等設計方式,從2000年RESTful的理論被提出的時候,在業界引發了很大反響,由於這種設計理念更易於用戶的使用,因此便很快的被你們所接受。咱們知道REST是一種從服務器公開數據的流行方式。前端

當REST的概念被說起出來時,客戶端應用程序對數據的需求相對簡單,而開發的速度並無達到今天的水平。react

所以REST對於許多應用程序來講是很是適合的。然而在業務愈加複雜,客戶對系統的擴展性有了更高的要求時,API環境發生了巨大的變化。特別是從下面三個方面在挑戰api設計的方式:web

1. 移動端用戶的爆發式增加須要更高效的數據加載

Facebook開發GraphQL的最初緣由是移動用戶的增長、低功耗設備和鬆散的網絡。GraphQL最小化了須要網絡傳輸的數據量,從而極大地改善了在這些條件下運行的應用程序。數據庫

2. 各類不一樣的前端框架和平臺

前端框架和平臺運行客戶端應用程序的異構環境使得咱們在構建和維護一個符合全部需求的API變得困難,使用GraphQL每一個客戶機均可以精確地訪問它須要的數據。segmentfault

3. 在不一樣前端框架,不一樣平臺下想要加快產品快速開發變的愈來愈難

持續部署已經成爲許多公司的標準,快速的迭代和頻繁的產品更新是必不可少的。對於REST api,服務器公開數據的方式經常須要修改,以知足客戶端的特定需求和設計更改。這阻礙了快速開發實踐和產品迭代。後端

二. GraphQL官方定義:一種用於 API 的查詢語言


GraphQL 既是一種用於 API 的查詢語言也是一個知足你數據查詢的運行時。 GraphQL 對你的 API 中的數據提供了一套易於理解的完整描述,使得客戶端可以準確地得到它須要的數據,並且沒有任何冗餘,也讓 API 更容易地隨着時間推移而演進,還能用於構建強大的開發者工具。api

請求你所要的數據:很少很多

向你的 API 發出一個 GraphQL 請求就能準確得到你想要的數據,很少很多。 GraphQL 查詢老是返回可預測的結果。使用 GraphQL 的應用能夠工做得又快又穩,由於控制數據的是應用,而不是服務器。數組

獲取多個資源:只用一個請求

GraphQL 查詢不只可以得到資源的屬性,還能沿着資源間引用進一步查詢。典型的 REST API 請求多個資源時得載入多個 URL,而 GraphQL 能夠經過一次請求就獲取你應用所需的全部數據。這樣一來,即便是比較慢的移動網絡鏈接下,使用 GraphQL 的應用也能表現得足夠迅速。前端框架

描述全部的可能:類型系統

GraphQL API 基於類型和字段的方式進行組織,而非入口端點。你能夠經過一個單一入口端點獲得你全部的數據能力。GraphQL 使用類型來保證應用只請求可能的數據,還提供了清晰的輔助性錯誤信息。應用可使用類型,而避免編寫手動解析代碼。服務器

三. GraphQL和RESTful的區別

前面提到GraphQL能夠理解爲基於RESTful的一種封裝,目的在於構建使Client更加易用的服務,能夠說GraphQL是更好的RESTful設計。

在過去的十多年中,REST已經成爲設計web api的標準(雖然只是一個模糊的標準)。它提供了一些很棒的想法,好比無狀態服務器和結構化的資源訪問。

然而REST api表現得過於僵化,沒法跟上訪問它們的客戶的快速變化的需求。 GraphQL的開發是爲了應付更多的靈活性和效率,它解決了與REST api交互時開發人員所經歷的許多缺點和低效之處。

爲了說明在從API獲取數據時REST和GraphQL之間的主要區別,讓咱們考慮一個簡單的示例場景:在blog應用程序中,應用程序須要顯示特定用戶的文章的標題。同一屏幕還顯示該用戶最後3個關注者的名稱。

REST和GraphQL如何解決這種狀況?

使用REST API來現實時,咱們一般能夠經過訪問屢次請求來收集數據。

好比在這個示例中,咱們能夠經過下面的三步來實現:

  1. 經過 /user/<id>獲取初始用戶數據
  2. 經過/user/<id>/posts 返回用戶的全部帖子
  3. 請求/user/<id>/followers返回每一個用戶的關注者列表

調用關係以下圖所示:

若是用GraphQL的話,咱們只須要一次請求就能夠完成上述的需求

在GraphQL的世界裏咱們不用多取數據,也不用擔憂數據取少了,咱們只須要按需獲取便可。

REST最多見的問題之一是API的返回數據過多或者過少,這是由於客戶端下載數據的惟一方法是經過訪問返回固定數據結構的endpoint,這就會致使咱們設計API很是困難,由於它既要可以爲客戶提供精確的數據需求,又須要知足不一樣調用者的需求,這自己就是相互矛盾的。GraphQL的發明者Lee Byron提出了一個很重要的概念: 「用圖形來思考,而不是endpoint」

經過上述直觀展現咱們能夠得出一下幾點:

1. 獲取了許多多餘的數據

一般狀況下咱們在調用一個通用API接口時,客戶端獲取的信息比應用程序中實際須要的要多。例如UI須要顯示一個用戶列表,而實際上只須要使用他們的名字。在REST API中一般會調用 /user 這個endpoint,並接收一個帶有用戶數據的JSON數組。可是這個響應可能包含更多關於返回的用戶的信息,例如他們的生日或地址,而這些信息對客戶來講是無用的,由於它只須要顯示用戶的名字。

2. 獲取的數據少於Client所須要的數據

通常來講數據獲取不足意味着某個特定的endpoint沒有提供客戶端須要的足夠信息,客戶端將須要額外的請求來獲取它所須要的一切。這可能會升級到客戶端須要首先獲取列表信息,而後須要對單條數據添加一個額外的請求以獲取其餘所需的數據。

3. 前端的快速產品迭代對API有很大的挑戰

REST api的一個常見模式是根據您在應用程序內部的展示邏輯來構造endpoint,這很方便,由於它容許客戶端經過訪問相應的endpoint獲取特定視圖的全部所需信息。

這種方法的主要缺點是它不容許前端的快速迭代。對於UI所作的每個更改,如今都存在比之前更多(或更少)的數據的高風險。

所以,須要對後端進行調整,以知足新的數據需求,這會下降生產力並顯著下降將用戶反饋集成到產品中的能力。 使用GraphQL這個問題就解決了。

因爲GraphQL的靈活性,無需在服務器上額外工做就能夠在客戶端上進行更改。因爲客戶端能夠指定準確的數據需求,因此當前端的設計和數據需求發生變化時,並不須要後端API作出任何的修改就能夠知足展示層的變化。

4. Schema和類型系統的好處

GraphQL使用強大的Type System來定義API的功能。全部在API中公開的類型都是使用GraphQL schema Definition Language (SDL)在模式中編寫的。

該模式充當客戶端和服務器之間的契約,以定義客戶機如何訪問數據。 一旦定義了模式,在前端和後端工做的團隊就能夠在沒有進一步通訊的狀況下完成工做,由於他們都知道經過網絡發送的數據的確切結構。

前端團隊能夠經過mock所需的數據結構來輕鬆測試他們的應用程序。一旦後端API實現完成,就能夠對客戶端應用程序進行切換來調用實際的API獲取數據,這也可使得咱們實現更好的客戶端和服務端的分離。

四. GraphQL語法

基礎語法

其實GraphQL所須要學習的語法不多,大部分語法與咱們平時的語法一致,能夠經過官網詳細瞭解。

首先,GraphQL是一門強類型語言,因此和咱們在數據庫定義一張表同樣,咱們須要定義每個屬性的類型.以下圖所示:

下面是一個簡單的類型定義,先是定義了一個枚舉,而後咱們定義了一個類型,類型中有四個屬性:id、 name、 friends、 appearsIn,其中id和name是標量類型,而friends是一個Person類型,這是一個嵌套類型,仔細想一想應該沒什麼毛病,畢竟你的朋友和你同樣,都是人,而appearsIn是一個枚舉類型,看起來仍是很熟悉的。

enum Episode {
  NEWHOPE
  EMPIRE
  JEDI
}
type Person{
     id: ID! 
     name: String!
     friends: [Person]
     appearsIn: [Episode]! 
}

瞭解完類型,再瞭解一下Arguments和resolver,二者都是偏服務端一些,可是瞭解一下,對graphql的使用原理有進一步的認識。

對於一個Restful API來說,除了知道接口URL,咱們還須要知道接口的傳參定義,對於GraphQL其實也同樣,雖然URL只有一個,不一樣的接口經過type來區別,但傳參同Restful API同樣,體現了客戶端與服務端的交互。

好比下面,查詢的目標是id = 2的用戶,獲取他的用戶名:

Query{
    user(id: 2) {
        id
        userName
    }
}

而在服務端定義一個接口時,咱們也須要去定義入參,主要從兩個方面,一是類型,二是其是否必填,好比下面這樣:

接口定義

user: {
    type: UserType,
    args: {
      id: { type: GraphQLID }
    },
    resolve: (root, args, context, info) => {
      const { id } = args;
      return getUser(id);
    }
  }

查詢定義

上面的代碼只是定義了一個輸入屬性id,並未定義其是不是必填,因此當查詢時,若是沒有配置查詢id,查詢不會報錯,只會獲取一個爲null的空值結果。可是講道理的話,咱們但願這是一個必填項,因此咱們須要修改服務端的代碼,將id: { type: GraphQLID } 更換爲id: {type: new GraphQLNonNull(GraphQLID)},這句代碼的含義就表示id是一個類型爲ID的必填項,再次執行咱們的查詢能夠獲得下面的錯誤提示,提示id是一個必填的ID類型,同時右側也沒有獲取到爲空的查詢結果.

在講上面Arguments時候,能夠零星的看到type中有一個resolve方法,其接收root, args, context, info四個參數。

其中root表明這個type上父節點的resolve值(由於GraphQL支持嵌套查詢),args就是上面講的,context表在Resolver解析鏈中不斷傳遞的中間變量,和react的上下文類似,而info這個概念,是當前Query的AST對象,比較抽象,可是能夠經過查看info,獲取這個QUERY的編譯對象。這個方法也是後端服務編寫的重點部分,經常咱們能夠在這裏與已有的Restful API關聯起來。

核心概念

Schema能夠說是GraphQL最具核心的部分,其描述了整個接口向外暴露的形式。

像Restful API,咱們會定義一個查詢全部人的接口url定義爲:
/api/v1/user/getUsers

查詢人具體信息的接口url爲:
/api/v1/user/getUserById

新增一我的員的接口url定義爲:
/api/v1/user/createUser

這樣前端人員調用起來會很直觀。

可是graphql是徹底不同的使用方式,其向前端暴露的url就一個像/api/graphql之類的,那這麼多接口怎麼區分呢? 咱們來看看:

奧妙就是上面這張圖,一個graphql接口都有一個Schema定義。

其定義三種操做方式:query(查詢),mutation(變動)和subscription(監聽)。

再往下延伸,一個查詢中包含多個field,也就是多種不一樣的查詢,好比query user查詢人,query message查詢消息,query weather查詢天氣。

經過這些就實現了Restful API使用多個url來達到不一樣操做的效果。

總結:

今天咱們只是講了一些GraphQL的基本知識,但咱們依然能夠看出GraphQL的出現可使咱們後端API具備更大的靈活性以及擴展性,知足了不一樣client對數據的須要,大大豐富了API的數據提供的能力。

部份內容來源:https://segmentfault.com/a/11...

相關文章
相關標籤/搜索