Open dotansimha opened 1 year ago
The store could also be external, e.g. an S3 bucket as in a setup with many GraphQL clienrs you rarely can embed all all client operations within the gateway at deployment time. for several reasons: many different client versions (where every client has different GraphQL operations that beed to be supported) or logistical issues (client code being in many different repositories that you may or may not control).
One thing that is often overlooked isbthat a GraphQL document can contain multiple executable operation definintions, where the actual operation being executed is determined via the operationName
parameter. I think the persisted document protocol should still allow that to be as spec compatible as possible.
Hey,
Wanted to jump in here to say that maybe we can formalise the request format with the rfc not sure if y'all have opinions here but omitting query
is currently not allowed when strictly following the http
spec. Specifying operationName
should be allowed so folks are allowed to specify an exact operation from a hash imho.
As for the protocol, I would prefer a non-/graphql
endpoint protocol extension -and rather a /<prefix>/<documentHash>/<optionalExecutionalOperationName>
format. Where depending on whether it is a mutation or query, either a POST request or GET request must be used. We would also need to figure out how to support this with The GraphQL over WS and GraphQL over SSE protocols.
Summary
Persisted Operations (or, Persisted Queries) goal is to provide a security and hardening layer on top of the GraphQL engine. Instead of executing a regular GraphQL operation, the clients are required to use a special string (hash) that's being translated into the actual GraphQL operation body.
For a more comprehensive/official spec, please refer to https://github.com/graphql/graphql-over-http/pull/264/files
Hash / Identifier
The hash for the persisted operation is usually generated at build/development time. The hash could be a generated, consistent string, an opaque value, or an actual computed hash based on the GraphQL operation body (for example, SHA256).
Store
The store is in charge of storing the actual mapping between the hash and the GraphQL operation body.
Simple Key->Value structure
A simple approach to providing the data to the store is to use a JSON key->value structure, as implemented by GraphQL-Yoga (https://the-guild.dev/graphql/yoga-server/docs/features/persisted-operations#quick-start).
This structure aims to be as simple as possible, and things like versioning can be part of the hash.
Apollo Persisted Query Manifest
Apollo is managing their own format, based on JSON, for managing the stored data. This format is called
apollo-persisted-query-manifest
.type
field andname
field seem like an extraction to make it easier later for the Studio (to avoid parsing again)1
. It's also hardcoded in Apollo Client as v1 (https://github.com/apollographql/apollo-client/blob/9229e0e1e65bb8ba199a5d2411d05debcb9d73c3/src/link/persisted-queries/index.ts#L269), so it seems like the version/structure does not change.Execution
Prior to the GraphQL engine execution, the gateway needs to translate the hash into the GraphQL operation. To do that efficiently, the server needs to be able to load the data from the store, and then allow the client to specify the hash.
There are several ways of exposing the persisted operations as entry points:
Relay
Relay client sends a custom JSON structure to the server, and it is expected to be handled this way:
https://relay.dev/docs/guides/persisted-queries/#network-layer-changes
HotChocolate
Based on Relay's standard, but with
id
field instead ofdoc_id
. (https://chillicream.com/docs/hotchocolate/v13/performance/persisted-queries#client-expectations)Wundergraph's HTTP routes
By default, Wundergraph disables the GraphQL endpoint (
/graphql
) and exposes HTTP-based routes for accessing the GraphQL operation.This process also involved exposing the GraphQL queries as
GET
operations, and GraphQL mutations asPOST
operations:Will be translated into:
Variables are either passed as
?variables={"myVar": "1"}
or?myVar=1
.Apollo manifest
Apollo is using the GraphQL
extensions
field to specify the store manifest version and the hash, overPOST
:Apollo also allows users to enable persisted operations over
GET
.Non-persisted operations
A gateway can choose to allow the execution of non-persisted operations.