Apollo Multiple Clients with React?

在实际开发中,一个项目后端会拆分成几个微服务进行写,如:用户一个模块体系,订单一个模块体系,支付一个模块体系等,后端会提供不同二级域名的api接口。如果后端使用Graphql而不是使用Restful Api,这样我们就要考虑多个Apollo客户端。在实际项目中我也遇到这个问题了。今天我们翻译篇文章,关于REACT使用多个Apollo客户端,后续我也会把我最后完成的demo放出来,供大家参考。这篇文章来自于Medium上面,作者:Rafael Nunes,翻译文章原始地址

Apollo Multiple Clients with React?

React + Apollo Client + GraphQL = ❤

这篇文章快速解释了,如何在同一个React应用程序中使用不同的Apollo clients,但在最后,在使用多个GraphQL APIs时讨论了其他方法。这并不是有意要以任何方式去质疑GraphQL原理!

写这篇文章的原因是,作者发现自己遇到问题了,在React应用程序中怎么使用多clients查询不同GraphQL APIs。这表明Apollo GitHub项目中存在很多问题,讨论需求并提出实施建议。

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

下面列出了与相关问题、讨论和方案的一些链接。一些旧方案确实也已经合并,并且带有老的react-apollo版本。然而,从2.1版本开始Apollo客户端的使用和查询发生了许多变化(for better)。

Why would we need multiple clients?

Apollo Client在初始化的时候只接受一个client uri。因此,就意味着同时只能使用一个client。

import ApolloClient from "apollo-boost";

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

例如,如果在React应用程序中需要从两个不同的GraphQL服务中获取数据,就不能使用相同的client或者程序实例了。

具体来说,我只是在找一个快速的实施方法从两个GraphQL APIs获取数据来验证解决方案。不必担心架构冲突,因类型、缓存、状态等等不会重叠。

在场景中,在Apollo上查询API时,有一种切换客户端的方法是有意义的。在当前方法中,使用ApolloProvider组件包裹你的整个应用程序,该组件通过上下文给应用程序传递实例化好的客户端。

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"));

实际上使用Query Component使查询数据变得简单,但这也意味着,当查询时,客户端提供的上下文变量是唯一的。

作者花了一些时间查阅了大量问题和相关项目,结果发现有一种方法可以覆盖QueryMutation组件的客户端上下文,通过另一个客户端的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;
...

官方文档并没有提及此功能。我们可以为组件传递任何client,这些client将优先通过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>
);
// ...

作者已经实现了一个可运行的示例,该示例使用了两个不同的clients,地址: https://github.com/peaonunes/apollo-multiple-clients-example

即使这个方法是有效的,也应该记住,除非同时传递两个clients的缓存,否则不会为两个clients同时运行Apollo功能(如果发生模式冲突,可能会有风险),单独管理其他功能。Apollo功能将会受到损害,并且随着应用程序的成长,代码库变得更冗余,开发将可能变得缓慢。

What would be the ideal approach then?

Solving the problem in the Frontend

本文讨论的结果,folks已经提出了一些解决方法,为解决这个问题他们做了自己的抽象/实现层。

Community own package implementations

Michael Duve写一个插件react-apollo-multiple-clients,用这个插件可以在多个clients之间转换。这个插件有多个提供者,它提供了一个高级组件(HOC),接收client prop,切换到使用者想要的client。进入里面讨论

Paul Grieselhuber,他建议了一种方法,所有的请求都是一个单client,选择一个uri方便的触发上下文,client就将请求分发出去。你可以在这里关注讨论。

Client-side schema stitching

尽管支持服务端,很少有人尝试在客户端上直接解决问题,在客户端上有一些问题正在寻找或请求联合,例如:#797

不过,Hasura公司给出了一个可行方案,关于客户端模式联合,在你的案例它可能已经足够了。

尽管作者认为这些方法可以解决这个问题,但是也认为随着应用程序增长,这些解决方法会增加前端程序的复杂性。我的观点,这个功能应该由后端来做,为所有的不同的APIs应提供统一的接口。

Gateways for Frontends

API网关是一种众所周知的模式,在我们的“微服务热潮”时代,采用频率越来越高。API网关是服务端和客户端之间的单个接口。

GraphQL开发中似乎已经达成共识,API网关是与不同GraphQL API进行连接的方式。然后有时网关会超越此范围,因为网关本身可以为其它REST和RPC API创建GraphQL 接口。

通过唯一网关提供不同APIs的真正问题是如何管理和编排不同的数据结构。

Client-side schema stitching

正如本文前面提到的,Apollo团队提倡的首次尝试是数据结构粘合。

经过一段时间的发展,社区反馈这个方法是错弱的,现在已经被弃用了。

Apollo Federation

Apollo最近推出了一个新概念,叫做“Apollo Federation”,解决一个网关管理不同数据结构的问题。

“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

在这之前他们已经推出了Federation规范,在一些语言中已经实行了,例如:apollo-gateway。这个想法要有一个组成架构的网关,并且可以通过keys联合服务(就像主键),还能够扩展types。所有的这些仅仅用了GraphQL常规的规范。

建议花一些时间观看下面视频,并花一些时间尝试一下这种更好的方法。

视频

作者亲自尝试过,并且看到公司正在基于这种新方法开发解决方案。同样值得注意的是,还有其他挑战和空间,例如管理身份验证/授权,网关应具有的灵活性等其他讨论。希望Federation根据社区和公司的反馈而不断发展。

Conclusion

正如这篇文章之前提到的,它不是明确的给出多个GraphQL API的“正确”方法,是要指出希望解决当前这个问题的方法。

作者认为使用API网关和管理不同的GraphQL数据结构的整个讨论才刚刚开始,社区将致力于更完美更好的解决方案。

翻译结束

如翻译有问题请多谢指出!

这篇文章作者也提到了两个方案解决apollo multiple clients,一个是在单个query里面添加client,一个是通过网关的方式解决。在单个query里面添加client代码不是很优雅;后端确实也会做相应的网关,但不能解决全部问题,实际项目中还是需要解决。在下一篇文章我将介绍另一个方法使用Apollo multiple clients,这个也许是个更好的解决方案。

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,590评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 86,808评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,151评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,779评论 1 277
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,773评论 5 367
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,656评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,022评论 3 398
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,678评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 41,038评论 1 299
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,659评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,756评论 1 330
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,411评论 4 321
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,005评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,973评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,203评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,053评论 2 350
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,495评论 2 343