|
| 1 | +# Persisted Operations |
| 2 | + |
| 3 | +This RFC is intended to add (Automatic) Persisted Operations to the spec. A persisted operation contains |
| 4 | +a _hash_ of the `operation` that we are sending to the server, the server can translate this to the proper |
| 5 | +`operation` and execute it. |
| 6 | + |
| 7 | +With Persisted Operations we have a few goals: |
| 8 | + |
| 9 | +- Reduce request transfer size (GraphQL documents have a deterministic size) |
| 10 | +- Support caching (avoid running into max-url size constraints when using the `GET` HTTP method) |
| 11 | +- Lock down the amount of request permutations (the server can choose to only accept persisted operations it is aware of) |
| 12 | + |
| 13 | +## Flow |
| 14 | + |
| 15 | +### Producing the document-id |
| 16 | + |
| 17 | +To produce a deterministic `documentId` we need a standardised way of stringifying to a minimal relevant document. |
| 18 | +In doing so we also avoid producing different ids for documents that contain a possible extra redundant character. |
| 19 | + |
| 20 | +We need to produce a minimal GraphQL Document according to the spec, [stripping all ignored tokens](https://spec.graphql.org/October2021/#sec-Language.Source-Text.Ignored-Tokens). |
| 21 | +The only non-excessive ignored token is a single space character (U+0020) that must be inserted between two non-punctuator tokens. |
| 22 | + |
| 23 | +After stringifying we can produce a SHA-256 hash of the stringified document which we can save somewhere and use as an identifier for our GraphQL server. |
| 24 | + |
| 25 | +### Sending |
| 26 | + |
| 27 | +When sending the persisted operation we will potentially be violating the current Request parameters where we say that `query` |
| 28 | +is a _required_ property. The proposal here is to add an additional _optional_ property `documentId` which has to be present |
| 29 | +when `query` isn't. We disallow neither `documentId` and `query` being absent when performing a GraphQL Request. |
| 30 | + |
| 31 | +The `documentId` would be the hashed representation of the stringified GraphQL document. |
| 32 | + |
| 33 | +We can send all the operation kinds as a persisted operation, however we should make the distinction between `query` and `mutation`. |
| 34 | +By definition `query` contains cacheable data so we can send this either as a `GET` or a `POST` so we follow the spec, however a |
| 35 | +`mutation` represents side-effects so we should only send this as a `POST` request when leveraging persisted operations. |
| 36 | + |
| 37 | +### Executing |
| 38 | + |
| 39 | +When a server receives a request containing only `documentId` it is assumed that the server can perform this lookup, when the lookup |
| 40 | +fails the server responds with a status-code 400 or 404 and denies the request. When the persisted operation can be found the server |
| 41 | +can assume that it's dealing with a valid GraphQL document, however the schema could have changed so the server _should_ still validate |
| 42 | +before executing. |
| 43 | + |
| 44 | +## Automatic persisted operations |
| 45 | + |
| 46 | +We can expand persisted operations with an "Automatic" mode which was initially pioneered by [Apollo](https://www.apollographql.com/docs/apollo-server/performance/apq/) |
| 47 | +with the automatic mode we have no expectation of the server being aware of our `documentId` before sending it. This means we |
| 48 | +can "teach" the server about the documents we are sending on the fly. |
| 49 | + |
| 50 | +For hashing and sending the persisted operation we keep the aforementioned flow, however the flow after is altered as the server |
| 51 | +responds with a status-code 200 and a GraphQLError containing a message of `PersistedOperationNotFound` when it supports persisted |
| 52 | +operations, when persisted operations are unsupported the server can change the error message to `PersistedOperationNotSupported`. |
| 53 | + |
| 54 | +The client is made aware of the server not knowing the document we are dealing with by means of this error, the client can now send |
| 55 | +a new request containing both `documentId` and `query` in the parameters to make the server aware of the correlation. The server can |
| 56 | +verify that the `documentId` and `query` are correctly being associated by performing the stringify and hash steps, this to avoid |
| 57 | +malicious actors inserting faux associations. |
| 58 | + |
| 59 | +The server can now save the association between the `documentId` and `query` for future requests. |
0 commit comments