Apollo-link: Feature idea: Allow string or function `uri` option for HttpLink

Created on 21 Nov 2017  路  9Comments  路  Source: apollographql/apollo-link

Hey, there!

I'm currently upgrading from [email protected] to [email protected], and I'm running into a slight issue since HTTPFetchNetworkInterface was removed. Originally, I had something like the following (based on this issue):

import { HTTPFetchNetworkInterface } from 'apollo-client';

export class CustomApolloNetworkInterface extends HTTPFetchNetworkInterface {
  fetchFromRemoteEndpoint({ request, options }) {
    const { _uri } = this;
    const { operationName } = request;
    if (operationName) this._uri += `?opname=${operationName}`;

    const requestPromise = super.fetchFromRemoteEndpoint({ request, options });
    this._uri = _uri;

    return requestPromise;
  }
}

export default function createApolloClient() {
  const networkInterface = new CustomApolloNetworkInterface(`${url}/graph/graphql`);
  return new ApolloClient({ networkInterface });
}

This let me add the operation name as a query parameter to each request, which I used for automated alerting and graphing based on operation timings. Since the custom network interface is no longer around, this is slightly more difficult.

My proposal is to make the uri option for HttpLink to be opened up to a function as well as a string. The actual fetch would turn out to be something like:

let uriValue = typeof uri === 'function' ? uri(operation) : uri;  // do a safer function validation here :P
fetcher(contextURI || uriValue, fetcherOptions).then(() => { ... });

That'd allow for something like:

export default function createApolloClient() {
  const link = new HttpLink({
    uri: (operation) => `${url}/graph/graphql?opname=${operation.operationName}`
  });

  const client = new ApolloClient({
    link,
    cache: new InMemoryCache().restore(IS_BROWSER ? window.__APOLLO_STATE__ : {}),
  });

  return client;
}

What do y'all think? If there's a better way to do this, please let me know! Thanks!

Most helpful comment

@Ingibjorg I'd be open to the API described in the original issue with a PR!

All 9 comments

@stephenkao this is super interesting! One way you can do it is with the context key during an operation. So if you are using just the client client.query({ query, context: { uri: /* my generated uri */ } }) or if using graphql from react-apollo: graphql(QUERY, { options: { context: { uri: /* my generated uri */ }})

Good suggestion @stephenkao !
@jbaxleyiii is there any way of changing the uri dynamically on the client itself instead of the method you suggest?

In my case, I'm toggling between two different environments in my app and it's not really optimal to check the stored uri value every time I make a query/mutation and then add it to the operation. I'm also using an npm module for some components with queries/mutations so I would have to pass the uri to them as well. It would be great if we could change the uri on the client itself for all operations.

@Ingibjorg I'd be open to the API described in the original issue with a PR!

Fantastic! I'm working a pull request now. :)

done and merged! :yay:!

@stephenkao so after a couple other issues, I realized this was already possible!

const customFetch = (uri, options) => {
  const { operationName } = JSON.parse(options.body);
  return fetch(`${uri}/graph/graphql?opname=${operationName}`, options);
};

const link = createHttpLink({ fetch: customFetch });

I've reverted the change here which also keeps the library under the 4kb threshold. Is this okay to you?

Thank you so much for bringing this issue up and the work on the PR, I really appreciate it and it helped to solve 3 other issues without adding anything else to the library!

@jbaxleyiii Yeah, this looks fantastic! Thank you so much for following up on this.

@jbaxleyiii Adding the operationNames to the querystring is really valuable for metrics/analytics but I'm not thrilled with having to do a JSON.parse in the customFetch.. especially on tablets making large queries..

What about having HttpLink & BatchHttpLink simply pass the body object as a non-standard option to the fetcher? (e.g. in apolloRequestBody)

Another option is to make serializeFetchParameter customisable - it could then do a no-op and leave the JSON.stringify to the custom fetcher.

Thanks!

Following works for me.

const httpLink = createHttpLink({ uri: ({ operationName }) => {
return ${uri}/${operationName};
} });

Was this page helpful?
0 / 5 - 0 ratings