在前幾天的《StateOfJS: 2018年JavaScript生態(tài)圈趨勢報告》一文中命爬,我們看到了2018年在數據層GraphQL的發(fā)展勢頭猛烈施敢,并且大部分用戶用過都說好周荐,但如上圖數據顯示狭莱,目前國內的使用人數還很少,大部分人連聽都沒聽過概作,今天小肆就為大家介紹一下腋妙,何為GraphQL。
一. GraphQL為何會出現讯榕?
當提起API設計的時候骤素,大家通常會想到SOAP,RESTful等設計方式愚屁,從2000年RESTful的理論被提出的時候济竹,在業(yè)界引起了很大反響,因為這種設計理念更易于用戶的使用霎槐,所以便很快的被大家所接受送浊。我們知道REST是一種從服務器公開數據的流行方式。
當REST的概念被提及出來時栽燕,客戶端應用程序對數據的需求相對簡單罕袋,而開發(fā)的速度并沒有達到今天的水平改淑。
因此REST對于許多應用程序來說是非常適合的碍岔。然而在業(yè)務越發(fā)復雜,客戶對系統(tǒng)的擴展性有了更高的要求時朵夏,API環(huán)境發(fā)生了巨大的變化蔼啦。特別是從下面三個方面在挑戰(zhàn)api設計的方式:
1. 移動端用戶的爆發(fā)式增長需要更高效的數據加載
Facebook開發(fā)GraphQL的最初原因是移動用戶的增加、低功耗設備和松散的網絡仰猖。GraphQL最小化了需要網絡傳輸的數據量捏肢,從而極大地改善了在這些條件下運行的應用程序。
2. 各種不同的前端框架和平臺
前端框架和平臺運行客戶端應用程序的異構環(huán)境使得我們在構建和維護一個符合所有需求的API變得困難饥侵,使用GraphQL每個客戶機都可以精確地訪問它需要的數據鸵赫。
3. 在不同前端框架,不同平臺下想要加快產品快速開發(fā)變的越來越難
持續(xù)部署已經成為許多公司的標準躏升,快速的迭代和頻繁的產品更新是必不可少的辩棒。對于REST api,服務器公開數據的方式常常需要修改膨疏,以滿足客戶端的特定需求和設計更改一睁。這阻礙了快速開發(fā)實踐和產品迭代。
二. GraphQL官方定義:一種用于 API 的查詢語言
GraphQL 既是一種用于 API 的查詢語言也是一個滿足你數據查詢的運行時佃却。 GraphQL 對你的 API 中的數據提供了一套易于理解的完整描述者吁,使得客戶端能夠準確地獲得它需要的數據,而且沒有任何冗余饲帅,也讓 API 更容易地隨著時間推移而演進复凳,還能用于構建強大的開發(fā)者工具瘤泪。
請求你所要的數據:不多不少
向你的 API 發(fā)出一個 GraphQL 請求就能準確獲得你想要的數據,不多不少育八。 GraphQL 查詢總是返回可預測的結果均芽。使用 GraphQL 的應用可以工作得又快又穩(wěn),因為控制數據的是應用单鹿,而不是服務器掀宋。
獲取多個資源:只用一個請求
GraphQL 查詢不僅能夠獲得資源的屬性,還能沿著資源間引用進一步查詢仲锄。典型的 REST API 請求多個資源時得載入多個 URL劲妙,而 GraphQL 可以通過一次請求就獲取你應用所需的所有數據。這樣一來儒喊,即使是比較慢的移動網絡連接下镣奋,使用 GraphQL 的應用也能表現得足夠迅速。
描述所有的可能:類型系統(tǒng)
GraphQL API 基于類型和字段的方式進行組織怀愧,而非入口端點侨颈。你可以通過一個單一入口端點得到你所有的數據能力。GraphQL 使用類型來保證應用只請求可能的數據芯义,還提供了清晰的輔助性錯誤信息哈垢。應用可以使用類型,而避免編寫手動解析代碼扛拨。
三. GraphQL和RESTful的區(qū)別
前面提到GraphQL可以理解為基于RESTful的一種封裝耘分,目的在于構建使Client更加易用的服務,可以說GraphQL是更好的RESTful設計绑警。
在過去的十多年中求泰,REST已經成為設計web api的標準(雖然只是一個模糊的標準)。它提供了一些很棒的想法计盒,比如無狀態(tài)服務器和結構化的資源訪問渴频。
然而REST api表現得過于僵化,無法跟上訪問它們的客戶的快速變化的需求北启。 GraphQL的開發(fā)是為了應付更多的靈活性和效率卜朗,它解決了與REST api交互時開發(fā)人員所經歷的許多缺點和低效之處。
為了說明在從API獲取數據時REST和GraphQL之間的主要區(qū)別暖庄,讓我們考慮一個簡單的示例場景:在blog應用程序中聊替,應用程序需要顯示特定用戶的文章的標題。同一屏幕還顯示該用戶最后3個關注者的名稱培廓。
REST和GraphQL如何解決這種情況?
使用REST API來現實時惹悄,我們通常可以通過訪問多次請求來收集數據肩钠。
比如在這個示例中泣港,我們可以通過下面的三步來實現:
通過
/user/<id>
獲取初始用戶數據通過
/user/<id>/posts
返回用戶的所有帖子請求
/user/<id>/followers
返回每個用戶的關注者列表
調用關系如下圖所示:
如果用GraphQL的話暂殖,我們只需要一次請求就可以完成上述的需求
在GraphQL的世界里我們不用多取數據,也不用擔心數據取少了当纱,我們只需要按需獲取即可呛每。
REST最常見的問題之一是API的返回數據過多或者過少,這是因為客戶端下載數據的唯一方法是通過訪問返回固定數據結構的endpoint坡氯,這就會導致我們設計API非常困難晨横,因為它既要能夠為客戶提供精確的數據需求,又需要滿足不同調用者的需求箫柳,這本身就是相互矛盾的手形。GraphQL的發(fā)明者Lee Byron提出了一個很重要的概念: “用圖形來思考,而不是endpoint”
通過上述直觀展示我們可以得出一下幾點:
1. 獲取了許多多余的數據
通常情況下我們在調用一個通用API接口時悯恍,客戶端獲取的信息比應用程序中實際需要的要多库糠。例如UI需要顯示一個用戶列表,而實際上只需要使用他們的名字涮毫。在REST API中通常會調用 /user 這個endpoint瞬欧,并接收一個帶有用戶數據的JSON數組。但是這個響應可能包含更多關于返回的用戶的信息罢防,例如他們的生日或地址枯途,而這些信息對客戶來說是無用的粥诫,因為它只需要顯示用戶的名字瑞信。
2. 獲取的數據少于Client所需要的數據
一般來說數據獲取不足意味著某個特定的endpoint沒有提供客戶端需要的足夠信息逆航,客戶端將需要額外的請求來獲取它所需要的一切。這可能會升級到客戶端需要首先獲取列表信息渤滞,然后需要對單條數據添加一個額外的請求以獲取其他所需的數據。
3. 前端的快速產品迭代對API有很大的挑戰(zhàn)
REST api的一個常見模式是根據您在應用程序內部的展現邏輯來構造endpoint榴嗅,這很方便妄呕,因為它允許客戶端通過訪問相應的endpoint獲取特定視圖的所有所需信息。
這種方法的主要缺點是它不允許前端的快速迭代嗽测。對于UI所做的每一個更改绪励,現在都存在比以前更多(或更少)的數據的高風險。
因此唠粥,需要對后端進行調整疏魏,以滿足新的數據需求,這會降低生產力并顯著降低將用戶反饋集成到產品中的能力晤愧。 使用GraphQL這個問題就解決了大莫。
由于GraphQL的靈活性,無需在服務器上額外工作就可以在客戶端上進行更改官份。由于客戶端可以指定準確的數據需求只厘,所以當前端的設計和數據需求發(fā)生變化時烙丛,并不需要后端API做出任何的修改就可以滿足展現層的變化。
4. Schema和類型系統(tǒng)的好處
GraphQL使用強大的Type System來定義API的功能羔味。所有在API中公開的類型都是使用GraphQL schema Definition Language (SDL)在模式中編寫的河咽。
該模式充當客戶端和服務器之間的契約,以定義客戶機如何訪問數據赋元。 一旦定義了模式忘蟹,在前端和后端工作的團隊就可以在沒有進一步通信的情況下完成工作,因為他們都知道通過網絡發(fā)送的數據的確切結構搁凸。
前端團隊可以通過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來區(qū)別湾盒,但傳參同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的必填項揽浙,再次執(zhí)行我們的查詢可以得到下面的錯誤提示状婶,提示id是一個必填的ID類型,同時右側也沒有獲取到為空的查詢結果.
在講上面Arguments時候馅巷,可以零星的看到type中有一個resolve方法膛虫,其接收root, args, context, info四個參數。
其中root代表這個type上父節(jié)點的resolve值(因為GraphQL支持嵌套查詢)令杈,args就是上面講的走敌,context表在Resolver解析鏈中不斷傳遞的中間變量,和react的上下文相似,而info這個概念逗噩,是當前Query的AST對象,比較抽象跌榔,但是可以通過查看info,獲取這個QUERY的編譯對象异雁。這個方法也是后端服務編寫的重點部分,常常我們可以在這里與已有的Restful API關聯(lián)起來僧须。
核心概念
Schema可以說是GraphQL最具核心的部分纲刀,其描述了整個接口向外暴露的形式。
像Restful API担平,我們會定義一個查詢所有人的接口url定義為:
/api/v1/user/getUsers
查詢人具體信息的接口url為:
/api/v1/user/getUserById
新增一個人員的接口url定義為:
/api/v1/user/createUser
這樣前端人員調用起來會很直觀示绊。
但是graphql是完全不一樣的使用方式锭部,其向前端暴露的url就一個像/api/graphql
之類的,那這么多接口怎么區(qū)分呢面褐? 我們來看看:
奧妙就是上面這張圖拌禾,一個graphql接口都有一個Schema定義。
其定義三種操作方式:query(查詢),mutation(變更)和subscription(監(jiān)聽)展哭。
再往下延伸湃窍,一個查詢中包含多個field,也就是多種不同的查詢匪傍,比如query user查詢人您市,query message查詢消息,query weather查詢天氣役衡。
通過這些就實現了Restful API使用多個url來達到不同操作的效果茵休。
總結:
今天我們只是講了一些GraphQL的基本知識,但我們依然可以看出GraphQL的出現可以使我們后端API具有更大的靈活性以及擴展性手蝎,滿足了不同client對數據的需要榕莺,大大豐富了API的數據提供的能力。