apollographql / apollo-link

:link: Interface for fetching and modifying control flow of GraphQL requests
https://www.apollographql.com/docs/link/
MIT License
1.44k stars 344 forks source link

apollo-link-batch-http doesn't work with a custom `fetch` #410

Open mdebbar opened 6 years ago

mdebbar commented 6 years ago

Intended outcome:

Using a custom fetch function should work with no errors.

Actual outcome:

Error:

Uncaught TypeError: this.apolloFetch.batchUse is not a function

Stack trace points to this line: https://github.com/apollographql/apollo-link/blob/8ceba7322b533a26ea1e886aba5faa6af1937232/packages/apollo-link-batch-http/src/batchHttpLink.ts#L60

How to reproduce the issue:

Provide a custom fetch function to the constructor:

new ApolloClient({
  cache: new InMemoryCache(),
  link: new BatchHttpLink({ fetch }),
})
Poincare commented 6 years ago

The batch link explicitly relies on apollo-fetch, which has docs here. Could you describe an instance in which you would need a custom fetch function that can't be wrapped with the ApolloFetch functionality?

civicevolution commented 6 years ago

I am using GraphQL server running in an AWS Lambda function accessed through API Gateway. There are two gateways with different Authorizers: One uses a cognito supplied Authorization token and the other uses IAM credentials to sign the request using SigV4.

I am using BatchHttpLink with a composed link that uses setContext() to set the Auth token. This composed link works with BatchHttpLink with no problem.

In order to SigV4 sign the request, I am using a custom fetch function that gives me access to the http request headers and body so I can sign the request.

I can compose a link using HttpLink() that includes my custom fetch function in the link configuration. This works great.

Once this was all working, I tried to configure BatchHttpLink with my custom fetch but this failed with "apolloFetch.batchUse is not a function"

I have looked at the docs for apollo-fetch. I have a few questions before I dive in:

  1. Can a custom apollo-fetch middleware allow me to sign the request? I would need access to the method, headers, and body of the request that will be sent.

  2. If not, is there an alternative way to achieve Batching and signing the batched request?

  3. Is apollo-fetch production ready?

  4. Is apollo-fetch experimental or definitely the way Apollo client is developing in the future.

Thanks

civicevolution commented 6 years ago

I solved this problem by using my custom fetch function by creating an apollo fetch function.

First, my custom fetch function receives the uri & options, signs them, and then calls the system fetch():

myCustomFetch = function(uri, options) {
  var request;
  request = {
    uri: uri,
    options: options
  };
  request = SVC("API_USER_AUTH").apigClient.formatGraphQlAsIam(request);
  return fetch(request.uri, request.options);
}

In order to create my ApolloClient, I need to create a Link which will use myCustomFetch()

Here is the link based on HttpLink:

httpConfig = {
  uri: 'https://domain.tld/graphql',
  fetch: myCustomFetch
}

link = ApolloLink.from([
  new HttpLink(httpConfig)
]);

Here is the link based on BatchHttpLink:

apolloFetch = createApolloFetch(
  {
    uri: 'https://domain.tld/graphql',
    customFetch: myCustomFetch
  }
);

batchConfig = {
  fetch: apolloFetch
};

link = ApolloLink.from([
  new BatchHttpLink({...batchConfig})
]);

Now that I have the link, I can create my Apollo client:

  imCache = imCache = new InMemoryCache( {} );

  const clientConfig = {
    link: ApolloLink.from([
      link
    ]),
    cache: imCache.restore({})
  };

  client = new ApolloClient( clientConfig );

I used ApolloLink.from([]) because I use additional links for error, setting an auth token and retry. They've been removed for simplicity.

Remember to import the needed objects:

import { createApolloFetch } from 'apollo-fetch';
import { ApolloLink } from 'apollo-link';
import { BatchHttpLink } from "apollo-link-batch-http";
import { HttpLink } from 'apollo-link-http';
import ApolloClient from 'apollo-client';
import { InMemoryCache } from 'apollo-cache-inmemory';
pschaub commented 6 years ago

Related or maybe good to know: https://github.com/apollographql/apollo-link/pull/364