Apollo Multiple Clients with React?

在實(shí)際開發(fā)中,一個(gè)項(xiàng)目后端會(huì)拆分成幾個(gè)微服務(wù)進(jìn)行寫痊夭,如:用戶一個(gè)模塊體系,訂單一個(gè)模塊體系脏里,支付一個(gè)模塊體系等她我,后端會(huì)提供不同二級(jí)域名的api接口。如果后端使用Graphql而不是使用Restful Api迫横,這樣我們就要考慮多個(gè)Apollo客戶端番舆。在實(shí)際項(xiàng)目中我也遇到這個(gè)問(wèn)題了。今天我們翻譯篇文章矾踱,關(guān)于REACT使用多個(gè)Apollo客戶端恨狈,后續(xù)我也會(huì)把我最后完成的demo放出來(lái),供大家參考呛讲。這篇文章來(lái)自于Medium上面禾怠,作者:Rafael Nunes返奉,翻譯文章原始地址

Apollo Multiple Clients with React?

React + Apollo Client + GraphQL = ?

這篇文章快速解釋了,如何在同一個(gè)React應(yīng)用程序中使用不同的Apollo clients吗氏,但在最后芽偏,在使用多個(gè)GraphQL APIs時(shí)討論了其他方法。這并不是有意要以任何方式去質(zhì)疑GraphQL原理弦讽!

寫這篇文章的原因是污尉,作者發(fā)現(xiàn)自己遇到問(wèn)題了,在React應(yīng)用程序中怎么使用多clients查詢不同GraphQL APIs往产。這表明Apollo GitHub項(xiàng)目中存在很多問(wèn)題被碗,討論需求并提出實(shí)施建議。

TL;DR: passing any ApolloClient instance to Query/Mutation/Subscription components as props works just fine! Check: https://github.com/peaonunes/apollo-multiple-clients-example

下面列出了與相關(guān)問(wèn)題仿村、討論和方案的一些鏈接锐朴。一些舊方案確實(shí)也已經(jīng)合并,并且?guī)в欣系?code>react-apollo版本奠宜。然而包颁,從2.1版本開始Apollo客戶端的使用和查詢發(fā)生了許多變化(for better)。

Why would we need multiple clients?

Apollo Client在初始化的時(shí)候只接受一個(gè)client uri压真。因此娩嚼,就意味著同時(shí)只能使用一個(gè)client。

import ApolloClient from "apollo-boost";

const client = new ApolloClient({
 uri: "https://48p1r2roz4.sse.codesandbox.io"
});

例如滴肿,如果在React應(yīng)用程序中需要從兩個(gè)不同的GraphQL服務(wù)中獲取數(shù)據(jù)岳悟,就不能使用相同的client或者程序?qū)嵗恕?/p>

具體來(lái)說(shuō),我只是在找一個(gè)快速的實(shí)施方法從兩個(gè)GraphQL APIs獲取數(shù)據(jù)來(lái)驗(yàn)證解決方案泼差。不必?fù)?dān)心架構(gòu)沖突贵少,因類型、緩存堆缘、狀態(tài)等等不會(huì)重疊滔灶。

在場(chǎng)景中,在Apollo上查詢API時(shí)吼肥,有一種切換客戶端的方法是有意義的录平。在當(dāng)前方法中,使用ApolloProvider組件包裹你的整個(gè)應(yīng)用程序缀皱,該組件通過(guò)上下文給應(yīng)用程序傳遞實(shí)例化好的客戶端斗这。

import React from "react";
import { render } from "react-dom";

import { ApolloProvider } from "react-apollo";
import ApolloClient from "apollo-boost";

const client = new ApolloClient({
  uri: "https://w5xlvm3vzz.lp.gql.zone/graphql"
});

const App = () => (
  <ApolloProvider client={client}>
    <div>
      <h2>My first Apollo app ??</h2>
    </div>
  </ApolloProvider>
);

render(<App />, document.getElementById("root"));

實(shí)際上使用Query Component使查詢數(shù)據(jù)變得簡(jiǎn)單,但這也意味著啤斗,當(dāng)查詢時(shí)表箭,客戶端提供的上下文變量是唯一的。

作者花了一些時(shí)間查閱了大量問(wèn)題和相關(guān)項(xiàng)目钮莲,結(jié)果發(fā)現(xiàn)有一種方法可以覆蓋QueryMutation組件的客戶端上下文免钻,通過(guò)另一個(gè)客戶端的props傳遞彼水。

<Query client={anotherClient} query={query}>
 {({ data }) => (<div>{data.name}</div>)}
 </Query>

Update, Aug 2019: Although they have changed the implementation it still works. https://github.com/apollographql/react-apollo/blob/master/packages/components/src/Query.tsx#L17

// https://github.com/apollographql/react-apollo/blob/master/src/Query.tsx#L167
// https://github.com/apollographql/react-apollo/blob/master/src/Mutation.tsx#L120
...
this.client = props.client || context.client;
...

官方文檔并沒(méi)有提及此功能。我們可以為組件傳遞任何client伯襟,這些client將優(yōu)先通過(guò)props而不是context傳遞猿涨。下面案例:

// ...
const customClient = new ApolloClient({
  uri: "http://other-api/graphql"
});

const Dogs = ({ onDogSelected }) => (
  <Query query={GET_DOGS} client={customClient} >
    {({ loading, error, data }) => {
      if (loading) return "Loading...";
      if (error) return `Error! ${error.message}`;

      return (
        <select name="dog" onChange={onDogSelected}>
          {data.dogs.map(dog => (
            <option key={dog.id} value={dog.breed}>
              {dog.breed}
            </option>
          ))}
        </select>
      );
    }}
  </Query>
);
// ...

作者已經(jīng)實(shí)現(xiàn)了一個(gè)可運(yùn)行的示例,該示例使用了兩個(gè)不同的clients,地址: https://github.com/peaonunes/apollo-multiple-clients-example

即使這個(gè)方法是有效的姆怪,也應(yīng)該記住叛赚,除非同時(shí)傳遞兩個(gè)clients的緩存,否則不會(huì)為兩個(gè)clients同時(shí)運(yùn)行Apollo功能(如果發(fā)生模式?jīng)_突稽揭,可能會(huì)有風(fēng)險(xiǎn))俺附,單獨(dú)管理其他功能。Apollo功能將會(huì)受到損害溪掀,并且隨著應(yīng)用程序的成長(zhǎng)事镣,代碼庫(kù)變得更冗余,開發(fā)將可能變得緩慢揪胃。

What would be the ideal approach then?

Solving the problem in the Frontend

本文討論的結(jié)果璃哟,folks已經(jīng)提出了一些解決方法,為解決這個(gè)問(wèn)題他們做了自己的抽象/實(shí)現(xiàn)層喊递。

Community own package implementations

Michael Duve寫一個(gè)插件react-apollo-multiple-clients,用這個(gè)插件可以在多個(gè)clients之間轉(zhuǎn)換随闪。這個(gè)插件有多個(gè)提供者,它提供了一個(gè)高級(jí)組件(HOC)骚勘,接收client prop铐伴,切換到使用者想要的client。進(jìn)入里面討論

Paul Grieselhuber俏讹,他建議了一種方法当宴,所有的請(qǐng)求都是一個(gè)單client,選擇一個(gè)uri方便的觸發(fā)上下文泽疆,client就將請(qǐng)求分發(fā)出去户矢。你可以在這里關(guān)注討論。

Client-side schema stitching

盡管支持服務(wù)端殉疼,很少有人嘗試在客戶端上直接解決問(wèn)題梯浪,在客戶端上有一些問(wèn)題正在尋找或請(qǐng)求聯(lián)合,例如:#797

不過(guò)株依,Hasura公司給出了一個(gè)可行方案,關(guān)于客戶端模式聯(lián)合延窜,在你的案例它可能已經(jīng)足夠了恋腕。

盡管作者認(rèn)為這些方法可以解決這個(gè)問(wèn)題,但是也認(rèn)為隨著應(yīng)用程序增長(zhǎng)逆瑞,這些解決方法會(huì)增加前端程序的復(fù)雜性荠藤。我的觀點(diǎn)伙单,這個(gè)功能應(yīng)該由后端來(lái)做,為所有的不同的APIs應(yīng)提供統(tǒng)一的接口哈肖。

Gateways for Frontends

API網(wǎng)關(guān)是一種眾所周知的模式吻育,在我們的“微服務(wù)熱潮”時(shí)代,采用頻率越來(lái)越高淤井。API網(wǎng)關(guān)是服務(wù)端和客戶端之間的單個(gè)接口布疼。

GraphQL開發(fā)中似乎已經(jīng)達(dá)成共識(shí),API網(wǎng)關(guān)是與不同GraphQL API進(jìn)行連接的方式币狠。然后有時(shí)網(wǎng)關(guān)會(huì)超越此范圍游两,因?yàn)榫W(wǎng)關(guān)本身可以為其它REST和RPC API創(chuàng)建GraphQL 接口。

通過(guò)唯一網(wǎng)關(guān)提供不同APIs的真正問(wèn)題是如何管理和編排不同的數(shù)據(jù)結(jié)構(gòu)漩绵。

Client-side schema stitching

正如本文前面提到的贱案,Apollo團(tuán)隊(duì)提倡的首次嘗試是數(shù)據(jù)結(jié)構(gòu)粘合。

經(jīng)過(guò)一段時(shí)間的發(fā)展止吐,社區(qū)反饋這個(gè)方法是錯(cuò)弱的宝踪,現(xiàn)在已經(jīng)被棄用了。

Apollo Federation

Apollo最近推出了一個(gè)新概念碍扔,叫做“Apollo Federation”瘩燥,解決一個(gè)網(wǎng)關(guān)管理不同數(shù)據(jù)結(jié)構(gòu)的問(wèn)題。

“Apollo Federation is our answer for implementing GraphQL in a microservice architecture. It’s designed to replace schema stitching and solve pain points such as coordination, separation of concerns, and brittle gateway code.” James Baxley III

在這之前他們已經(jīng)推出了Federation規(guī)范蕴忆,在一些語(yǔ)言中已經(jīng)實(shí)行了颤芬,例如:apollo-gateway。這個(gè)想法要有一個(gè)組成架構(gòu)的網(wǎng)關(guān)套鹅,并且可以通過(guò)keys聯(lián)合服務(wù)(就像主鍵)站蝠,還能夠擴(kuò)展types。所有的這些僅僅用了GraphQL常規(guī)的規(guī)范卓鹿。

建議花一些時(shí)間觀看下面視頻菱魔,并花一些時(shí)間嘗試一下這種更好的方法。

視頻

作者親自嘗試過(guò)吟孙,并且看到公司正在基于這種新方法開發(fā)解決方案澜倦。同樣值得注意的是,還有其他挑戰(zhàn)和空間杰妓,例如管理身份驗(yàn)證/授權(quán)藻治,網(wǎng)關(guān)應(yīng)具有的靈活性等其他討論。希望Federation根據(jù)社區(qū)和公司的反饋而不斷發(fā)展巷挥。

Conclusion

正如這篇文章之前提到的桩卵,它不是明確的給出多個(gè)GraphQL API的“正確”方法,是要指出希望解決當(dāng)前這個(gè)問(wèn)題的方法。

作者認(rèn)為使用API網(wǎng)關(guān)和管理不同的GraphQL數(shù)據(jù)結(jié)構(gòu)的整個(gè)討論才剛剛開始雏节,社區(qū)將致力于更完美更好的解決方案胜嗓。

翻譯結(jié)束

如翻譯有問(wèn)題請(qǐng)多謝指出!

這篇文章作者也提到了兩個(gè)方案解決apollo multiple clients钩乍,一個(gè)是在單個(gè)query里面添加client辞州,一個(gè)是通過(guò)網(wǎng)關(guān)的方式解決。在單個(gè)query里面添加client代碼不是很優(yōu)雅寥粹;后端確實(shí)也會(huì)做相應(yīng)的網(wǎng)關(guān)变过,但不能解決全部問(wèn)題,實(shí)際項(xiàng)目中還是需要解決排作。在下一篇文章我將介紹另一個(gè)方法使用Apollo multiple clients牵啦,這個(gè)也許是個(gè)更好的解決方案。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末妄痪,一起剝皮案震驚了整個(gè)濱河市哈雏,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌衫生,老刑警劉巖裳瘪,帶你破解...
    沈念sama閱讀 212,454評(píng)論 6 493
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異罪针,居然都是意外死亡彭羹,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,553評(píng)論 3 385
  • 文/潘曉璐 我一進(jìn)店門泪酱,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)派殷,“玉大人,你說(shuō)我怎么就攤上這事墓阀≌毕В” “怎么了?”我有些...
    開封第一講書人閱讀 157,921評(píng)論 0 348
  • 文/不壞的土叔 我叫張陵斯撮,是天一觀的道長(zhǎng)经伙。 經(jīng)常有香客問(wèn)我,道長(zhǎng)勿锅,這世上最難降的妖魔是什么帕膜? 我笑而不...
    開封第一講書人閱讀 56,648評(píng)論 1 284
  • 正文 為了忘掉前任,我火速辦了婚禮溢十,結(jié)果婚禮上垮刹,老公的妹妹穿的比我還像新娘。我一直安慰自己张弛,他們只是感情好荒典,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,770評(píng)論 6 386
  • 文/花漫 我一把揭開白布宗挥。 她就那樣靜靜地躺著,像睡著了一般种蝶。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上瞒大,一...
    開封第一講書人閱讀 49,950評(píng)論 1 291
  • 那天螃征,我揣著相機(jī)與錄音,去河邊找鬼透敌。 笑死盯滚,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的酗电。 我是一名探鬼主播魄藕,決...
    沈念sama閱讀 39,090評(píng)論 3 410
  • 文/蒼蘭香墨 我猛地睜開眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼撵术!你這毒婦竟也來(lái)了背率?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 37,817評(píng)論 0 268
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤嫩与,失蹤者是張志新(化名)和其女友劉穎寝姿,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體划滋,經(jīng)...
    沈念sama閱讀 44,275評(píng)論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡饵筑,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,592評(píng)論 2 327
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了处坪。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片根资。...
    茶點(diǎn)故事閱讀 38,724評(píng)論 1 341
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖同窘,靈堂內(nèi)的尸體忽然破棺而出玄帕,到底是詐尸還是另有隱情,我是刑警寧澤塞椎,帶...
    沈念sama閱讀 34,409評(píng)論 4 333
  • 正文 年R本政府宣布桨仿,位于F島的核電站,受9級(jí)特大地震影響案狠,放射性物質(zhì)發(fā)生泄漏服傍。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 40,052評(píng)論 3 316
  • 文/蒙蒙 一骂铁、第九天 我趴在偏房一處隱蔽的房頂上張望吹零。 院中可真熱鬧,春花似錦拉庵、人聲如沸灿椅。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,815評(píng)論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)茫蛹。三九已至操刀,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間婴洼,已是汗流浹背骨坑。 一陣腳步聲響...
    開封第一講書人閱讀 32,043評(píng)論 1 266
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留柬采,地道東北人欢唾。 一個(gè)月前我還...
    沈念sama閱讀 46,503評(píng)論 2 361
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像粉捻,于是被迫代替她去往敵國(guó)和親礁遣。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,627評(píng)論 2 350