Skip to content

Commit 1635ac7

Browse files
authored
RFC: persisted operations (#253)
1 parent 4569bd2 commit 1635ac7

File tree

1 file changed

+59
-0
lines changed

1 file changed

+59
-0
lines changed

rfcs/PersistedOperations.md

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
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

Comments
 (0)