Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Allow for multiple remote endpoints #84

Closed
jbaxleyiii opened this issue Apr 8, 2016 · 50 comments
Closed

Allow for multiple remote endpoints #84

jbaxleyiii opened this issue Apr 8, 2016 · 50 comments

Comments

@jbaxleyiii
Copy link
Contributor

One of things things that has me a little confused, is the seemingly lack of versioning recommended by GraphQL implementation articles and examples. Speaking for our team, we are already planning on making our current implementation essentially a pre release (/graphql) and starting with our apollo server build out at api/v1 or graphql/v1.

That being said, I'm interested to get thoughts on using the apollo client with multiple remote endpoints. Here are a few examples / thoughts on the api design

3rd party service + in house service

Let's say we look into the future a year or so. GraphQL has solidified around a standard (already pretty dang close!) and more and more services offer Graph endpoints along with their standard REST endpoints. So I'm building an app that pulls from my app source, and pulls from reddit for example.

// a vanilla, not external redux store example
import ApolloClient, { createNetworkInterface } from "apollo-client";

const Reddit = createNetworkInterface('https://reddit.com/graphql');
const Heighliner = createNetworkInterface('https://api.newspring.cc/graphql');

// we can either specify the remote locations on the queries
// fwiw, I think this is a bad idea ;) but anything is possible right?
// this would mean only on client instantiated with multiple network interfaces passed to it
`
  @Reddit
  query getCategory($categoryId: Int!) {
      category(id: $categoryId) {
        name
        color
      }
    }
`

`
  @Heighliner
  query getCategory($categoryId: Int!) {
      category(id: $categoryId) {
        name
        color
      }
    }
`

const client = new ApolloClient({
  networkInterface: {
    Reddit,
    Heighliner
  }
})

// or we can create multiple clients but will have to figure out the duplicate store key
const RedditClient = new ApolloClient({
  networkInterface: Reddit,
  reduxRootKey: "reddit",
});

const HeighlinerClient = new ApolloClient({
  networkInterface: Heighliner,
  reduxRootKey: "heighliner",
});

Personally I am a fan of instantiating multiple clients as the two endpoints should have no overlap (if they did, your GraphQL server should be handling the linking, not the client). That also gives us the benefit of doing something like this in the future

Remote service + local service. One query / action language to rule them all

// a vanilla, not external redux store example
import ApolloClient, { createNetworkInterface, createLocalInterface } from "apollo-client";

// localGraphQLResolver is a client side implementation of a graphql app
// it allows for mutations to update / change local state of the app
// and queries to get app state
// @NOTE this is just an idea I've been playing with. Hope to have an example soon
const Local = createLocalInterface(localGraphQLResolver); 
const Heighliner = createNetworkInterface('https://api.newspring.cc/graphql');

const App = new ApolloClient({
  networkInterface: Local,
  reduxRootKey: "local",
});

const HeighlinerClient = new ApolloClient({
  networkInterface: Heighliner,
  reduxRootKey: "heighliner",
});

So all in all, I don't think this requires any API changes, just wanted to bring it up 👍

@stubailo
Copy link
Contributor

stubailo commented Apr 9, 2016

Personally, I think the main strength of GraphQL is having exactly one endpoint. If you're querying multiple GraphQL servers from the same client, I feel like you're doing it wrong (just my opinion at the moment).

I think having a function in the server to proxy part of the schema to a different GraphQL server would be the right approach, so that you maintain the property that you can render your whole UI with one roundtrip.

As for merging client and server data, I have a few ideas for how to do that, but I think that's a special case - it should be exactly one server schema and one client schema IMO.

One main argument for me is that the whole point of GraphQL is to be able to traverse data in the same query, and have schema types that reference each other - just sending two root queries to different servers doesn't really give you any benefit over just initializing two clients IMO.

We have some ideas for how this might work in a future Apollo proxy server, which would let you traverse between schemas at will in a single query, from your schema, to external services, etc.

@stubailo
Copy link
Contributor

stubailo commented Apr 9, 2016

seemingly lack of versioning recommended by GraphQL implementation articles and examples

My understanding is that GraphQL is supposed to remove the need for versioning by having deprecated fields. So you can deprecate a field, then track how many clients access that field, and once it drops below a certain level you just remove it. Rather than having different versions of the whole API.

@jbaxleyiii
Copy link
Contributor Author

@stubailo I haven't played around with deprecated fields. I'll give that a go for our use case. I can see how having the server doing the linking would be a better world too.

http://facebook.github.io/graphql/#sec-Deprecation for reference

Thanks for the input 🎉

@stubailo
Copy link
Contributor

stubailo commented Apr 9, 2016

Yeah I would really love to know how that works for you. And it would be cool to collaborate on the proxy thing, I think that would be really valuable, if you could just have your schema say "oh, this is a GitHub user" and then be able to import the whole GitHub API schema and just traverse it.

@stubailo
Copy link
Contributor

stubailo commented Apr 9, 2016

Moving this: #86

@jbaxleyiii
Copy link
Contributor Author

@stubailo since our move would be to the apollo version of the graphql server, I'm sure you'll hear about it haha.

then be able to import the whole GitHub API schema and just traverse it

The dream. Application libraries that you can import and use for your app

@crumhorn
Copy link

crumhorn commented Aug 17, 2016

We are in the process of rewriting the front end to existing old-school webservices, and it's quite naive to assume that a server will only ever have one endpoint.

In our case we have two SOAP services (yep, old stuff) that provide data, and that's just "how it is" currently. In any case, as Apollo as of this writing doesn't seem to support our scenario as I can only tell apollo to use one endpoint based on my url, here's a quick and dirty copy & paste of what I did to make apollo + express handle this (Using Ang2 RC5 if it matters) in case someone else is interested.

First, on the client side, I set up a middleware so that it takes a passed "variable" and sets it as part of the header, as follows:

const networkInterface = createNetworkInterface('http://localhost:4000/graphql');

networkInterface.use([{
    applyMiddleware(req, next) {
        if (!req.options.headers) {
            req.options.headers = {};
        }

        req.options.headers.service = req.request.variables["service"];

        next();
    }
}]);

In my data provider service on the client, I then set up a basic fetch method like:

private fetchData(service:string, query:any):any {
        return new Promise((resolve, reject) => {
            this.angularApollo.query({
                query: query,
                variables: {
                    service: service
                }
            }).then((graphQLResult) => this.handleGraphQLResponse(graphQLResult, (data) => resolve(data), (errors) => reject(errors)));
        });
    }

This will ensure my passed in "service" string ends up on the request header sent to the server.

Now, on the server, using express, I set up a redirect route that reads the header and sends the request on to either one or the other endpoint based on this header:

graphQLServer.use('/graphql', function (req, res) {
    // fetch service from header
    var service = req.headers["service"];

    var path = '';

    if (service == 'trading') {
        path = '/graphqlTrading'
    }
    else if (service == 'ref') {
        path = '/graphqlRef'
    }
    else {
        res.send('Unknown service in header, or not set. Service was: ' + service);
        return;
    }

    var url = 'http://localhost:4000'+path;

    // console.log("Piping '" + service + "' service " + req.method + " + request to "  +url);
    req.pipe(request(url)).pipe(res);
});

Do note however that bodyParser() can mess up redirects as it modifies the body on the request object and can cause the pipe to hang, in my case the express() I use for the GraphQL server is separate from the one I use for the other Ang2 server-side stuff and I don't include bodyParser unless specifically via:

graphQLServer.use('/graphiqlRef', bodyParser.json(), graphiqlExpress({
    endpointURL: '/graphqlRef'
}));

In my opinion, it would be great if you could load up multiple network interfaces in apollo and at query-time be able to tell it which one I want it to use. I'm sure there's many possible solutions though.

@yodacom
Copy link

yodacom commented Aug 26, 2016

Were you able to work with ApolloStack in context and access your SOAP services? I have a similar case with using legacy SOAP services that we need to access with our Reactjs/Redux app ..we would like to go about this in the new "ApolloStack way" so further describing your success/experience with experimenting with SOAP using ApolloStack would be greatly appreciated and helpful to all of us!

@stubailo
Copy link
Contributor

I'm pretty confident that the correct way to use GraphQL is to have one server that calls different services in the background. Having multiple ApolloClient instances is the best way to call multiple services.

@crumhorn
Copy link

crumhorn commented Sep 6, 2016

@yodacom, yes, we are successful using GraphQL and NodeJS together with npm module "soap". I wrote a quick and dirty java project to generate the GraphQL schema from a WSDL (requires some manual interaction post-generation). Since this issue is regarding something else, feel free to contact me if you are intesteted in it. I could put it up on github if others also are interested.

@helfer
Copy link
Contributor

helfer commented Sep 6, 2016

@crumhorn I think it would be great if you could put it on GitHub, then we can link to it from here.

@crumhorn
Copy link

crumhorn commented Sep 7, 2016

@helfer, and anyone else interested, here it is:

https://github.com/crumhorn/wsdl2graphql

I wrote up a quick Readme.md for it.

Like I mentioned earlier, it's a very quick and dirty project and I didn't spend much time on it, but it did the job for us, saving us a large amount of time. Do note the "Important" section, as there's two lines in the code that might necessary to change to fit other peoples web services.

I suggest running it from either IntelliJ IDEA (or Eclipse) just to make it easy as getting the right classpath on the command line is (as always) a pain.

@yodacom
Copy link

yodacom commented Sep 7, 2016

Thanks Emil. Yeah the WSDL's are a great conversion project to get to
GraphQL. Thanks for posting it on Github so we can poke around and see how
to get these old legacy resources on the shiny new path with GraphQL and
ApolloStack!!!

All the best,

Jeremy

Yoda of YodaCom
[email protected]
www.yodacom.com

Standard Disclosure:
YodaCom is a mobile application architect, strategist, and developer.
*
“YodaCom
is also a DMTA - Digital Marketing Technology Architect and Advisor. *

On Wed, Sep 7, 2016 at 2:17 AM, Emil [email protected] wrote:

@helfer https://github.com/helfer, and anyone else interested, here it
is:

https://github.com/crumhorn/wsdl2graphql

I wrote up a quick Readme.md for it.

Like I mentioned earlier, it's a very quick and dirty project and I didn't
spend much time on it, but it did the job for us, saving us a large amount
of time. Do note the "Important" section, as there's two lines in the code
that might necessary to change to fit other peoples web services.

I suggest running it from either IntelliJ IDEA (or Eclipse) just to make
it easy as getting the right classpath on the command line is (as always) a
pain.


You are receiving this because you were mentioned.
Reply to this email directly, view it on GitHub
#84 (comment),
or mute the thread
https://github.com/notifications/unsubscribe-auth/ABjIwdhGSx-A752bqC8un6rfyPnF5foLks5qnnMagaJpZM4IDeaw
.

@pasviegas
Copy link

So, I was searching for a "createLocalInterface" (would do the same as https://github.com/relay-tools/relay-local-schema) for apollo-client and I ended up in this issue.

For my app implementation I really need this. As I couldn't find it, I will have to do it. Is there any interest in having this on this project? Should I make a PR or do it in a separate library?

@stubailo
Copy link
Contributor

stubailo commented Oct 2, 2016

Please do it in a separate library, you should be able to reuse basically all of the code from that Relay package, I think.

What's the use case for you? Unable to run a GraphQL server so you need to do it on the client?

@pasviegas
Copy link

It is app that uses a schema to normalise the data, but actually has drivers to multiple ecommece platforms (which are all rest). So independent of which backend, the app doesn't change, just the bundled driver.

I've found another issue #379, concerning the same thing, it seems like a very common use case and is such a small piece of code. It also helps while prototyping. Please consider doing it in the client itself.

@stubailo
Copy link
Contributor

stubailo commented Oct 2, 2016

What piece of code are you looking for in particular? Is it just the following?

import { schema } from './schema';
import { print, graphql } from 'graphql';

// Set to whatever
let root, context, variables;

const networkInterface = {
  query: ({ query, variables }) => graphql(schema, print(query), root, context, variables)
}

Or is it more than just calling a schema on the client?

@stubailo
Copy link
Contributor

stubailo commented Oct 2, 2016

I'm trying to find out if this is just something we can document, or if there is a lot of logic involved.

@pasviegas
Copy link

Yep, it is just calling the schema on the client, that is all. Maybe documenting is enough though.

@ffxsam
Copy link

ffxsam commented May 23, 2017

@stubailo I just thought of this and stumbled upon this issue. I saw GitHub released a GraphQL API, and thought "how would I write a front-end app that connected to my own GraphQL backend, but also used GitHub's API?" I don't know of any way for Apollo to connect to multiple servers.

@morgs32
Copy link

morgs32 commented Jul 1, 2017

@stubailo any chance you could provide an example of this without going to too much trouble?
#84 (comment)

I'm pretty confident that the correct way to use GraphQL is to have one server that calls different services in the background. Having multiple ApolloClient instances is the best way to call multiple services.

@adamhaney
Copy link

@stubailo I have a similar use case to @ffxsam. I'm working on creating a prepackaged library that I'd like to distribute for other developers to use (so in my case imagine that I am Github and I'm trying to develop a client library that abstracts away the fact that the underlying implementation of my backend is graphql/apollo).

Is there any way that I can create an HOC that utilizes ApolloProvider or similar without conflicting with code that's outside of the scope of my library? This would allow me to distribute an HOC in a library for other developers to use without accidentally smashing their code. Is there a way that I can change the prop that Apollo provider passes down so I could do something like

<ApolloProvider propName={"data"} client={this.theirClient}>
  <ApolloProvider propName={"dontTouch"} client={this.myLibraryClient}>
  </ApolloProvider>
</ApolloProvider>

@ffxsam
Copy link

ffxsam commented Aug 21, 2017

Maybe I was thinking about it the wrong way, and third party GraphQL APIs should be accessed on the backend, and ingested via resolvers?

@stubailo
Copy link
Contributor

BTW this is our long-term solution to this: ardatan/graphql-tools#382

jbaxleyiii pushed a commit that referenced this issue Oct 17, 2017
Add "Auth tokens" section. and correct syntax highlighting to "CORS" section as it appears to be missing.
@abumalick
Copy link

there is also an interesting approach described here:

https://medium.com/open-graphql/apollo-multiple-clients-with-react-b34b571210a5

he passes a different client to the query as a prop..

@in19farkt
Copy link

Why can't I specify multiple GraphQL endpoints? Because the main strength of GraphQL is having exactly one endpoint? What if I don't have a single one?

I just want to write a frontend in which I will work with several public GraphQL endpoints. I don’t have my own server and I don’t have the slightest desire to raise the server and bother with the merging of the endpoints I need. And to solve this seemingly trivial task, there is nothing but crutches ...

@Elijen
Copy link

Elijen commented Nov 20, 2019

Assuming we can stitch data on the server is just plain wrong. You could say the same about REST APIs.

Yes, in perfect world every app has one GraphQL endpoint perfectly designed for its needs. But this almost never happens. Developers in the real world need to deal with legacy data and code.

@in19farkt
Copy link

in19farkt commented Nov 22, 2019

Workaround for using of multiple GraphQL endpoints.

We can use custom directives and write a Link that will select one of the Links to the GraphQL endpoints:

import { ApolloLink } from 'apollo-link';
import { getMainDefinition } from 'apollo-utilities';

const allowedDirectives = ['dai', 'compound'] as const;
type DirectiveName = (typeof allowedDirectives)[number];

const linkByDirective: Record<DirectiveName | 'default', ApolloLink> = {
  dai: daiLink,
  compound: compoundLink,
  default: daiLink,
};

const link = new ApolloLink(operation => {
  const { query } = operation;

  const definition = getMainDefinition(query);

  const foundDirective =
    'operation' in definition &&
    definition.directives?.find(item =>
      allowedDirectives.includes(item.name.value as DirectiveName),
    );
  const directive: DirectiveName | 'default' = foundDirective
    ? (foundDirective.name.value as DirectiveName)
    : 'default';

  return linkByDirective[directive].request(operation);
});

GraphQL query can be written like this

query Users($first: Int!) @compound {
  users(first: $first) {
    id
    cTokens {
      id
      cTokenBalance
    }
    totalSupplyInEth
    totalBorrowInEth
  }
}

You can also use the graphql-codegen, but then you will need to write queries in separate files and the files will need to be named according to the template. For example, for the compound directive, you can specify such a template - QueryName.compound.graphql. Then the graphql-codegen configuration might look like this:

overwrite: true
generates:
  src/generated/compound-graphql.tsx:
    documents: "./src/**/*.compound.graphql"
    schema: "https://api.thegraph.com/subgraphs/name/compound-finance/compound-v2-rinkeby"
    plugins:
      - "typescript"
      - "typescript-operations"
      - "typescript-react-apollo"
    config:
      withComponent: false
      withHOC: false
      withHooks: true
      reactApolloVersion: 3
      scalars:
        BigInt: string
        BigDecimal: string
        Bytes: string
  src/generated/dai-graphql.tsx:
    documents: "./src/**/*.dai.graphql"
    schema: "https://api.thegraph.com/subgraphs/name/raisehq/dai-kovan"
    # ... other configurations

@rodrigofd
Copy link

rodrigofd commented Dec 30, 2019

import { ApolloLink } from 'apollo-link';
import { getMainDefinition } from 'apollo-utilities';

const allowedDirectives = ['dai', 'compound'] as const;
type DirectiveName = (typeof allowedDirectives)[number];

const linkByDirective: Record<DirectiveName | 'default', ApolloLink> = {
  dai: daiLink,
  compound: compoundLink,
  default: daiLink,
};

Good one 👍 ... small typo:

const link = new ApolloLink(operation => {
  const { query } = operation;

  const definition = getMainDefinition(query);

  const link = new ApolloLink(operation => {
  const { query } = operation;

  const definition = getMainDefinition(query);

You have that code block duplicated ;)

@in19farkt
Copy link

@rodrigofd Thanks, updated :)

@rodrigofd
Copy link

@in19farkt following up on your idea (love it :))
Say that I need to implement distinct authentication mechanisms on each endpoint...
Would you pack it all on a single 'dynamic' apollo link? Or would you put aside a second auth. link that repeats all the logic to sort out the custom directive? I'm torned between those two approaches...

Plus, by the way, I had to add code to remove the directive at the end, otherwise the GQL server complains on it... did it happen to you ?

@in19farkt
Copy link

Say that I need to implement distinct authentication mechanisms on each endpoint...
Would you pack it all on a single 'dynamic' apollo link? Or would you put aside a second auth. link that repeats all the logic to sort out the custom directive? I'm torned between those two approaches...

I think that a 'dynamic' link should choose a link according to the directive and nothing more. The chosen link should handle authentication logic.

Plus, by the way, I had to add code to remove the directive at the end, otherwise the GQL server complains on it... did it happen to you ?

I had no such problems.

@shafqatevo
Copy link

shafqatevo commented Jan 13, 2020

@in19farkt Does the Split method described here help solve this issue as well?

https://www.apollographql.com/docs/link/composition/

@in19farkt
Copy link

@shafqatevo for only 2 Graphql endpoints. If there are more endpoints, then the Split becomes uncomfortable.

@EarthlingDavey
Copy link

A working solution using multiple links in your one Apollo client.

https://www.loudnoises.us/next-js-two-apollo-clients-two-graphql-data-sources-the-easy-way/

@sbussard
Copy link

sbussard commented Jun 11, 2020

UPDATE
This approach is WAY EASIER than adding directives

in your setup

const link = new RetryLink().split(
  (operation) => operation.getContext().endpointA,
  new HttpLink({ uri: ENDPOINT_A }),
  new RetryLink().split(
    (operation) => operation.getContext().endpointB,
    new HttpLink({ uri: ENDPOINT_B }),
    new HttpLink({ uri: DEFAULT_ENDPOINT })
  )
);

in your component

  const { data, loading, error } = useQuery(YOUR_QUERY, {
    context: { endpointA: true }
  });

ORIGINAL
Here's modified version of @in19farkt's solution. The main difference is that on the client side it tries to remove the directive before sending it so that no further work is needed on the server side.

import { ApolloLink } from 'apollo-link'; // or from @apollo/client
import { getMainDefinition } from 'apollo-utilities';

const links = {
  account: new HttpLink({ uri: 'http://localhost/graphql1' }),
  default: new HttpLink({ uri: 'http://localhost/graphql2' })
};

const link = new ApolloLink(operation => {
  const definition = getMainDefinition(operation.query);
  let endpoint = 'default';

  if ('operation' in definition) {
    const foundDirective = definition.directives?.find(item =>
      Object.keys(links).includes(item.name.value)
    );

    if (foundDirective) {
      endpoint = foundDirective.name.value;

      // remove the directive from the request
      operation.query.definitions[0].directives = operation.query.definitions[0].directives.filter(
        dir => dir.name.value !== endpoint
      );

      operation.query.loc.source.body = operation.query.loc.source.body.replace(
        `@${endpoint} `,
        ''
      );
    }
  }

  return links[endpoint].request(operation);
});

@jean9696
Copy link
Contributor

jean9696 commented Jun 18, 2020

Hi there, thank you all for your inputs it was very useful for us. Because we have multiple clients we created a library to handle the case of this issue. Feel free to use it or add issues 🙂
https://github.com/habx/apollo-multi-endpoint-link

@mario-petrovic
Copy link

mario-petrovic commented Aug 5, 2020

UPDATE
This approach is WAY EASIER than adding directives

in your setup

const link = new RetryLink().split(
  (operation) => operation.getContext().endpointA,
  new HttpLink({ uri: ENDPOINT_A }),
  new RetryLink().split(
    (operation) => operation.getContext().endpointB,
    new HttpLink({ uri: ENDPOINT_B }),
    new HttpLink({ uri: DEFAULT_ENDPOINT })
  )
);

in your component

  const { data, loading, error } = useQuery(YOUR_QUERY, {
    context: { endpointA: true }
  });

ORIGINAL
Here's modified version of @in19farkt's solution. The main difference is that on the client side it tries to remove the directive before sending it so that no further work is needed on the server side.

import { ApolloLink } from 'apollo-link'; // or from @apollo/client
import { getMainDefinition } from 'apollo-utilities';

const links = {
  account: new HttpLink({ uri: 'http://localhost/graphql1' }),
  default: new HttpLink({ uri: 'http://localhost/graphql2' })
};

const link = new ApolloLink(operation => {
  const definition = getMainDefinition(operation.query);
  let endpoint = 'default';

  if ('operation' in definition) {
    const foundDirective = definition.directives?.find(item =>
      Object.keys(links).includes(item.name.value)
    );

    if (foundDirective) {
      endpoint = foundDirective.name.value;

      // remove the directive from the request
      operation.query.definitions[0].directives = operation.query.definitions[0].directives.filter(
        dir => dir.name.value !== endpoint
      );

      operation.query.loc.source.body = operation.query.loc.source.body.replace(
        `@${endpoint} `,
        ''
      );
    }
  }

  return links[endpoint].request(operation);
});

@sbussard

Thanks to you and the original solution creator in19farkt, this almost worked for me.

  1. Where i have a problem is when i lets say, i do login graphQL call and use this solution that removes directive so that BE does not need to handle that. Login works.
  2. After this i logout and come to login page.
  3. When i use again that same graphQL call for login, and debug your ApolloLink, there is no directive present there. So call is made as if i didn't set directive, in my case @open but name is not relevant.

So my question is, does your solution remove that directive for good? Because in my implementation it does.

And also if you can add an example where you can combine this solution ApolloLink with onError, because your solution does not do forward(operation) as linked error handler expects. Or to chain it with any other ApolloLink when provided as:

   client.current = new ApolloClient({
      link: from([linkWithModificationForDirective, someOtherHandling]),
      cache: new InMemoryCache(),
    });

Thanks

@karnash
Copy link

karnash commented Sep 16, 2020

Thanks to you and the original solution creator in19farkt, this almost worked for me too.
but when i return to view always set default, and dont take the endpoint in query, in my case the Mutation.
image
how i can.. set the endpoint before to make the mutation or query. ??
image
i using thats gql.. thk to our help.

@karnash
Copy link

karnash commented Sep 17, 2020

well i solve that.. using a context var.... on each useQuery or Mutation like that
const [createMutate, {loading, error, data}] = useMutation(mutateGQL, { context: { endpoint: 'myendpoint' }, });

and in the App.js i use

if (endpoint == 'default') { const ctx = operation.getContext(); if ('endpoint' in ctx) { endpoint = ctx.endpoint; } } return links[endpoint].request(operation);

@karnash
Copy link

karnash commented Oct 27, 2020

but.. now... how.. i can send the header..!! ??? some idea....!! ..

@antekai
Copy link

antekai commented Jan 20, 2021

if you want to combine additive and directional composition with ApolloLink.from() and ApolloLink.split() you can:

import {RestLink} from 'apollo-link-rest';
import {ApolloLink} from 'apollo-link';
import {HttpLink} from 'apollo-link-http';
import {InMemoryCache} from 'apollo-cache-inmemory';

const restLink = new RestLink()
const graphql1 = new HttpLink({uri: 'http://localhost/graphql1'});
const graphql2 = new HttpLink({uri: 'http://localhost/graphql2});

const graphqlEndpoints = ApolloLink.split(
  operation => operation.getContext().serviceName === 'service2',
  graphql1,
  graphql2,
);

const link = ApolloLink.from([restLink, graphqlEndpoints]);
const cache = new InMemoryCache();

const Client = new ApolloClient({link,cache});

export default Client;

You can use "service2" endpoint as follows

const {data} = useQuery(SOME_QUERY, { context: {serviceName: 'service2'} });

@langarus
Copy link
Contributor

For other people coming here to find a solution - this part of the docs should solve this problem, the headers can be attached after uri in new HttpLink
https://www.apollographql.com/docs/react/api/link/introduction/#providing-to-apollo-client

@EugeneGohh
Copy link
Contributor

For other people coming here to find a solution - this part of the docs should solve this problem, the headers can be attached after uri in new HttpLink https://www.apollographql.com/docs/react/api/link/introduction/#providing-to-apollo-client

I am using Next.js and I followed this by adding headers under uri in new HttpLink but it's not working. Fyi, my headers is for GitHub access token. Do you have any ideas on how to get away with this?

@langarus
Copy link
Contributor

@EugeneGohh That's exactly how it should be done, and how I did it as well. For headers you need to use the context link

https://www.apollographql.com/docs/react/api/link/apollo-link-context/

@EugeneGohh
Copy link
Contributor

@langarus Thanks for replying, I did it in this way but I got an error saying status code 401.

const endpoint2 = new HttpLink({
  uri: "https://api.github.com/graphql",
});

const authLink = setContext((_, { headers }) => {
  return {
    headers: {
      ...headers,
      authorization: `Bearer ${process.env.GITHUB_ACCESS_TOKEN}`,
    },
  };
}).concat(endpoint2);

const client = new ApolloClient({
  ssrMode: true,
  link: ApolloLink.split(
    (operation) => operation.getContext().clientName === "authLink",
    authLink,
    endpoint1
  ),
  cache: new InMemoryCache(),
});

@langarus
Copy link
Contributor

langarus commented Oct 20, 2021 via email

@EugeneGohh
Copy link
Contributor

I think you should remove .concat(endopint2). And rather in client link: ApolloLink.split( (operation) => operation.getContext().clientName === "authLink", authLink, from([authLink, endpoint1]) from([authLink, endpoint2]) ), In case you want to remove the header from one endpoint in the link just pass it directly without "from"

On Wed, Oct 20, 2021 at 9:41 AM Eugene Goh @.***> wrote: @langarus https://github.com/langarus Thanks for replying, I did it in this way but I got an error saying status code 401. const endpoint2 = new HttpLink({ uri: "https://api.github.com/graphql", }); const authLink = setContext((_, { headers }) => { return { headers: { ...headers, authorization: Bearer ${process.env.GITHUB_ACCESS_TOKEN}, }, }; }).concat(endpoint2); const client = new ApolloClient({ ssrMode: true, link: ApolloLink.split( (operation) => operation.getContext().clientName === "authLink", authLink, endpoint1 ), cache: new InMemoryCache(), }); — You are receiving this because you were mentioned. Reply to this email directly, view it on GitHub <#84 (comment)>, or unsubscribe https://github.com/notifications/unsubscribe-auth/AK3JO2R7QIATPZMRDSDORMDUHZP75ANCNFSM4CAN42YA . Triage notifications on the go with GitHub Mobile for iOS https://apps.apple.com/app/apple-store/id1477376905?ct=notification-email&mt=8&pt=524675 or Android https://play.google.com/store/apps/details?id=com.github.android&referrer=utm_campaign%3Dnotification-email%26utm_medium%3Demail%26utm_source%3Dgithub.

It's still the same after removing. Do you have any samples how you do it?

@EugeneGohh
Copy link
Contributor

@langarus Hey I just fixed my problem. Thanks.

If anyone needs it:

I fixed it by configuring environment variables in next.config.js. Feel free to check this out https://nextjs.org/docs/api-reference/next.config.js/environment-variables .

Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

No branches or pull requests