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

Can you do Apollo Server caching if you use schema stitching #1380

Closed
ra-external opened this issue Apr 22, 2020 · 17 comments
Closed

Can you do Apollo Server caching if you use schema stitching #1380

ra-external opened this issue Apr 22, 2020 · 17 comments

Comments

@ra-external
Copy link

ra-external commented Apr 22, 2020

I have an Apollo Server that uses graphql-tools (at the moment graphql-tools-fork) to do schema-stitching (this Apollo Server acts as an interface between an Apollo Client frontend and several backends, some of which are REST, some of which are GraphQL: the interface combines all of the backends into one GraphQL API for the front).

We want to do caching of API calls from this server to the remote APIs and Apollo Server caching was recommended to us as one way to do this. However, near the top of that page it says

Important note on compatibility: Setting cache hints is currently incompatible with the graphql-tools implementation of schema stitching, because cache hints are not appropriately communicated from one service to the other.

It's unclear to me whether this means that all Apollo Server caching won't work, or whether only the hinting. That is, whether the entire documentation page above is moot if you're using schema-stitching, or whether part of it -- for instance Serving HTTP cache headers -- is still valid. I suspect that one can't use caching at all, since Serving HTTP cache headers seems to depend on maxAge, which seems to depend on the cache hints.

@yaacovCR
Copy link
Collaborator

cacheControl has been addressed before by a number of relevant issues (#451, #890, #903)

I think this is the current state of the problem:

  1. graphql-tools v4 and v5 both copy static cache hints from subschemas to the gateway schema
  2. apollo-server does not read cache hints from extensions
  3. neither graphql-tools v4 nor graphql v5 support dynamic cache hints -- that seems possible, but a bit of work with a number of moving pieces.
  4. static cache hint support is limited for remote schemas because if you use the introspection result, the introspection result used to build the schema will be devoid of directives. if you have access to the original schema sdl, i.e. then this should be possible. Another idea is to create a way to easily add directives to the gateway schema, but this requires the use of extend, see above.

I am not 100% sure that the above is correct, but I think it is a good start!

@yaacovCR
Copy link
Collaborator

yaacovCR commented Apr 23, 2020

In terms of progress on point 4, see graphql/graphql-spec#300

@yaacovCR
Copy link
Collaborator

yaacovCR commented Apr 23, 2020

I think the best solution of the above is to use an insight of @macrozone #890 (comment) slightly differently, if the subschema is local, presumably you have access to the directives, and can set a static hint, but if the subschema is remote and is respecting the directive, even though you don't have the directive, cache headers will be returned. The gateway or wrapping schema could be set up to automatically set a dynamic cache control hint based on the headers in the response.

@yaacovCR
Copy link
Collaborator

yaacovCR commented Apr 23, 2020

Reasoning about this one piece at a time, the available hooks seems to be here:

https://github.com/ardatan/graphql-tools/blob/master/src/wrap/resolvers.ts#L111 and
https://github.com/ardatan/graphql-tools/blob/master/src/stitch/mergeInfo.ts#L167 and
https://github.com/ardatan/graphql-tools/blob/master/src/wrap/makeRemoteExecutableSchema.ts#L57

Ie you can pass in your own custom proxying resolvers (and merging resolvers if you are using stitching with type merging) that have access to info object from gateway schema and can set dynamic cache hints based on results of delegation.

How to get access to response headers? See #969 (comment)

That suggestion also raises the idea of using a custom extension other than cache control to set caching headers directly, but advantage of using dynamic cacheControl -- I believe -- is that it let's you use other Apollo Server caching features.

To Do:

  1. Provide code sample of link or fetcher that copies response headers from remote schema to graphql context.
  2. Provide code sample of custom proxying resolver that sets cacheControl headers based on context value after completion of delegation.

@yaacovCR yaacovCR changed the title Can you do Apollo Server caching if you use 'graphql-tools'? Can you do Apollo Server caching if you use schema stitching Apr 23, 2020
@yaacovCR
Copy link
Collaborator

Or, we could also provide the link/fetcher with the info argument and it could be responsible for setting the dynamic hint directly, obviating the need entirely for the second step.

That seems easier!

yaacovCR added a commit that referenced this issue Apr 23, 2020
info argument may have additional functionality added to it via extensions that custom fetchers or links defined on subschemas may wish to use, such as dynamic cacheControl hints, see #1380
yaacovCR added a commit that referenced this issue Apr 23, 2020
= info argument may have additional functionality added to it via extensions.
= fetchers or links defined on subschemas may wish to hook into this functionality, such as dynamic cacheControl hints, see #1380.
= in some cases, the fetcher/link is the only appropriate place for this hook, as additional metadata about the response, such as caching headers, may be lost after the fetcher/link completes, as only data and errors are stitched back into the response.
@yaacovCR
Copy link
Collaborator

After #1397, the remaining bit is creating the custom fetcher or link that parses the headers in response and sets the cache hint on the root field dynamically.

At least, that's how it should work!

@ra-external , are you able to set up a failing test demonstrating the desired functionality with apollo-server, akin to how upload.test.ts uses express-graphql?

That would help a lot.

@yaacovCR
Copy link
Collaborator

In v6, this will be a custom executor that sets a dynamic cache hint based on the headers in the response. It could still be a custom link, it would just need to be wrapped with linkToExecutor

@m-sanders
Copy link

Hi

I have a similar use case. I've just moved from the fork to the latest graphql-tools. We have an Apollo Server acting as a gateway for several APIs (GraphQL & Rest). We would like to transform the stitched schema to include cacheControl directives on various queries and fields in order to leverage Apollo Server Caching. I am not concerned out the upstream cache control headers. I am not as concerned about passing along cache control header from the link as upstream endpoint does not set any cache headers. Is this possible or related to this issue?

@yaacovCR
Copy link
Collaborator

I think this issue is about cacheControl from gateway to subschemas and you are talking about from client to gateway.

For your issue, I think you might be able to use type merging, which should merge directives, but I have not tested. You could also just manually merge the gateway schema with a non executable schema that has the directives.

The problem is that there is no easy way in graphql to extend existing fields with directives, whether by extensions or otherwise.

@oaksoe
Copy link

oaksoe commented Jul 20, 2020

Reasoning about this one piece at a time, the available hooks seems to be here:

https://github.com/ardatan/graphql-tools/blob/master/src/wrap/resolvers.ts#L111 and
https://github.com/ardatan/graphql-tools/blob/master/src/stitch/mergeInfo.ts#L167 and
https://github.com/ardatan/graphql-tools/blob/master/src/wrap/makeRemoteExecutableSchema.ts#L57

Ie you can pass in your own custom proxying resolvers (and merging resolvers if you are using stitching with type merging) that have access to info object from gateway schema and can set dynamic cache hints based on results of delegation.

How to get access to response headers? See #969 (comment)

That suggestion also raises the idea of using a custom extension other than cache control to set caching headers directly, but advantage of using dynamic cacheControl -- I believe -- is that it let's you use other Apollo Server caching features.

To Do:

  1. Provide code sample of link or fetcher that copies response headers from remote schema to graphql context.
  2. Provide code sample of custom proxying resolver that sets cacheControl headers based on context value after completion of delegation.

Thanks! Tried this on stitching with two services (author and chirp). It works!
https://github.com/oaksoe/gql-stitching-to-federation/blob/spike/caching-with-stitching/stitch-server.js

Whether it's static or dynamic cache hints, will calculate minimum max-age field and set it to final response cache-control header.

@yaacovCR
Copy link
Collaborator

Great!! Thanks for posting full example. Would it be ok to copy code to new examples folder within this project. A PR from you would be welcome, otherwise we could copy.

Just a note for those falling along, the example uses v5 rather than v6.

In terms of your last paragraph, you mean that subschema static and dynamic hints are being passed through, but as far as i can tell you are passing them all through at the gateway dynamically, right?

@oaksoe
Copy link

oaksoe commented Jul 20, 2020

@yaacovCR Sure. I will create PR :)

Yes. at the gateway, passing dynamically. Just that I have set static and dynamic hints at the implementing services, to test them out.

@yaacovCR
Copy link
Collaborator

yaacovCR commented Oct 6, 2020

Closing this for now, although PR still welcome with example...

@yaacovCR yaacovCR closed this as completed Oct 6, 2020
@oaksoe
Copy link

oaksoe commented Oct 7, 2020

Hey @yaacovCR sorry for delay for PR with example. I tried to replicate my js codes in typescript with graphql-tools v5 but stitching didn't work. So, instead of debugging further, I was thinking why not do this example with v6. Will try to get it done this weekend.

@oaksoe
Copy link

oaksoe commented Oct 18, 2020

@yaacovCR I have created example, but don't have permission to push the new branch I created.

@yaacovCR
Copy link
Collaborator

You should be able to open a pull request?

@oaksoe
Copy link

oaksoe commented Oct 18, 2020

created PR => #2119

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

No branches or pull requests

4 participants