Closed oprypkhantc closed 1 year ago
@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.
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.
Thanks @oprypkhantc, I'm clear on the implementation and it looks good - merging.
Awesome @oojacoboo :)
Closes #566
This PR implements Automatic persisted queries by Apollo:
PERSISTED_QUERY_NOT_SUPPORTED
code to avoid Apollo clients from trying to use persisted queriesThis diagram shows the process:
Essentially, whenever an Apollo client with enabled APQ attempts to execute a query, these are the steps taken:
query { field }
needs to be executed, it doeshash('sha256', 'query { field }')
and sends the resulting hash to the server:/graphql?hash=7b82cd908482825da2a4381cdda62a1384faa0c1b4c248e086aa44aa59fb9cd8
CachePersistedQueryLoader
. It checks if there's a7b82cd908482825da2a4381cdda62a1384faa0c1b4c248e086aa44aa59fb9cd8
entry in cache.extensions.code = 'PERSISTED_QUERY_NOT_FOUND'
. That tells Apollo clients that persisted queries are supported and it should go to the next step/graphql?hash=7b82cd908482825da2a4381cdda62a1384faa0c1b4c248e086aa44aa59fb9cd8&query=query { field }
7b82cd908482825da2a4381cdda62a1384faa0c1b4c248e086aa44aa59fb9cd8
matches the provided query usinghash('sha256', 'query { field }') === $hash
. If so, the server safely knows that this hash7b82cd908482825da2a4381cdda62a1384faa0c1b4c248e086aa44aa59fb9cd8
will always correspond to queryquery { 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 thequery { field }
in the first place.query { field }
, they'll also first try executing it with just the hash:/graphql?hash=7b82cd908482825da2a4381cdda62a1384faa0c1b4c248e086aa44aa59fb9cd8
/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.