日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問(wèn) 生活随笔!

生活随笔

當(dāng)前位置: 首頁(yè) > 编程资源 > 编程问答 >内容正文

编程问答

为什么说 GraphQL 可以取代 REST API?

發(fā)布時(shí)間:2024/1/17 编程问答 30 豆豆
生活随笔 收集整理的這篇文章主要介紹了 为什么说 GraphQL 可以取代 REST API? 小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

幾年前,我在 DocuSign 帶領(lǐng)了一個(gè)開(kāi)發(fā)團(tuán)隊(duì),任務(wù)是重寫(xiě)一個(gè)有數(shù)千萬(wàn)個(gè)用戶在使用的 Web 應(yīng)用程序。當(dāng)時(shí)還沒(méi)有可以支持前端的 API,因?yàn)閺囊婚_(kāi)始,Web 應(yīng)用程序就是一個(gè).NET 大單體。西雅圖的 API 團(tuán)隊(duì)在將拆分單體,并逐步暴露出 RESTful API。這個(gè) API 團(tuán)隊(duì)由兩名工程師組成,發(fā)布周期為一個(gè)月,而我們?cè)谂f金山的前端團(tuán)隊(duì)每周都會(huì)發(fā)布新版本。

API 團(tuán)隊(duì)的發(fā)布周期太長(zhǎng),因?yàn)楹芏?#xff08;幾乎所有)功能都必須進(jìn)行手動(dòng)測(cè)試,這是可以理解的。它畢竟是一個(gè)單體,而且沒(méi)有適當(dāng)?shù)淖詣?dòng)化測(cè)試——如果他們修改了一個(gè)地方,不知道在應(yīng)用程序的其他地方會(huì)出現(xiàn)什么問(wèn)題。

我記得有一次,我們的前端團(tuán)隊(duì)面臨為某大會(huì)交付新版本的壓力,但我們忘記跟進(jìn)一個(gè)重要的 API 變更,這個(gè)變更未被包含在即將發(fā)布的 API 版本中。我們要么一直等待,直到錯(cuò)過(guò)截止日期,要么有人愿意放棄優(yōu)先權(quán),以便讓我們的變更包括在即將發(fā)布的版本中。所幸的是,這個(gè)變更最后被包含在新版本中,我們也及時(shí)發(fā)布了新的前端版本。我真的希望當(dāng)時(shí)我們已經(jīng)使用了 GraphQL,因?yàn)樗梢韵龑?duì)外部團(tuán)隊(duì)及其發(fā)布周期的重度依賴。

在這篇文章中,我將介紹 GraphQL 的優(yōu)勢(shì),以及為什么它會(huì)變得如此受歡迎。

很多公司已經(jīng)在內(nèi)部從 RESTful 轉(zhuǎn)向了 GraphQL API:IBM、Twitter、Walmart Labs、紐約時(shí)報(bào)、Intuit、Coursera,等等。

其他一些公司不僅是在內(nèi)部而且還將外部 API 也轉(zhuǎn)為 GraphQL:AWS、Yelp、GitHub、Facebook 和 Shopify,等等。GitHub 甚至打算停止使用 REST API,他們的 v4 版本只使用 GraphQL。

GraphQL 究竟是一個(gè)炒作流行語(yǔ)還是真正會(huì)帶來(lái)一場(chǎng)變革?有趣的是,我之前列出的大多數(shù)從 GraphQL 獲益的公司都有以下這些共同點(diǎn)。

  • 他們擁有包括移動(dòng)端在內(nèi)的多個(gè)客戶端;

  • 他們正在轉(zhuǎn)向或者已經(jīng)采用了微服務(wù)架構(gòu);

  • 他們的遺留 REST API 數(shù)量暴增,變得十分復(fù)雜;

  • 他們希望消除客戶端團(tuán)隊(duì)對(duì) API 團(tuán)隊(duì)的依賴;

  • 他們注重良好的 API 文檔和開(kāi)發(fā)者體驗(yàn)。

GitHub 工程團(tuán)隊(duì)表明了他們的動(dòng)機(jī):

“GraphQL 彌合了發(fā)布的內(nèi)容與可以使用的內(nèi)容之間的差距。我們真的很期待能夠同時(shí)發(fā)布它們。GraphQL 代表了 API 開(kāi)發(fā)的巨大飛躍。類型安全、內(nèi)省、生成文檔和可預(yù)測(cè)的響應(yīng)都為我們平臺(tái)的維護(hù)者和消費(fèi)者帶來(lái)了好處。我們期待著由 GraphQL 提供支持的平臺(tái)進(jìn)入新時(shí)代,也希望你們也這樣做!”

GraphQL 加速了開(kāi)發(fā)速度,提升了開(kāi)發(fā)者體驗(yàn),并提供了更好的工具。我并不是說(shuō)這絕對(duì)是這樣的,但我會(huì)盡力說(shuō)明 GraphQL 與 REST 之間的爭(zhēng)論點(diǎn)及其原因。

超級(jí)數(shù)據(jù)聚合器

我是 Indeed(世界排名第一的求職網(wǎng)站)的軟件工程負(fù)責(zé)人,所以讓我們先來(lái)看看 Indeed.com 的主頁(yè)和職位查詢結(jié)果頁(yè)面。它們分別發(fā)出了 10 和 11 個(gè) XHR 請(qǐng)求。

需要注意的是,在 REST 中使用 POST 進(jìn)行頁(yè)面瀏覽并不是很“正規(guī)”。

以下是其中的一些調(diào)用:

  • GET?https://inbox.indeed.com/api/getConversationCount

  • GET?https://www.indeed.com/rpc/jobdescs

  • GET?https://www.indeed.com/rpc/vjslog

  • GET?https://www.indeed.com/rpc/preccount

  • POST?https://www.indeed.com/rpc/jobalert

  • POST?https://www.indeed.com/rpc/count

在使用 GraphQL 時(shí),上面的這些請(qǐng)求可以被包含在單個(gè)查詢和單個(gè)請(qǐng)求中。

復(fù)制代碼

?

query HomePage {

?

? getConversationCount(...) {

?

? ? ?...

?

? }

?

? jobdescs(...) {

?

? ? ?...

?

? }

?

? vjslog(...) {

?

? ? ?...

?

? }

?

? preccount(...) {

?

? ? ?…

?

? }

?

? jobalert(...) {

?

? ? ?…

?

? }

?

? count(...) {

?

? ? ?…

?

? }

?

}

響應(yīng)結(jié)果可能是這樣的:

復(fù)制代碼

?

{

?

? "data": {

?

? ? "getConversationCount": [

?

? ? ? {

?

? ? ? ? ...

?

? ? ? }

?

? ? ],

?

? ? "vjslog": [...],

?

? ? "preccount": [...],

?

? ? ? "jobalert": [...],

?

? ? "count": {}

?

? },

?

? "errors": []

?

}

通常,單個(gè)調(diào)用比多個(gè)調(diào)用更方便、更有效,因?yàn)樗枰俚拇a和更少的網(wǎng)絡(luò)開(kāi)銷。來(lái)自 PayPal 過(guò)程團(tuán)隊(duì)的開(kāi)發(fā)體驗(yàn)還證實(shí),很多 UI 工作實(shí)際上不是 UI 工作,而是其他任務(wù),例如前端和后端之間的通信:

“我們發(fā)現(xiàn),UI 開(kāi)發(fā)人員實(shí)際用于構(gòu)建 UI 的時(shí)間不到三分之一,剩下的時(shí)間用于確定在何處以及如何獲取數(shù)據(jù)、過(guò)濾 / 映射數(shù)據(jù)以及編排 API 調(diào)用,還有一些用于構(gòu)建和部署。”

需要注意的是,有實(shí)時(shí)使多個(gè)請(qǐng)求也是有必要的,例如多個(gè)單獨(dú)的請(qǐng)求可以快速且異步獨(dú)立地獲取不同的數(shù)據(jù),如果采用了微服務(wù)架構(gòu),它們會(huì)增加部署靈活性,而且它們的故障點(diǎn)是多個(gè),而不是一個(gè)。

此外,如果頁(yè)面是由多個(gè)團(tuán)隊(duì)開(kāi)發(fā)的,GraphQL 提供了一個(gè)功能,可以將查詢分解稱為片段。稍后我們將詳細(xì)介紹這方面的內(nèi)容。

從更大的角度來(lái)看,GraphQL API 的主要應(yīng)用場(chǎng)景是 API 網(wǎng)關(guān),在客戶端和服務(wù)之間提供了一個(gè)抽象層。

微服務(wù)架構(gòu)很好,但也存在一些問(wèn)題,GraphQL 可以用來(lái)解決這些問(wèn)題。以下是來(lái)自 IBM 在微服務(wù)架構(gòu)中使用 GraphQL 的經(jīng)驗(yàn):

“總的來(lái)說(shuō),GraphQL 微服務(wù)的開(kāi)發(fā)和部署都非???。他們 5 月份開(kāi)始開(kāi)發(fā),7 月份就進(jìn)入了生產(chǎn)環(huán)境。因?yàn)樗麄儾恍枰鞯迷S可,直接開(kāi)干。他強(qiáng)烈推薦這個(gè)方案,比開(kāi)會(huì)討論好太多了。”

接下來(lái),讓我們逐一討論 GraphQL 的每一個(gè)好處。

提高開(kāi)發(fā)速度

首先,GraphQL 有助于減少發(fā)出的請(qǐng)求數(shù)。通過(guò)單個(gè)調(diào)用來(lái)獲取所需的數(shù)據(jù)比使用多個(gè)請(qǐng)求要容易得多。從工程師的角度來(lái)看,這加快了開(kāi)發(fā)速度。后面我會(huì)解釋更多有關(guān)為什么會(huì)提升開(kāi)發(fā)速度的原因,但現(xiàn)在我想先說(shuō)明另一個(gè)問(wèn)題。

后端和客戶端團(tuán)隊(duì)需要通過(guò)密切合作來(lái)定義 API、測(cè)試它們,并做出更改。前端、移動(dòng)、物聯(lián)網(wǎng)(例如 Alexa)等客戶端團(tuán)隊(duì)不斷迭代功能,并嘗試使用新的 UX 和設(shè)計(jì)。他們的數(shù)據(jù)需求經(jīng)常發(fā)生變化,后端團(tuán)隊(duì)必須跟上他們的節(jié)奏。如果客戶端和后端代碼由同一團(tuán)隊(duì)負(fù)責(zé),那么問(wèn)題就沒(méi)那么嚴(yán)重了。Indeed 的大多數(shù)工程團(tuán)隊(duì)都是由全棧工程師組成,但并非全部都是這樣。對(duì)于非全棧團(tuán)隊(duì),客戶端團(tuán)隊(duì)經(jīng)常因?yàn)橐蕾嚵撕蠖藞F(tuán)隊(duì)開(kāi)發(fā)速度受到影響。

當(dāng)我轉(zhuǎn)到 Job Seeker API 團(tuán)隊(duì)時(shí),移動(dòng)團(tuán)隊(duì)開(kāi)始我們的開(kāi)發(fā)進(jìn)度。我們之間有很多關(guān)于參數(shù)、響應(yīng)字段和測(cè)試的事情需要溝通。

在使用了 GraphQL 之后,客戶端工程師就可以完全控制前端,不需要依賴任何人,因?yàn)樗麄兛梢愿嬖V后端他們需要什么以及響應(yīng)結(jié)構(gòu)應(yīng)該是怎樣的。他們使用了 GraphQL 查詢,它們會(huì)告訴后端 API 應(yīng)該要提供哪些數(shù)據(jù)。

客戶端工程師不需要花時(shí)間讓后端 API 團(tuán)隊(duì)添加或修改某些內(nèi)容。GraphQL 具有自文檔的特點(diǎn),所以可以節(jié)省一些用于查找文檔以便了解如何使用 API 的時(shí)間。我相信大多數(shù)人曾經(jīng)在找出確切的請(qǐng)求參數(shù)方面浪費(fèi)了很多時(shí)間。GraphQL 協(xié)議本身及其社區(qū)在文檔方面為我們提供了一些有用的工具。在某些情況下,可以從模式自動(dòng)生成文檔。其他時(shí)候,只需使用 GraphiQL Web 界面就足以編寫(xiě)一個(gè)查詢。

來(lái)自紐約時(shí)報(bào)的工程師表示,他們?cè)谵D(zhuǎn)到 GraphQL 和 Relay 之后,在做出變更時(shí)不需要改太多的東西:

“當(dāng)我們想要更新所有產(chǎn)品的設(shè)計(jì)時(shí),不再需要修改多個(gè)代碼庫(kù)。這就是我們想要的。我們認(rèn)為 Relay 和 GraphQL 是幫助我們實(shí)現(xiàn)這個(gè)偉大目標(biāo)的完美工具?!?/p>

當(dāng)一家公司已經(jīng)擁有大量 GraphQL API,然后有人想出了一個(gè)新的產(chǎn)品創(chuàng)意,這也是我最喜歡 GraphQL 的應(yīng)用場(chǎng)景。使用已有的 GraphQL API 實(shí)現(xiàn)原型比調(diào)用各種 REST 端點(diǎn)(將提供太少或太多的數(shù)據(jù))或?yàn)樾聭?yīng)用程序構(gòu)建新的 REST API 要快得多。

開(kāi)發(fā)速度的提升與開(kāi)發(fā)者體驗(yàn)的提升密切相關(guān)。

提升開(kāi)發(fā)者體驗(yàn)

GraphQL 提供了更好的開(kāi)發(fā)者體驗(yàn)(DX),開(kāi)發(fā)者將花更少的時(shí)間思考如何獲取數(shù)據(jù)。在使用 Apollo 時(shí),他們只需要在 UI 中聲明數(shù)據(jù)。數(shù)據(jù)和 UI 放在一起,閱讀代碼和編寫(xiě)代碼都變得更方便。

通常,在開(kāi)發(fā) UI 時(shí)需要在 UI 模板、客戶端代碼和 UI 樣式之間跳轉(zhuǎn)。GraphQL 允許工程師在客戶端開(kāi)發(fā) UI,減少摩擦,因?yàn)楣こ處熢谔砑踊蛐薷拇a時(shí)無(wú)需在文件之間切換。如果你熟悉 React,這里有一個(gè)很好的比喻:GraphQL 之于數(shù)據(jù),就像 React 之于 UI。

下面是一個(gè)簡(jiǎn)單的示例,UI 中直接包含了屬性名稱launch.name和?launch.rocket.name?。

復(fù)制代碼

?

const GET_LAUNCHES = gql`

?

query launchList($after: String) {

?

launches(after: $after) {

?

launches {

?

id

?

name

?

isBooked

?

rocket {

?

id

?

name

?

}

?

}

?

}

?

}

?

`;

??
?

export default function Launches() {

?

return (

?

<Query query={GET_LAUNCHES}>

?

{({ data, loading, error }) => {

?

if (loading) return <Loading />;

?

if (error) return <p>ERROR</p>;

??
?

return (

?

<div>

?

{data.launches.launches.map(launch => (

?

<div

?

key={launch.id}

?

>{launch.name}<br/>

?

Rocket: {launch.rocket.name}

?

</div>

?

))}

?

</div>

?

);

?

}}

?

</Query>

?

);

?

};

使用這種方法,可以非常容易地修改或向 UI 或查詢(gql)添加新字段。React 組件的可移植性更強(qiáng)了,因?yàn)樗鼈兠枋隽怂璧乃袛?shù)據(jù)。

如前所述, GraphQL 提供了更好的文檔,而且還有一個(gè)叫作 GraphiQL 的 IDE:

前端工程師很喜歡 GraphiQL,下面引用 Indeed 的一位高級(jí)工程師說(shuō)過(guò)的話:

“我認(rèn)為開(kāi)發(fā)體驗(yàn)中最好的部分是能夠使用 GraphiQL。對(duì)我來(lái)說(shuō),與典型的 API 文檔相比,這是一種編寫(xiě)查詢更有效的輔助方法”。

GraphQL 的另一個(gè)很棒的功能是片段,因?yàn)樗试S我們?cè)诟叩慕M件層面重用查詢。

這些功能改善了開(kāi)發(fā)者體驗(yàn),讓開(kāi)發(fā)人員更快樂(lè),更不容易出現(xiàn) JavaScript 疲勞。

提升性能

工程師并不是唯一從 GraphQL 中受益的人。用戶也會(huì)從中受益,因?yàn)閼?yīng)用程序的性能獲得了提升(可以感知到的):

1. 減少了有效載荷(客戶端只需要必要的東西);

2. 多個(gè)請(qǐng)求合并為一個(gè)請(qǐng)求可減少網(wǎng)絡(luò)開(kāi)銷;

3. 使用工具可以更輕松地實(shí)現(xiàn)客戶端緩存和后端批處理和后端緩存;
4. 預(yù)取;
5. 更快的 UI 更新。

PayPal 使用 GraphQL 重新設(shè)計(jì)了他們的結(jié)賬流程。下面是來(lái)自用戶的反饋:

“REST 的原則并沒(méi)有為 Web 和移動(dòng)應(yīng)用及其用戶的需求考慮,這個(gè)在結(jié)賬優(yōu)化交易中體現(xiàn)得尤為明顯。用戶希望能夠盡快完成結(jié)賬,如果應(yīng)用程序使用了很多原子 REST API,就需要在客戶端和服務(wù)器之間進(jìn)行多次往返以獲取數(shù)據(jù)。我們的結(jié)賬每次往返網(wǎng)絡(luò)時(shí)間至少需要 700 毫秒,這還不包括服務(wù)器處理請(qǐng)求的時(shí)間。每次往返都會(huì)導(dǎo)致渲染變慢,用戶體驗(yàn)不好,結(jié)算轉(zhuǎn)換率也會(huì)降低。”

性能改進(jìn)中有一項(xiàng)是“多個(gè)請(qǐng)求組合成一個(gè)請(qǐng)求可以減少網(wǎng)絡(luò)開(kāi)銷”。對(duì)于 HTTP/1 而言,這是非常正確的,因?yàn)樗鼪](méi)有 HTTP/2 那樣的多路復(fù)用機(jī)制。但盡管 HTTP/2 提供的多路復(fù)用機(jī)制有助于優(yōu)化單獨(dú)的請(qǐng)求,但它對(duì)于圖遍歷(獲取相關(guān)或嵌套對(duì)象)并沒(méi)有實(shí)際幫助。讓我們來(lái)看一看 REST 和 GraphQL 是如何處理嵌套對(duì)象和其他復(fù)雜請(qǐng)求的。

標(biāo)準(zhǔn)化和簡(jiǎn)化復(fù)雜的 API

通常,客戶端會(huì)發(fā)出復(fù)雜的請(qǐng)求來(lái)獲取有序、排好序、被過(guò)濾過(guò)的數(shù)據(jù)或子集(用于分頁(yè)),或者請(qǐng)求嵌套對(duì)象。GraphQL 支持嵌套數(shù)據(jù)和其他難以使用標(biāo)準(zhǔn) REST API 資源(也叫端點(diǎn)或路由)實(shí)現(xiàn)的查詢。

例如,我們假設(shè)有三種資源:用戶、訂閱和簡(jiǎn)歷。工程師需要按順序進(jìn)行兩次單獨(dú)的調(diào)用(這會(huì)降低性能)來(lái)獲取一個(gè)用戶簡(jiǎn)歷,首先需要通過(guò)調(diào)用獲取用戶資源,拿到簡(jiǎn)歷 ID,然后再使用簡(jiǎn)歷 ID 來(lái)獲取簡(jiǎn)歷數(shù)據(jù)。對(duì)于訂閱來(lái)說(shuō)也是一樣的。

1.GET /users/123:響應(yīng)中包含了簡(jiǎn)歷 ID 和工作崗位通知訂閱的 ID 清單;
2.GET /resumes/ABC:響應(yīng)中包含了簡(jiǎn)歷文本——依賴第一個(gè)請(qǐng)求;
3.GET /subscriptions/XYZ:響應(yīng)中包含了工作崗位通知的內(nèi)容和地址——依賴第一個(gè)請(qǐng)求。

上面的示例很糟糕,原因有很多:客戶端可能會(huì)獲得太多數(shù)據(jù),并且必須等待相關(guān)的請(qǐng)求完成了以后才能繼續(xù)。此外,客戶端需要實(shí)現(xiàn)如何獲取子資源(例如建立或訂閱)和過(guò)濾。

想象一下,一個(gè)客戶端可能只需要第一個(gè)訂閱的內(nèi)容和地址以及簡(jiǎn)歷中的當(dāng)前職位,另一個(gè)客戶端可能需要所有訂閱和整個(gè)簡(jiǎn)歷列表。所以,如果使用 REST API,對(duì)第一個(gè)客戶端來(lái)說(shuō)有點(diǎn)不劃算。

另一個(gè)例子:用戶表里可能會(huì)有用戶的名字和姓氏、電子郵件、簡(jiǎn)歷、地址、電話、社會(huì)保障號(hào)、密碼(當(dāng)然是經(jīng)過(guò)混淆的)和其他私人信息。并非每個(gè)客戶端都需要所有字段,有些應(yīng)用程序可能只需要用戶電子郵件,所以向這些應(yīng)用程序發(fā)送社會(huì)保障號(hào)等信息就不太安全。

當(dāng)然,為每個(gè)客戶端創(chuàng)建不同的端點(diǎn)也是不可行的,例如 /api/v1/users 和 /api/v1/usersMobile。事實(shí)上,各種客戶端通常都有不同的數(shù)據(jù)需求:/api/v1/userPublic、/api/v1/userByName、/api/v1/usersForAdmin,如果這樣的話,端點(diǎn)會(huì)呈指數(shù)級(jí)增長(zhǎng)。

GraphQL 允許客戶要求 API 發(fā)送他們想要的字段,這將使后端工作變得更加容易:/api/gql——所有客戶端只需要這個(gè)端點(diǎn)。

注意:對(duì)于 REST 和 GraphQL,后端都需要使用訪問(wèn)控制級(jí)別。

或者可以使用舊 REST 來(lái)實(shí)現(xiàn) GraphQL 的很多功能。但是這樣要付出什么代價(jià)?后端可以支持復(fù)雜的 RESTful 請(qǐng)求,這樣客戶端就可以使用字段和嵌套對(duì)象進(jìn)行調(diào)用:

復(fù)制代碼

?

GET /users/?fields=name,address&include=resumes,subscriptions

上面的請(qǐng)求將比使用多個(gè) REST 請(qǐng)求更好,但它不是標(biāo)準(zhǔn)化的,不受客戶端庫(kù)支持,而且這樣的代碼也更難編寫(xiě)和維護(hù)。對(duì)于相對(duì)復(fù)雜的 API,工程師需要在查詢中使用自己的查詢字符串參數(shù)約定,最終得到類似 GraphQL 的東西。既然 GraphQL 已經(jīng)提供了標(biāo)準(zhǔn)和庫(kù),為什么還要基于 REST 設(shè)計(jì)自己的查詢約定呢?

將復(fù)雜的 REST 端點(diǎn)與以下的 GraphQL 嵌套查詢進(jìn)行對(duì)比,嵌套查詢使用了更多的過(guò)濾條件,例如“只要給我前 X 個(gè)對(duì)象”和“按時(shí)間按升序排列”(可以添加無(wú)限制的過(guò)濾選項(xiàng)):

復(fù)制代碼

?

{

?

user (id: 123) {

?

id

?

firstName

?

lastName

?

address {

?

city

?

country

?

zip

?

}

?

resumes (first: 1, orderBy: time_ASC) {

?

text

?

title

?

blob

?

time

?

}

?

subscriptions(first: 10) {

?

what

?

where

?

time

?

}

?

}

?

}

?

}

在使用 GraphQL 時(shí),我們可以在查詢中保留嵌套對(duì)象,對(duì)于每個(gè)對(duì)象,我們將精確地獲得我們需要的數(shù)據(jù),不多也不少。

響應(yīng)消息的數(shù)據(jù)格式反映了請(qǐng)求查詢的結(jié)構(gòu),如下所示:

復(fù)制代碼

?

{

?

"data": {

?

"user": {

?

"id": 123,

?

"firstName": "Azat",

?

"lastName": "Mardan",

?

"address": {

?

"city": "San Francisco",

?

"country": "US",

?

"zip": "94105"

?

},

?

"resumes" [

?

{

?

"text": "some text here...",

?

"title": "My Resume",

?

"blob": "<BLOB>",

?

"time": "2018-11-13T21:23:16.000Z"

?

},

?

],

?

"subscriptions": [ ]

?

},

?

"errors": []

?

}

相比復(fù)雜的 REST 端點(diǎn),使用 GraphQL 的另一個(gè)好處是提高了安全性。這是因?yàn)?URL 經(jīng)常會(huì)被記錄下來(lái),而 RESTful GET 端點(diǎn)依賴于查詢字符串(是 URL 的一部分)。這可能會(huì)暴露敏感數(shù)據(jù),所以 RESTful GET 請(qǐng)求的安全性低于 GraphQL 的 POST 請(qǐng)求。我打賭這就是為什么 Indeed 主頁(yè)會(huì)使用 POST 發(fā)出“閱讀”頁(yè)面請(qǐng)求。

使用 GraphQL 可有更容易地實(shí)現(xiàn)分頁(yè)等關(guān)鍵功能,這要?dú)w功于查詢以及 BaaS 提供商提供的標(biāo)準(zhǔn),以及后端的實(shí)現(xiàn)和客戶端庫(kù)使用的標(biāo)準(zhǔn)。

改進(jìn)的安全性、強(qiáng)類型和驗(yàn)證

GraphQL 的 schema 與語(yǔ)言無(wú)關(guān)。對(duì)前面的示例進(jìn)行擴(kuò)展,我們可以在 schema 中定義 Address 類型:

復(fù)制代碼

?

type Address {

?

city: String!

?

country: String!

?

zip: Int

?

}

String 和 Int 是標(biāo)量類型,! 表示字段不可為空。

schema 驗(yàn)證是 GraphQL 規(guī)范的一部分,因此像這樣的查詢將返回錯(cuò)誤,因?yàn)?name 和 phone 不是 Address 對(duì)象的字段:

復(fù)制代碼

?

{

?

user (id: 123) {

?

address {

?

name

?

phone

?

}

?

}

?

}

我們可以使用我們的類型構(gòu)建復(fù)雜的 GraphQL schema。例如,用戶類型可能會(huì)使用我們的地址、簡(jiǎn)歷和訂閱類型,如下所示:

復(fù)制代碼

?

type User {

?

id: ID!

?

firstName: String!

?

lastName: String!

?

address: Address!

?

resumes: [Resume]

?

subscriptions: [Subscription]

?

}

Indeed 的大量對(duì)象和類型都是使用 ProtoBuf 定義的。類型化數(shù)據(jù)并不是什么新鮮事物,而且類型數(shù)據(jù)的好處也是眾所周知。與發(fā)明新的 JSON 類型標(biāo)準(zhǔn)相比,GraphQL 的優(yōu)點(diǎn)在于已經(jīng)存在可以從 ProtoBuf 自動(dòng)換換到 GraphQL 的庫(kù)。即使其中一個(gè)庫(kù)(rejoiner)不能用,也可以開(kāi)發(fā)自己的轉(zhuǎn)換器。

GraphQL 提供了比 JSON RESTful API 更強(qiáng)的安全性,主要有兩個(gè)原因:強(qiáng)類型 schema(例如數(shù)據(jù)驗(yàn)證和無(wú) SQL 注入)以及精確定義客戶端所需數(shù)據(jù)的能力(不會(huì)無(wú)意泄漏數(shù)據(jù))。

靜態(tài)驗(yàn)證是另一個(gè)優(yōu)勢(shì),可以幫助工程師節(jié)省時(shí)間,并在進(jìn)行重構(gòu)時(shí)提升工程師的信心。諸如eslint-plugin-graphql之類的工具可以讓工程師知道后端發(fā)生的變化,并讓后端工程師確保不會(huì)破壞客戶端代碼。

保持前端和后端之間的契約是非常重要的。在使用 REST API 時(shí),我們要小心不要破壞了客戶端代碼,因?yàn)榭蛻舳藷o(wú)法控制響應(yīng)消息。相反,GraphQL 為客戶端提供了控制,GraphQL 可以頻繁更新,而不會(huì)因?yàn)橐肓诵骂愋驮斐芍卮笞兏?。因?yàn)槭褂昧?schema,所以 GraphQL 是一種無(wú)版本的 API。

GraphQL 的實(shí)現(xiàn)

在選擇實(shí)現(xiàn) GraphQL API 的平臺(tái)時(shí),Node 是一個(gè)候選項(xiàng),因?yàn)樽畛?GraphQL 用于 Web 應(yīng)用程序和前端,而 Node 是開(kāi)發(fā) Web 應(yīng)用程序的首選,因?yàn)樗腔?JavaScript 的。使用 Node 可以非常容易地實(shí)現(xiàn) GraphQL(假設(shè)提供了 schema)。事實(shí)上,使用 Express 或 Koa 來(lái)實(shí)現(xiàn)只需要幾行代碼:

復(fù)制代碼

?

const Koa = require('koa');

?

const Router = require('koa-router'); // koa-router@7.x

?

const graphqlHTTP = require('koa-graphql');

??
?

const app = new Koa();

?

const router = new Router();

??
?

router.all('/graphql', graphqlHTTP({

?

schema: schema,

?

graphiql: true

?

}));

??
?

app.use(router.routes()).use(router.allowedMethods());

schema 是使用 npm 的 graphql 中的類型來(lái)定義的。Query 和 Mutation 是特殊的 schema 類型。

GraphQL API 的大部分實(shí)現(xiàn)都在于 schema 和解析器。解析器可以包含任意代碼,但最常見(jiàn)的是以下五個(gè)主要類別:

  • 調(diào)用 Thrift、gRPC 或其他 RPC 服務(wù);

  • 調(diào)用 HTTP REST API(當(dāng)優(yōu)先事項(xiàng)不是重寫(xiě)現(xiàn)有 REST API 時(shí));

  • 直接調(diào)用數(shù)據(jù)存儲(chǔ);

  • 調(diào)用其他 GraphQL schema 查詢或服務(wù);

  • 調(diào)用外部 API。

這里有一個(gè)示例。

Node 很棒,但在 Indeed,我們主要使用 Java。包括 Java 在內(nèi)的很多語(yǔ)言都支持 GraphQL,例如https://github.com/graphql-go和https://github.com/graphql-python。

由于 Indeed 主要使用了 Java,因此這里給出一個(gè)使用 graphql-java 的 Java GraphQL 示例,完整代碼位于這里。它定義了 /graphql 端點(diǎn):

復(fù)制代碼

?

import com.coxautodev.graphql.tools.SchemaParser;

?

import javax.servlet.annotation.WebServlet;

?

import graphql.servlet.SimpleGraphQLServlet;

??
?

@WebServlet(urlPatterns = "/graphql")

?

public class GraphQLEndpoint extends SimpleGraphQLServlet {

??
?

public GraphQLEndpoint() {

?

super(SchemaParser.newParser()

?

.file("schema.graphqls") //parse the schema file created earlier

?

.build()

?

.makeExecutableSchema());

?

}

?

}

GraphQL 的 schema 使用 POJO 來(lái)定義。GraphQL 端點(diǎn)類使用了 LinkRepository POJO。解析器包含了操作的(例如獲取鏈接)實(shí)際代碼:

復(fù)制代碼

?

@WebServlet(urlPatterns = "/graphql")

?

public class GraphQLEndpoint extends SimpleGraphQLServlet {

??
?

public GraphQLEndpoint() {

?

super(buildSchema());

?

}

??
?

private static GraphQLSchema buildSchema() {

?

LinkRepository linkRepository = new LinkRepository();

?

return SchemaParser.newParser()

?

.file("schema.graphqls")

?

.resolvers(new Query(linkRepository))

?

.build()

?

.makeExecutableSchema();

?

}

?

}

在很多情況下,GraphQL 的 schema 可以從其他類型的 schema 自動(dòng)生成,例如 gRPC、Boxcar、ProtoBuf 或 ORM/ODM。

GraphQL 不一定需要客戶端。一個(gè)簡(jiǎn)單的 GraphQL 請(qǐng)求就是一個(gè)常規(guī)的 POST HTTP 請(qǐng)求,其中包含了查詢內(nèi)容。我們可以使用任意的 HTTP 代理庫(kù)(如 CURL、axios、fetch、superagent 等)來(lái)生成請(qǐng)求。例如,在終端中使用 curl 發(fā)送請(qǐng)求:

復(fù)制代碼

?

curl \

?

-X POST \

?

-H "Content-Type: application/json" \

?

--data '{ "query": "{ posts { title } }" }' \

?

https://1jzxrj179.lp.gql.zone/graphql

??

以下代碼可以在任意一個(gè)現(xiàn)代瀏覽器(為了避免 CORS,請(qǐng)?jiān)L問(wèn) launchpad.graphql.com)中運(yùn)行。

復(fù)制代碼

?

fetch('https://1jzxrj179.lp.gql.zone/graphql', {

?

method: 'POST',

?

headers: { 'Content-Type': 'application/json' },

?

body: JSON.stringify({ query: '{ posts { title } }' }),

?

})

?

.then(res => res.json())

?

.then(res => console.log(res.data));

雖然構(gòu)建 GraphQL 請(qǐng)求很容易,但是還需要實(shí)現(xiàn)很多其他東西,比如緩存,因?yàn)榫彺婵梢詷O大地改善用戶體驗(yàn)。構(gòu)建客戶端緩存不是那么容易,所幸的是,Apollo 和 Relay Modern 等提供了開(kāi)箱即用的客戶端緩存。

什么時(shí)候不該使用 GraphQL?

當(dāng)然,完美的解決方案是不存在的(盡管 GraphQL 接近完美),還有一些問(wèn)題需要注意,例如:

1. 它有單點(diǎn)故障嗎?

2. 它可以擴(kuò)展嗎?

3. 誰(shuí)在使用 GraphQL?

最后,以下列出了我們自己的有關(guān) GraphQL 可能不是一個(gè)好選擇的主要原因:

  • 當(dāng)客戶端的需求很簡(jiǎn)單時(shí):如果你的 API 很簡(jiǎn)單,例如 /users/resumes/123,那么 GraphQL 就顯得有點(diǎn)重了;

  • 為了加快加載速度使用了異步資源加載;

  • 在開(kāi)發(fā)新產(chǎn)品時(shí)使用新的 API,而不是基于已有的 API;

  • 不打算向公眾公開(kāi) API;

  • 不需要更改 UI 和其他客戶端;

  • 產(chǎn)品開(kāi)發(fā)不活躍;

  • 使用了其他一些 JSON schema 或序列化格式。

總結(jié)

GraphQL 是一種協(xié)議和一種查詢語(yǔ)言。GraphQL API 可以直接訪問(wèn)數(shù)據(jù)存儲(chǔ),但在大多數(shù)情況下,GraphQL API 是一個(gè)數(shù)據(jù)聚合器和一個(gè)抽象層,一個(gè)可以提升開(kāi)發(fā)速度、減少維護(hù)工作并讓開(kāi)發(fā)人員更快樂(lè)的層。因此,GraphQL 比公共 API 更有意義。很多公司開(kāi)始采用 GraphQL。IBM、PayPal 和 GitHub 聲稱在使用 GraphQL 方面取得了巨大的成功。如果 GraphQL 很有前途,我們現(xiàn)在是否可以停止構(gòu)建過(guò)時(shí)且笨重的 REST API,并擁抱 GraphQL?

英文原文:https://webapplog.com/graphql/

總結(jié)

以上是生活随笔為你收集整理的为什么说 GraphQL 可以取代 REST API?的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。

如果覺(jué)得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。