Multiple Apollo Clients in React

Rafael Nunes - Aug 9 '19 - - Dev Community

This is a review after one year of Apollo Multiple Clients with React?. I took a time to rewrite it, adding community implementation suggestions, Apollo Federation, etc. Realised then that I could share my humble knowledge here as well.

This quick post explains how to use different Apollo clients in the same React application, but in the end, discuss other approaches when working on multiple GraphQL APIs. This is not intended to question the GraphQL philosophy by any means 😝!

I wrote this because I found myself questioning how could I use multiple clients to query different GraphQL APIs from my React application. It turns out there were lots of issues in Apollo GitHub project, discussing the need and presenting suggested implementations for it.

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

Some links to related issues, discussions, and proposals are listed below. Some of the old proposals indeed were merged and come along old react-apollo versions. However, the approach to using the Apollo client and querying changed a lot (for better) since 2.1.


Why would we need multiple Apollo clients?

Apollo Client accepts only one client uri on its initialization, therefore, it is meant to be used with one client at the time.

import ApolloClient from "apollo-boost";

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

So if in your React application you need to retrieve data from two different GraphQL services, for example, you cannot use the same client or provider instance.

In my case specifically, I was just looking for a quick win implementation approach to get data from two GraphQL APIs to validate a solution. I was not worrying too much about schemas collision since the types, cache, state (...) would not overlap.

In my scenario, it would make sense to have a way of switching clients when querying APIs on Apollo. In the current approach though, you wrap your entire application with the ApolloProvider component which passes the client for the application through the context.

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

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

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

render(<App />, document.getElementById("root"));
Enter fullscreen mode Exit fullscreen mode

That actually makes it simple to query data using the Query Component, but it also means that the client provided via context is the only used when querying.

⭐️ First Solution

I spent some time looking through numerous issues and related projects and it turns out that there is a way of overriding the context client for Query and Mutation component passing another client through props 🎉 🎉 !

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

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

This feature is not mentioned in any part of the official documentation. We can indeed pass any client for the components that they will give preference for the one passed via props order than via context. So we could make:

// ...
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>
);
// ...
Enter fullscreen mode Exit fullscreen mode

I have implemented a runnable example that uses two different clients in this repository: https://github.com/peaonunes/apollo-multiple-clients-example

img alt

Even though this approach is functional you should keep in mind that you won't have Apollo features running applying for both clients unless you pass the same cache to the clients (that might be a risk in case of schema collision), manage other features will be by your own. Apollo features will get compromised and as the application grows your codebase becomes thicker and development will probably be slower.

What would be the ideal approach then?

Solving the problem in the Frontend

Some people found the original article on medium and beyond discussing the approaches they have come up with their implementations for solving this issue.

Community implementations

Michael Duve, wrote react-apollo-multiple-clients a packaged that allows you to switch between clients. It considers multiple providers and provides you a HOC component that accepts a client prop to switch to the desired client consumer. Discussion

Paul Grieselhuber, suggested in his post a way where everything worked through a single client and allowed you to simply toggle on context to select a uri where the client will dispatch the requests. You can follow the discussion here.

Client-side schema stitching

Despite support for server side, it is not common to see people trying to solve the issue right on the client, there are some issues looking for or requesting stitching on client side, #797 for example.

The company Hasura, though, points out a way of client side schema stitching and it might be sufficient in your case.


Although I think that these approaches solve the problem I also think they can increase so much the complexity of the frontend application as the application grows. From my point of view, the work should be done on Backend by providing a unique interface for all the different APIs.

Gateways for Frontends

API Gateway is a known pattern with increasing adoption in our "microservice boom" age. API Gateway is a single interface between the services and clients.

It seems to be a consensus in the GraphQL world as well that the API Gateway is the way to go on connection with different GraphQL APIs. However sometimes going beyond that, since the Gateway itself can create a GraphQL interface for other REST and RPC APIs.

The real problem of serving different APIs through a unique gateway is how to manage and orchestrate different schemas.

Schema Stitching

The first attempt the Apollo team advocated for was Schema Stitching.

Schema stitching is the process of creating a single GraphQL schema from multiple underlying GraphQL APIs.

After some time of development and feedback from the community this approach was considered fragile and it is now deprecated.

Apollo Federation

Apollo recently launched a new concept for solving this problem of managing different schemas through a gateway which is called 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

They have launched the Federation spec before and it already counts with implementations in some languages, apollo-gateway, for example. The idea is to have a gateway that composes the schemas and the federated services can connect with each other through keys (just like primary keys) and they are also able to extend types. All this just using regular GraphQL spec.

I recommend taking the time to watch the video below and spend some time playing around with this promising approach.


Apollo Federation - A revolutionary architecture for building a distributed graph

I personally tried it and I am seeing companies working on solutions based on this new approach. It is also notable that there are some challenges and space for other discussions like managing authentication/authorization, how flexible the gateway should be, etc. Hopefully, the Federation get evolving based on feedbacks from the community and companies.

Conclusion

As I mentioned before this post is not about questioning the right way on querying multiple GraphQL APIs, but it is about pointing out approaches that hopefully might be enough for solving the issues of today.

I think the whole discussion about using API Gateways and managing different GraphQL schemas is just at the beginning and the community will continuously work on better solutions.

I am more than happy to read suggestions and engage discussions so leave your thoughts below.

. . . . . . . . .