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

Apollo Automatic persisted queries #611

Merged
merged 7 commits into from
Aug 4, 2023

Conversation

oprypkhantc
Copy link
Contributor

Closes #566

This PR implements Automatic persisted queries by Apollo:

  • by default it makes sure to respond with a correct PERSISTED_QUERY_NOT_SUPPORTED code to avoid Apollo clients from trying to use persisted queries
  • if requested by the user, it will use automatic persisted queries through cache. Of course the user is free to implement a custom persisted query loader if they wish to.

This diagram shows the process:

image

Essentially, whenever an Apollo client with enabled APQ attempts to execute a query, these are the steps taken:

  1. Given query query { field } needs to be executed, it does hash('sha256', 'query { field }') and sends the resulting hash to the server: /graphql?hash=7b82cd908482825da2a4381cdda62a1384faa0c1b4c248e086aa44aa59fb9cd8
  2. Server sees that hash and triggers a persisted query loader, in our case - CachePersistedQueryLoader. It checks if there's a 7b82cd908482825da2a4381cdda62a1384faa0c1b4c248e086aa44aa59fb9cd8 entry in cache.
  3. Given there's none, it haults execution and returns a GraphQL error with extensions.code = 'PERSISTED_QUERY_NOT_FOUND'. That tells Apollo clients that persisted queries are supported and it should go to the next step
  4. Apollo client sends BOTH the query and the hash to the server: /graphql?hash=7b82cd908482825da2a4381cdda62a1384faa0c1b4c248e086aa44aa59fb9cd8&query=query { field }
  5. Server sees that hash and again triggers a persisted query loader. It once again sees that there's nothing in cache, but this time the full query was provided. It checks if provided hash 7b82cd908482825da2a4381cdda62a1384faa0c1b4c248e086aa44aa59fb9cd8 matches the provided query using hash('sha256', 'query { field }') === $hash. If so, the server safely knows that this hash 7b82cd908482825da2a4381cdda62a1384faa0c1b4c248e086aa44aa59fb9cd8 will always correspond to query query { field }. It will write it to the cache: cache->set('7b82cd908482825da2a4381cdda62a1384faa0c1b4c248e086aa44aa59fb9cd8', 'query { field }'). Afterwards, the request is processed normally, as if the client sent just the query { field } in the first place.
  6. Next time an Apollo client (that or any other) wants to execute query query { field }, they'll also first try executing it with just the hash: /graphql?hash=7b82cd908482825da2a4381cdda62a1384faa0c1b4c248e086aa44aa59fb9cd8
  7. This time, the server will actually have a corresponding query for that hash in cache and will simply execute it as if a client sent /graphql?query=query { field }

The reason authentication (or any other variables or headers) doesn't matter here is that only the string query { field } is cached. If two different clients try to use the same persisted query, both requests will still go through the full parsing, validation and execution process and may yield different responses.

Hope that clears it up.

@oojacoboo
Copy link
Collaborator

@oprypkhantc thanks, this looks good.

I am wondering, why the client needs to send the sha256 hash and the query, and not just simply the sha256. If this is truly deterministic, as the code is written, why wouldn't the server just set the hash key on the first persisted query request, instead of requiring the client to resubmit the request? It seems like unnecessary communication and complications. The server is already validating the hash.

@oprypkhantc
Copy link
Contributor Author

The first persisted query request in this case would only contain the hash of the query from the client, say 7b82cd908482825da2a4381cdda62a1384faa0c1b4c248e086aa44aa59fb9cd8, but it doesn't have the query. So if the cache doesn't have a query for that hash, the only option server has is to request the client to provide the query, because it's impossible to extract the query from the hash.

Plus, if your APQ is configured correctly, 99% of those "first" persisted query requests won't require another request from the client because the cache would be populated already from one of the previous requests.

@oojacoboo
Copy link
Collaborator

Thanks @oprypkhantc, I'm clear on the implementation and it looks good - merging.

@oojacoboo oojacoboo merged commit 7b41bc9 into thecodingmachine:master Aug 4, 2023
5 checks passed
@oprypkhantc
Copy link
Contributor Author

Awesome @oojacoboo :)

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

Successfully merging this pull request may close these issues.

Implement Persisted Queries mechanism to easily allow server-side Caching
2 participants