在實(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?
這篇文章快速解釋了,如何在同一個(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)。
- https://github.com/apollographql/react-apollo/pull/481
- https://github.com/apollographql/react-apollo/issues/464
- https://github.com/apollographql/react-apollo/issues/1588
- https://github.com/apollographql/react-apollo/pull/729
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)有一種方法可以覆蓋Query
和Mutation
組件的客戶端上下文免钻,通過(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è)更好的解決方案。