天天看點

帶你讀《GraphQL學習指南》之一:歡迎來到GraphQL的世界第1章

點選檢視第二章 點選檢視第三章

GraphQL學習指南

帶你讀《GraphQL學習指南》之一:歡迎來到GraphQL的世界第1章

[美] Eve Porcello

Alex Banks 著

郭笑鵬 譯

機械工業出版社

第1章

歡迎來到GraphQL的世界

在被英國女王冊封為爵士前,蒂姆 "伯納斯 "李(Tim Berners-Lee,英國計算機科學家,網際網路之父)同你我一樣也是一名程式員。他曾就職于CERN(European Organization for Nuclear Research,歐洲核子研究組織)位于瑞典的歐洲粒子實體實驗室,與一大批才華橫溢的研究人員共事。在工作生涯中,伯納斯想要幫助同僚們分享彼此的想法,是以他決定建立一個網絡,通過它科學家們可以釋出和更新最新的資訊。這個項目最終發展成了第一個Web伺服器和第一個Web用戶端,并于1990年12月由CERN(

https://www.w3.org/People/Berners-Lee/Longer.html

)正式推出,這便是我們耳熟能詳的“WorldWideWeb”(網際網路)浏覽器(後更名為“Nexus”)。

通過這個項目,伯納斯使研究人員們能夠通過自己的電腦檢視并更新網頁内容。 “WorldWideWeb”由HTML、URL、浏覽器以及一個所見即所得(WYSIWYG,What You See Is What You Get )的界面組成,便于使用者更新浏覽内容。

現如今,網際網路早已超出了通過浏覽器擷取資訊這一傳統概念。它既是筆記本電腦,也是智能手機、智能手表,還是公交卡中的射頻識别(RFID,Radio Frequency Identification)晶片,甚至可以成為管家機器人在你離家時幫你簽收快遞。

現如今,客戶終端的數量跟以往相比早已不可同日而語,但我們所做的事情卻始終如一:加載資料,越快越好。程式員們不斷地改善着自己應用的性能,而原因卻再簡單不過—因為使用者需要。使用者認為我們的應用應該在任何條件下都能正常運作,他們可不管是2G功能機還是光纖網。速度就是唯一的準繩。應用越快通路網站的使用者就越多,應用越快使用者體驗就越好,應用越快我們就越賺錢。

精準而快速地從伺服器端擷取資料是網際網路永恒的主題。盡管本書會去研究一些過去的事情,但此時此刻我們要談論的是現代解決方案。我們談談未來,我們談談GraphQL。

GraphQL是什麼

GraphQL(

http://www.graphql.cn

)是一種API查詢語言,也是一種用于實作資料查詢的運作時(runtime)。GraphQL服務與傳輸方式沒有關系,但通常基于HTTP協定。

為了示範GraphQL查詢和傳回的資料,我們調用星球大戰資料API(

https://graphql.org/swapi-gaphql/

)。該API是GraphQL封裝的表述性狀态傳遞方式(Representational State Transfer,即RESTful)API。我們就用它來發送查詢并接收資料。

圖1-1是GraphQL的查詢流程示例。左側為我們想要查詢的資料。我們請求的是萊娅公主(Leia Organa)的資料,并且指定限定條件(personID:5)。接下來指定我們想要的三個資料字段:name、birthYear和created。右側為我們獲得的響應結果:資料以JSON格式傳回,并且隻包含我們所需要的資料。

帶你讀《GraphQL學習指南》之一:歡迎來到GraphQL的世界第1章

接下來我們來調整一下查詢字段,不必擔心,查詢是互動式的。我們改變之後立刻就能看到新的結果。如圖1-2所示,我們添加字段了filmConnection,并指定了次級字段,進而獲得了萊娅公主登場的所有電影的名字。

在執行查詢時,根據嵌套的查詢字段可以周遊相關對象。這樣我們就可以查詢兩種不同的資料類型而隻發出一個HTTP請求。不必在多個對象之間來回查找資料,因為我們不需要的資料根本不會傳回。使用GraphQL,使用者隻需一次請求就能擷取所需的所有資料。

每當我們向GraphQL伺服器請求資料時,它會自動通過類型檢測系統進行驗證。每個GraphQL伺服器都會在其GraphQL Schema中定義資料類型。可以将類型檢測系統看作是API資料的架構,并自定義資料對象的内容。舉例來說,之前我們傳回的Person對象大體如下:

type Person {

id: ID!
name: String
birthYear: String
eyeColor: String
gender: String
hairColor: String
height: Int
mass: Float
skinColor: String
homeworld: Planet
species: Species
filmConnection: PersonFilmsConnection
starshipConnection: PersonStarshipConnection
vehicleConnection: PersonVehiclesConnection
created: String
edited: String           

}

帶你讀《GraphQL學習指南》之一:歡迎來到GraphQL的世界第1章

Person類型包括了萊娅公主可查詢的所有字段及其類型。第3章将深入研究GraphQL的類型檢測系統和模闆。

GraphQL通常被稱為聲明式資料擷取語言。簡而言之,開發人員可根據他們需要的資料來列出請求的資料字段,而無須關注如何獲得資料。許多語言都有其對應的GraphQL服務端庫,包括C#、Clojure、Elixir、Erlang、Go、Groovy、Java、JavaScript、.NET、PHP、Python、Scala和Ruby注1。

本書将重點介紹如何使用JavaScript來建構GraphQL服務。同時書中讨論的所有GraphQL技術也同樣适用于其他語言。

GraphQL規範

GraphQL是用戶端和伺服器之間通信的規範。俗話說無規矩不成方圓,規範描述的是語言的功能和特性。有了規範才能為社群和論壇提供使用該語言的通用詞彙和最佳實踐,讓廣大程式員也為之受益。

業内最耳熟能詳的規範要數ECMAScript規範了。每隔一段時間,來自浏覽器公司、技術公司和廣大社群的代表們便濟濟一堂,對ECMAScript規範進行修訂。GraphQL亦是如此,同樣有一群愛好者聚在一起制定GraphQL的實作标準,以飧後來者。

規範釋出後,GraphQL的作者們還分享了在JavaScript環境下部署GraphQL伺服器的實作方案--graphql.js(

https://github.com/graphql/graphql-js

)。該方案作為推廣GraphQL的先導性庫有其積極意義,但并不意味着強制規定使用哪種語言來實作服務。它僅僅是一個向導。在了解了查詢語言和類型檢測系統之後,可以使用任何喜歡的語言來建構伺服器。

那麼如果規範和實作方案出現了分歧該如何解決,規範裡面又是如何解釋的呢?實際上,規範描述的是編寫查詢字段時應該使用的語言和文法,規定了類型檢測系統以及該系統的執行和驗證引擎。除此之外,規範并不進行強制性規定。它沒有規定使用何種語言、如何存儲資料或支援哪些客戶。誠然查詢語言有其指導原則,但項目的實際設計則完全取決于你自己(如果你想探究整個過程,可以浏覽文檔:

https://facebook.github.io/graphql/

)。

GraphQL的設計原則

盡管GraphQL不會幹涉你建構API的過程,但其确實針對如何考慮服務提供了一些指導方針:

分層

GraphQL查詢字段層次分明。字段嵌套在其他字段當中,查詢字段的結構與其傳回的資料結構相似。

以産品為中心

GraphQL由兩大要素驅動,即用戶端所需的資料,以及用戶端支援的語言和運作時。

強類型

GraphQL伺服器由GraphQL類型檢測系統提供支援。每個資料點在模闆當中都有其特定的類型,并且均會進行驗證。

用戶端指定查詢

GraphQL伺服器提供功能供用戶端使用。

類型自查

GraphQL語言能夠查詢GraphQL伺服器的類型檢測系統。

現在我們已經對GraphQL規範有了初步了解,接下來詳述建立它的原因。

GraphQL的起源

時間回到2012年,那時Facebook決定重新建構原生移動應用。起因是公司的iOS和Android應用尚停留在圍繞移動網站視圖進行簡單包裝的階段。他們也有自己的RESTful伺服器和SQL資料庫,可惜性能不佳,應用時不時就崩一下展示存在感。正因如此,工程師們意識到他們需要改進将資料發送到用戶端應用的方式。

由李 "拜倫(Lee Byron)、尼克 "施洛克(Nick Schrock)和丹 "查菲爾(Dan Schafer)組成的團隊決定從用戶端角度重新考慮他們的資料。以此為契機,他們着手建構一種查詢語言,它可以描述其所需資料模型的功能和需求,為公司的用戶端/服務端提供便利。

2015年7月,該團隊釋出了最初的GraphQL規範以及基于JavaScript的實作庫graphql.js。2016年9月,在Facebook内部使用了許多年後,GraphQL脫離了“技術預覽”階段,這宣告GraphQL正式進入生産環境。如今,GraphQL在幾乎所有的Facebook資料擷取方面發揮着不可替代的作用,并被IBM、Intuit、Airbnb等公司廣泛采用。

資料傳輸的曆史

回顧資料傳輸的曆史,将會幫助我們更深刻地了解GraphQL呈現出的一些新想法。我們考慮資料傳輸時,總是試圖搞清楚計算機之間如何來回傳遞資料。普遍的想法是,我們向遠端伺服器發送請求然後伺服器傳回響應資料。

遠端過程調用

遠端過程調用(remote procedure call,RPC)發明于20世紀60年代。遠端過程調用由用戶端發起,向遠端計算機發起請求以執行某些操作。接下來,遠端計算機則向用戶端發送響應。那個年代的計算機雖然與今天所使用的不可同日而語,但是資訊傳輸過程卻基本相同,即用戶端請求,伺服器響應。

簡單對象通路協定

20世紀90年代末,微軟提出了簡單對象通路協定(Simple Object Access Protocol,SOAP)。SOAP使用XML将消息編碼并通過HTTP傳輸,內建了類型檢測系統,并引入了面向資源的資料調用概念。SOAP所提供的結果具有可預測性,可惜的是由于其實作過于複雜而導緻失敗的機率也很高。

表述性狀态傳遞(REST)

今天各位最熟悉的API範例就是表述性狀态傳遞(Representational State Transfer,REST)。REST是羅伊 "菲爾丁(Roy Fielding)于2000年在其博士論文中提出來的一種軟體架構風格。REST描述了一種面向資源的架構,使用者可以通過執行GET、POST、PUT和DELETE等操作來浏覽Web資源。他把資源網絡看成虛拟狀态機,并且使用者執行的動作(GET、POST、PUT、DELETE)都是機器内的狀态變化。站在今天的視角可能會覺得這是理所當然的,但在那個年代,這無疑是個劃時代的想法。

在RESTful架構中,路由代表了資訊。舉例來說,從以下這些路由請求資訊将産生特定的響應:

/api/food/hot-dog/

api/sport/skiing/

api/city/Lisbon

REST使我們可以利用各種接口建立資料模型,明顯優于之前的架構。它提供了在日益複雜的Web中處理資料的新方法,且沒有強制規定資料響應格式。起初,REST是同XML一起使用的。AJAX最開始是Asynchronous JavaScript And XML的首字母縮寫,因為Ajax請求的響應資料被格式化為XML(現在Ajax已經是一個獨立的單詞了)。這讓前端開發者很困惑:在JavaScript中使用資料前還要解析XML響應。

沒過多久,道格拉斯 "克羅克福德(Douglas Crockford)開發出了JSON(JavaScript Object Notation)并将其标準化。JSON和語言無關,它提供的是一種優雅的資料格式,許多不同的語言都可以解析和使用它。緊接着,克羅克福德撰寫了著名的《JavaScript: The Good Parts》,充分展示了JSON的優點。

RESTful API的影響不言而喻。所有的API開發者都從中獲益。以至于有一些愛好者去和别人争論什麼是RESTful,哪些不是RESTful,這些人被稱為“REST黨”(RESTafarians)。好了,既然RESTful API如此成功,那麼拜倫他們緣何還要另起爐竈呢?這就要從REST的缺點說起了。

REST的缺點

GraphQL最初釋出的時候,一些人将其吹捧為REST的替代品。“REST已經涼了啊!”早期的GraphQL使用者一邊四處宣揚,一邊慫恿别人把他們的RESTful API全部停用。如果在部落格或者論壇上說這種話倒沒什麼,但是如此簡單粗暴地将GraphQL描述為REST殺手未免有失偏頗。更為真實的原因是,随着網絡的發展,REST在某些條件下已經顯示出其局限性。GraphQL想要做的就是改善這一切。

過量擷取

假設我們正在建構一個應用,使用的資料來自星球大戰接口的RESTful API。首先,我們需要加載一些與角色編号1号和盧克 "天行者(Luke Skywalker)相關的資料。注4使用GET請求通路

https://swapi.co/api/people/1/

,得到的JSON資料如下:

帶你讀《GraphQL學習指南》之一:歡迎來到GraphQL的世界第1章
帶你讀《GraphQL學習指南》之一:歡迎來到GraphQL的世界第1章

傳回的資料太多了,遠遠超過了我們的需求。實際上我們隻需要姓名、身高和體重的資訊:

帶你讀《GraphQL學習指南》之一:歡迎來到GraphQL的世界第1章

這個例子充分展示了何為擷取過量—我們獲得了大量不需要的資料。用戶端僅需要三個字段,而我們卻傳回了一個擁有16個鍵的對象,這樣一來完全用不到的資訊也通過網絡進行發送,無形之中造成了浪費。

在GraphQL中,該請求該如何顯示呢?我們仍舊期望取回Luke Skywalker的名字、身高和體重,如圖1-3所示。

帶你讀《GraphQL學習指南》之一:歡迎來到GraphQL的世界第1章

左側是我們發出的GraphQL查詢字段。我們隻請求所需的字段。右側是我們收到的一個JSON響應,這次隻有我們所請求的資料,沒有額外的13個字段,無形中節省了從基站到手機的流量。隻傳回我們需要的資料,不多不少,響應更快也是理所當然的。

擷取不足

項目經理剛剛在我們的辦公桌旁重新整理了一下,他想給星球大戰應用添加一項功能。除了姓名、身高和體重外,還加上了盧克 "天行者所有參演電影的片段清單。這樣一來,在我們從上個接口請求資料後則不得不調用其他接口請求資料,這使我們“捉襟見肘”。

為了擷取每部電影的名字,我們需要從電影數組中的每個路由擷取資料:

帶你讀《GraphQL學習指南》之一:歡迎來到GraphQL的世界第1章

擷取這些資料需要向之前的接口(

)發出一個請求,然後再向每一部電影發出5個請求。對于每一部電影,我們所請求到的都是一個大對象,可我們隻需要其中一個值。

帶你讀《GraphQL學習指南》之一:歡迎來到GraphQL的世界第1章
帶你讀《GraphQL學習指南》之一:歡迎來到GraphQL的世界第1章

如果我們想要列出這部電影中的角色,就不得不發出更多的請求。這樣一來,我們恐怕得再調用16次接口,資料不停地在用戶端和伺服器端之間往複,并且每個HTTP請求都會占用用戶端資源并取回大量的無用資料,其結果就是降低了使用者體驗。倘若使用者的網速或裝置較慢,那麼可能什麼内容都看不到。

GraphQL的解決方案是定義一個嵌套的查詢結構,然後一次取回所有請求資料,如圖1-4所示。

帶你讀《GraphQL學習指南》之一:歡迎來到GraphQL的世界第1章

采用GraphQL,我們僅需一次請求便可獲得所需的資料,同時傳回資料的結構和我們的查詢結構相符合。

管理REST接口

另一個和RESTful API相關的抱怨是缺乏靈活性。有時候用戶端的需求發生了變化,導緻我們不得不建立新的接口。

按照這個星球大戰RESTful API來說,我們不得不向數個路由送出請求。大型應用通常使用自定義接口來盡可能減少HTTP請求。你可能會突然發現接口文檔多了一個接口,類似于/api/character-with-movie-title。這種開發方式恐怕會拖慢開發速度,因為設定新的接口通常意味着前後端團隊需要開會來溝通。

GraphQL将傳統的架構內建于一個接口。該接口可以充當網關并協調多個資料源,而且隻有一個接口讓資料結構更為簡便。

在讨論REST的缺點時,值得注意的是好多公司同時使用了GraphQL和REST。設定GraphQL接口從RESTful API擷取資料是使用GraphQL的最有效方法。也許這正是在你的公司裡逐漸推廣GraphQL的好辦法。

現實世界中的GraphQL

許多公司都使用GraphQL為他們的應用、網站和API提供支援。GitHub是世界上最早采用GraphQL的著名公司之一。它的REST API經曆了三次疊代,并在公共API第4版中使用了GraphQL。就像網站上所說的那樣,GitHub發現“能夠精确定義你想要的資料(且隻有你想要的資料)比第3版的RESTful API要強大太多”。

其他公司,如《紐約時報》、IBM、Twitter和Yelp等也推崇GraphQL。他們的團隊成員經常在會議上宣傳GraphQL的優點。

目前至少有3個專門的GraphQL會議:在舊金山舉辦的GraphQL峰會(GraphQL Summit)、赫爾辛基舉辦的GraphQL芬蘭峰會(GraphQL Finland)和在柏林舉辦的GraphQL歐洲峰會(GraphQL Europe)。這些社群通過本地聚會和線上會議共同推進GraphQL的發展。

GraphQL用戶端庫

正如之前我們曾多次說過的,GraphQL隻是一個規範。它并不關心你所用的是React、Vue或原生JavaScript甚至是浏覽器。GraphQL隻是對一些特定的問題有一些指導意見,但除此之外,項目架構的一切都由你自己決定。是以一些超出規範的GraphQL用戶端庫也像雨後春筍一般被開發出來。讓我們來看一看。

GraphQL用戶端庫的出現加速了開發團隊的工作流程,提高了應用的效率和性能。它們能處理諸如網絡請求、資料緩存以及将資料注入使用者界面等任務。當下有許多GraphQL用戶端庫,其中的佼佼者當屬Relay(

https://facebook.github.io/relay/

)和Apollo(

https://www.apollographql.com/

Relay是Facebook開發的用戶端庫,搭配React和React Native一起使用。Relay旨在成為React元件和從GraphQL伺服器擷取資料之間的紐帶,現已被Facebook、GitHub、Twitch等公司廣泛使用。

Apollo是由Meteor開發組開發的,并由社群驅動的用戶端庫,旨在圍繞GraphQL建構更全面的工具庫。Apollo支援所有的主流前端開發平台,并且與架構無關。Apollo還開發了一些工具,可以協助建構GraphQL服務、改善後端性能以及對GraphQL API進行性能監控。Airbnb、CNBC、《紐約時報》和Ticketmaster等公司也在其産品中使用了Apollo用戶端庫。

GraphQL的生态系統很龐大并且還在不斷演進中,不過好消息是它的規範相當穩定。在接下來的章節中,我們将讨論如何編寫模闆和建立GraphQL服務端。在此過程中,網址

https://github.com/moonhighway/learning-graphql/

中有一些資料可以為你提供幫助。在這裡你可以按章節找到有用的連結、示例和所有的項目檔案。

在我們深入研究如何使用GraphQL之前,先來談談圖論和GraphQL中那些奇思妙想的由來。

繼續閱讀