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

Remove mentions of APQ and Stripping ignored tokens from Persisted Operations RFC #266

Merged
merged 2 commits into from
Oct 9, 2023
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
33 changes: 6 additions & 27 deletions rfcs/PersistedOperations.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# Persisted Operations

This RFC is intended to add (Automatic) Persisted Operations to the spec. A persisted operation contains
This RFC is intended to add Persisted Operations to the spec. A persisted operation contains
a _hash_ of the `operation` that we are sending to the server, the server can translate this to the proper
`operation` and execute it.

Expand All @@ -11,16 +11,11 @@ With Persisted Operations we have a few goals:
- Lock down the number of request permutations (the server can choose to only accept persisted operations it is aware of)

## Flow

### Producing the document-id

To produce a deterministic `documentId` we need a standardised way of stringifying to a minimal relevant document.
In doing so we also avoid producing different ids for documents that contain a possible extra redundant character.

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).
The only non-excessive ignored token is a single space character (U+0020) that must be inserted between two non-punctuator tokens.

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.
To produce a `documentId` we'll take a document containing the operation, including its fragments and perform a hashing algorithm like
SHA-256 on the document. Now the the correlation of a document and its hash can be persisted to a GraphQL Server and the client can
send this hash in GraphQL Requests.

### Sending

Expand All @@ -36,28 +31,12 @@ By definition `query` contains cacheable data so we can send this either as a `G

When sending GraphQL variables along with a `query` operation over the `GET` HTTP method, the URL size limit (typically 2048
characters) should be considered if the URL's query string is to be employed to encode these GraphQL variables. If this is an
issue, one should consider utilizing a `POST` request's `body` or an HTTP header to encode these variables.
issue, one should consider utilizing a `POST` request's `body` or an HTTP header to encode these variables. When using a HTTP header
to encode the variables the server has to add this request-header to the `Vary` header to ensure the correct cache-key is achieved.

### Executing

When a server receives a request containing only `documentId` it is assumed that the server can perform this lookup, when the lookup
fails the server responds with a status code 400 or 404 and denies the request. When the persisted operation can be found the server
can assume that it's dealing with a valid GraphQL document, however, the schema could have changed so the server _should_ still validate
before executing.

## Automatic persisted operations

We can expand persisted operations with an "Automatic" mode which was initially pioneered by [Apollo](https://www.apollographql.com/docs/apollo-server/performance/apq/)
with the automatic mode we have no expectation of the server being aware of our `documentId` before sending it. This means we
can "teach" the server about the documents we are sending on the fly.

For hashing and sending the persisted operation we keep the aforementioned flow, however, the flow after is altered as the server
responds with a status code 200 and a GraphQLError containing a message of `PersistedOperationNotFound` when it supports persisted
operations, when persisted operations are unsupported the server can change the error message to `PersistedOperationNotSupported`.

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
a new request containing both `documentId` and `query` in the parameters to make the server aware of the correlation. The server can
verify that the `documentId` and `query` are correctly being associated by performing the stringify and hash steps, to avoid
malicious actors inserting faux associations.

The server can now save the association between the `documentId` and `query` for future requests.