Apollo-client: HTTP GET for fetching?

Created on 19 Oct 2016  路  23Comments  路  Source: apollographql/apollo-client

If my understanding were correct, Apollo always uses HTTP POST to fetch the data. Unfortunately it becomes more challenging to cache since many Varnish based solutions cache GET request only (https://www.fastly.com/blog/api-caching-part-i & http://stackoverflow.com/questions/31539043/how-to-cache-post-requests-with-varnish)

  public fetchFromRemoteEndpoint({
    request,
    options,
  }: RequestAndOptions): Promise<IResponse> {
    return fetch(this._uri, assign({}, this._opts, {
      body: JSON.stringify(printRequest(request)),
      method: 'POST',
    }, options, {
      headers: assign({}, {
        Accept: '*/*',
        'Content-Type': 'application/json',
      }, options.headers),
    }));
  };

Is it possible to change this to HTTP GET? If not, what would be best way to do server side edge caching on Apollo/GraphQL?

Most helpful comment

Most GraphQL endpoints, FB included, will contextualised their responses depending of the authenticated user.

So does basically every authenticated API. That does not eliminate the value or desire to cache responses. Applications using GraphQL would still benefit from ETag caching which could drastically reduce the amount of unchanged data that needs to be transferred across the wire when re-fetching data. There's some good background on how that technology works in this Heroku Dev Center article.

It should be up to the server to determine if and when resources can be cached by downstream clients (and to opt into performance enhancements like ETag). The server (along with any intermediaries like caching proxies, CDNs, etc) is still responsible for ensuring that caches respect any authentication headers or cookies, and for making sure that query params are respected as part of cache keys.

By using POST instead of GET for idempotent, read-only requests, you're merely preventing servers from being able to opt into the performance benefits that caching offers.

The simplest change to support this in apollo-client would just be to tweak the network interface to use GET if a query (not mutation) is being performed. If you're primarily concerned about compatibility with existing clients, then you could make this an option. But there's really no reason for it not to be the default.

All 23 comments

@fikriauliya you could definitely write your own network interface that uses GET instead of POST, and just pass the query and options in there. However, you will want to be careful with caching on the server side for any queries that send a different response depending on the authenticated user.

i think that once apollostack/apollo-server#121 is done, we should add or modify existing interface to support GET as well.
until GET will be supported on server there is no reason to support it on client.

@DxCx I think the two are independent, because apollo client can be used with any server, and apollo server can be used with any client.

Yep, got it. Altough i havnt seen any other GraphQL GET Server, but from your answer i understand that this is probably just me lacking this information..

Yes, express-graphql supports GET - but it gets a bit messy 馃槃

Thx for the answer, I would close this issue, but feel free to reopen if needed.

This section on the GraphQL docs explicitly states that GraphQL HTTP servers should handle HTTP GET and POST methods.

It seems like a good GraphQL client should use GET whenever possible in order to convey intent for safe/non-modifying queries, and to enable caching by intermediaries. Ultimately it would be great if servers supported Etags and Cache-Control headers to fully take advantage of this.

POST should only be used if the query is too long for a GET request (per-browser limits here) or if the request contains mutations.

It seems like a good GraphQL client should use GET whenever possible

If you have specific needs about when you want to use GET or POST, that seems like a pretty reasonable case for a custom network interface IMO.

I'm not sure there is a consensus in the community about which HTTP requests should be used when, so always sending the same thing seems like a sane default.

If you have specific needs about when you want to use GET or POST, that seems like a pretty reasonable case for a custom network interface IMO.

I wouldn't say that I have any "specific needs" other than following HTTP standards to maximize caching by intermediaries and browsers. That seems like something everybody would benefit from having available by default.

I'm not sure there is a consensus in the community about which HTTP requests should be used when, so always sending the same thing seems like a sane default.

I haven't been following the GraphQL community at all, so it's entirely possible that this is true. If it is, though, I can't imagine why. HTTP is very clear as to the semantics of safe, cacheable methods like GET vs. POST.

Is there some reason you can think of why somebody _wouldn't_ want to enable caching, other than the increase in backend complexity from handling both methods?

If there is indeed no consensus, this might be a good place to lead the way and show the community how much better/faster your solution can be by leveraging browser caching the way it's meant to be used.

I agree there is an opportunity to lead the community here. There are a few things to be figured out:

  1. Do all queries make sense to be sent over GET?
  2. How should the server decide how long a response should be cached for, especially in the case where a query loads many different types of entities? (perhaps, it should use the shortest cache length of all objects in the result?)
  3. Can we leverage stuff like eTags to avoid retransmitting results that haven't changed?

I think it's worth opening a new issue to talk about it. @glasser was interested in this concept as well!

Just want to provide another use case which can benefit from supporting GET.

e.g. In server side, readonly database connection can be used for GET request. Read-write database connection will be used for POST request.

Without supporting GET request, server has to parse graphql statement first before deciding how to handle a query.

Sure, can someone open a new issue? I don't like having feature discussions on closed issues.

Does anyone want to take a stab at a specific design?

As @helfer pointed out, I believe that the main reason GraphQL is mainly using POST instead of GET is to precisely avoid caching. Most GraphQL endpoints, FB included, will contextualised their responses depending of the authenticated user.

It seems like a good GraphQL client should use GET whenever possible in order to convey intent for safe/non-modifying queries, and to enable caching by intermediaries.

The issue is the client has no knowledge if the server will contextualize the response so clients are prudent and use POST.

Nothing is stoping you for designing a graphql endpoint that does not contextualize anything and therefore would be friendly to some caching by a layer above but so far I haven't seen many / any of those.

The viewer pattern for example used by Relay would prevent caching at a layer above instantly.

But honestly I don't think that the general philosophy with GraphQL, I believe the intent is to contextualize as much as you can.

In the end, I don't think you can simply apply the same caching strategy you would do for a REST endpoint on a GraphQL endpoint. With GraphQL, the caching will most likely happen in your data loaders within your GraphQL server.

e.g. In server side, readonly database connection can be used for GET request. Read-write database connection will be used for POST request.
Without supporting GET request, server has to parse graphql statement first before deciding how to handle a query.

@tendant, it's not like you can bypass the graphql layer anyway so by the time your code hit your data loaders, you already know if it's a query or a mutation.

Most GraphQL endpoints, FB included, will contextualised their responses depending of the authenticated user.

So does basically every authenticated API. That does not eliminate the value or desire to cache responses. Applications using GraphQL would still benefit from ETag caching which could drastically reduce the amount of unchanged data that needs to be transferred across the wire when re-fetching data. There's some good background on how that technology works in this Heroku Dev Center article.

It should be up to the server to determine if and when resources can be cached by downstream clients (and to opt into performance enhancements like ETag). The server (along with any intermediaries like caching proxies, CDNs, etc) is still responsible for ensuring that caches respect any authentication headers or cookies, and for making sure that query params are respected as part of cache keys.

By using POST instead of GET for idempotent, read-only requests, you're merely preventing servers from being able to opt into the performance benefits that caching offers.

The simplest change to support this in apollo-client would just be to tweak the network interface to use GET if a query (not mutation) is being performed. If you're primarily concerned about compatibility with existing clients, then you could make this an option. But there's really no reason for it not to be the default.

Look into the source, looks like there is an option could override default post method, I haven't tested yet, but it might be helpful. https://github.com/apollostack/apollo-client/blob/master/src/transport/networkInterface.ts#L161

Here's a related critique on GraphQL implementations' cache-unfriendliness: http://kellysutton.com/2017/01/02/do-we-need-graphql.html

There are some great ideas flying around, but I think for now this should live in a user land network interface. If that network interface gets popular and sees a lot of use we will definitely consider adding it to Apollo Client core. Currently though Apollo Client gives you great tools to handle this yourself however you would like.

Closing as for now this is a user land concern. We will endorse anything you come up with :+1:

Any news on this?

@johnunclesam This is the wrong place for that discussion since apollo-link-http is now responsible for this. The issue was solved in https://github.com/apollographql/apollo-link/issues/236#issuecomment-348176745

@fikriauliya you could definitely write your own network interface that uses GET instead of POST, and just pass the query and options in there. However, you will want to be careful with caching on the server side for any queries that send a different response depending on the authenticated user.

As per your comment, I had changed method call to
Request.Builder builder = original.newBuilder().method(original.method(), null);
OR
Request.Builder builder = original.newBuilder().get();
but while debugging i found that the class RealInterceptorChain having below method requesting POST method.
@Override public Response proceed(Request request) throws IOException {
return proceed(request, streamAllocation, httpCodec, connection);
}

I want to my request method should be GET

@mshaileshr It looks like you're talking about Java here? apollo-client is a JS project.

@mshaileshr It looks like you're talking about Java here? apollo-client is a JS project.

YES

Was this page helpful?
0 / 5 - 0 ratings