Multiple GraphQL Endpoints with Apollo Client
Published: 2024-01-09 09:46:06

If you are knee-deep in the world of web development, chances are you have crossed paths with GraphQL and Apollo Client. This dynamic duo has revolutionized how developers interact with data on the frontend. However Apollo Client does have a flaw, *"How do I connect to multiple GraphQL endpoints using a single client instance?* This post will explain a few ways to achieve just that.

*Note: For the sake of brevity, 'query' will refer to any GraphQL operation; Query, Mutation or Subscription.*

## ApolloLink.split()
The native approach is to make use of the `split` function provided by the `@apollo/client`. This will enable your link chain to branch to one of two `HttpLink`s based on the result of a boolean check.
```javascript
import { ApolloClient, ApolloLink, HttpLink } from '@apollo/client';

const endpoint1 = new HttpLink({
  uri: 'http://endpoint1.com/graphql',
  ...
});

const endpoint2 = new HttpLink({
  uri: 'http://endpoint2.com/graphql',
  ...
});

const client = new ApolloClient({
  link: ApolloLink.split(
      operation => operation.getContext().apiName === 'end1', // boolean check
      endpoint1, // if true
      endpoint2  // if fa
  )
  ...
});

// pass the apiName via the context in your Query
useQuery(QUERY, { variables, context: { apiName: 'end1' } });
```
In this example, the boolean check is based on a context variable, however the test can be anything. The `split` function is simple to implement but is limited to only two GQL connections.

## Dynamic HttpLink
But now what if a application serves as the frontend for a suite of microservices? Well, by expanding the use of the operation `context` the endpoint in the `HttpLink` can be set dynamically.
```javascript
import { ApolloClient, createHttpLink } from '@apollo/client';

const httpLink = createHttpLink({
  uri: ({ getContext }) => {
    const { apiName } = getContext();
    
    if (apiName === 'end1') return 'http://endpoint1.com/graphql';
    if (apiName === 'end2') return 'http://endpoint2.com/graphql';
    if (apiName === 'end3') return 'http://endpoint3.com/graphql';
    ...
  }
});

const client = new ApolloClient({
  link: httpLink
  ...
});

// pass the apiName via the context in your Query
useQuery(QUERY, { variables, context: { apiName: 'end1' } });
useQuery(QUERY, { variables, context: { apiName: 'end2' } });
```
This solution is also relatively simple and allows for expendability as new microservices are added to the suite that the client app has to connect to. 

The disadvantage of both of these approaches is that the developer has to include the `apiName` in the context every time that `useQuery`. Personally I found this approach lead to a messy implementation. I mean declaring the context object on *every* request is so repetitive.

## Apollo MultiAPILink
I wanted a solution that was more intuitive though, something that removed the need to declare the API every time a `useQuery` was used, enter [@habx/apollo-multi-endpoint-link](https://github.com/habx/apollo-multi-endpoint-link). This library allows the API to be declared on the GQL query as a directive, instead of every time `useQuery` is called.
```javascript
const QUERY1 = gql`
  query Users @api(name: end1) {
    users { id name }
  }
`;

const QUERY2 = gql`
  query Companies @api(name: end2) {
    companies { id name code }
  }
`;
```
The `@api(name: end1)` informs the library which endpoint to use based on configuration. 
*Note: The directive is removed from the query before it is sent to the server.*
```javascript
import { ApolloClient, createHttpLink } from '@apollo/client';
import { MultiAPILink } from '@habx/apollo-multi-endpoint-link';

const multiHttpLink = new MultiAPILink({
  endpoints: {
    end1: 'http://endpoint1.com',
    end2: 'http://endpoint2.com',
    end3: 'http://endpoint3.com',
    ... 
  },
  createHttpLink: () => createHttpLink()
});

const client = new ApolloClient({
  link: multiHttpLink
  ...
});

// Now use the query without any additional api config
useQuery(QUERY1);
useQuery(QUERY2);
```
The `MultiAPILink` matches the API name with the relevant endpoint. Since the API is declared on the query, it doesn't have to be repeated in every `useQuery`, leaving code much cleaner and containing less duplication.  

## Conclusion
In summary, its definitely possible to have multiple endpoints in a single Apollo Client instance. 

### Sources
[Github: @habx/apollo-multi-endpoint-link](https://github.com/habx/apollo-multi-endpoint-link)
[Apollo Link Overview](https://www.apollographql.com/docs/react/api/link/introduction/)
[Apollo Link - Directional Composition](https://www.apollographql.com/docs/react/api/link/introduction/#directional-composition)