@@ -32,6 +32,51 @@ thus should generate different document identifiers.
3232A _ document identifier_ must either be a _ prefixed document identifier_ or a
3333_ custom document identifier_ .
3434
35+ A _ document identifier_ must only contain colons (` : ` ) and characters that are
36+ defined as
37+ [ ` unreserved ` in RFC3986] ( https://datatracker.ietf.org/doc/html/rfc3986#section-2.3 )
38+ (alphanumeric characters (` A-Z ` , ` a-z ` , ` 0-9 ` ), dashes (` - ` ), periods (` . ` ),
39+ underscores (` _ ` ), and tildes (` ~ ` )).
40+
41+ DocumentIdentifier ::
42+
43+ - PrefixedDocumentIdentifier
44+ - CustomDocumentIdentifier
45+
46+ PrefixedDocumentIdentifier ::
47+
48+ - UnreservedCharacter+ PrefixedDocumentIdentifierContinue+
49+
50+ PrefixedDocumentIdentifierContinue ::
51+
52+ - Colon UnreservedCharacter\*
53+
54+ CustomDocumentIdentifier ::
55+
56+ - UnreservedCharacter+ [ lookahead != Colon]
57+
58+ UnreservedCharacter ::
59+
60+ - Letter
61+ - Digit
62+ - ` - `
63+ - ` . `
64+ - ` _ `
65+ - ` ~ `
66+
67+ Letter :: one of
68+
69+ - ` A ` ` B ` ` C ` ` D ` ` E ` ` F ` ` G ` ` H ` ` I ` ` J ` ` K ` ` L ` ` M `
70+ - ` N ` ` O ` ` P ` ` Q ` ` R ` ` S ` ` T ` ` U ` ` V ` ` W ` ` X ` ` Y ` ` Z `
71+ - ` a ` ` b ` ` c ` ` d ` ` e ` ` f ` ` g ` ` h ` ` i ` ` j ` ` k ` ` l ` ` m `
72+ - ` n ` ` o ` ` p ` ` q ` ` r ` ` s ` ` t ` ` u ` ` v ` ` w ` ` x ` ` y ` ` z `
73+
74+ Digit :: one of
75+
76+ - ` 0 ` ` 1 ` ` 2 ` ` 3 ` ` 4 ` ` 5 ` ` 6 ` ` 7 ` ` 8 ` ` 9 `
77+
78+ Colon :: ` : `
79+
3580### Prefixed Document Identifier
3681
3782:: A _ prefixed document identifier_ is a document identifier that contains at
@@ -86,14 +131,46 @@ Would have this different _SHA256 hex document identifier_:
86131sha256:71f7dc5758652baac68e4a10c50be732b741c892ade2883a99358f52b555286b
87132```
88133
134+ Persisted Documents may contain multiple (named) operations and fragments too;
135+ so the following GraphQL document (with no trailing newline):
136+
137+ ``` graphql example
138+ query UserName ($id : ID ! ) {
139+ user (id : $id ) {
140+ name
141+ }
142+ }
143+
144+ query FriendsNames ($id : ID ! ) {
145+ user (id : $id ) {
146+ ... User
147+ friends {
148+ ... User
149+ }
150+ }
151+ }
152+
153+ fragment User on User {
154+ id
155+ name
156+ }
157+ ```
158+
159+ Would have the following _ SHA256 hex document identifier_ :
160+
161+ ``` example
162+ sha256:517c56d2ba0779653b7698881207f749509f331bdaccbe951a82c378bc869556
163+ ```
164+
89165### Custom Document Identifier
90166
91167:: A _ custom document identifier_ is a document identifier that contains no
92168colon symbols (` : ` ). The meaning of a custom document identifier is
93169implementation specific.
94170
95- Note: A 32 character hexadecimal _ custom document identifier_ is likely to be an
96- MD5 hash of the GraphQL document, as traditionally used by Relay.
171+ Note: A 32 character hexadecimal lower-case _ custom document identifier_ is
172+ likely to be an MD5 hash of the GraphQL document, as traditionally used by
173+ Relay.
97174
98175## Persisting a Document
99176
@@ -105,12 +182,11 @@ specific.
105182
106183Note: When used as an operation allow-list, persisted documents are typically
107184stored into a trusted shared key-value store at client build time (either
108- directly, or indirectly via an authenticated request to the server) such that
109- the server may retrieve them given the identifier at request time. This must be
110- done in a secure manner (preventing untrusted third parties from adding their
111- own persisted document) such that the server will be able to retrieve the
112- identified document within a _ persisted document request_ and know that it is
113- trusted.
185+ directly, or indirectly via authenticated requests to the server) such that the
186+ server may retrieve them given the identifier at request time. This must be done
187+ in a secure manner (preventing untrusted third parties from adding their own
188+ persisted document) such that the server will be able to retrieve the identified
189+ document within a _ persisted document request_ and know that it is trusted.
114190
115191Note: When used solely as a bandwidth optimization, as in the technique known
116192colloquially as "automatic persisted queries (APQ)," an error-based mechanism
@@ -130,12 +206,16 @@ deployed client.
130206
131207## Persisted Document Request
132208
133- A server MAY accept a _ persisted document request_ via ` GET ` or ` POST ` .
209+ A server MAY accept a _ persisted document request_ via an HTTP ` GET ` or ` POST `
210+ request to a _ GraphQL endpoint_ or subpath thereof.
134211
135212### Persisted Document Request Parameters
136213
137- :: A _ persisted document request_ is an HTTP request that encodes the following
138- parameters in one of the manners described in this specification:
214+ :: A _ persisted document request_ is an HTTP request that encodes the _ persisted
215+ document request parameters_ in one of the manners described in this
216+ specification.
217+
218+ :: The _ persisted document request parameters_ are as follows:
139219
140220- {documentId} - (_ Required_ , string): The string identifier for the Document.
141221- {operationName} - (_ Optional_ , string): The name of the Operation in the
@@ -145,20 +225,62 @@ parameters in one of the manners described in this specification:
145225- {extensions} - (_ Optional_ , map): This entry is reserved for implementors to
146226 extend the protocol however they see fit.
147227
228+ ### Persisted Document Request URL
229+
230+ To enable non-GraphQL HTTP tooling to better integrate with a Persisted Document
231+ Request, it is recommended that the URL to which a Persisted Document Request is
232+ sent is a subpath of the _ GraphQL endpoint_ containing the {documentId} and the
233+ {operationName} (if any) separated by a forward slash character (` / ` ). It is
234+ recommended that this practice is only followed when {documentId} is a prefixed
235+ document identifier, since the prefix helps avoid clashes with other subpaths of
236+ the _ GraphQL endpoint_ .
237+
238+ Note: By following this practice, traditional HTTP tooling can exercise concerns
239+ such as caching, rate limiting, audit logging, access-pattern analysis, error
240+ detection, monitoring and more without needing to fully parse the incoming
241+ GraphQL request.
242+
243+ For example, if the _ GraphQL endpoint_ is ` https://example.com/graphql ` then a
244+ persisted document request may be made to an endpoint such as
245+ ` https://example.com/graphql/sha256:517c56d2ba0779653b7698881207f749509f331bdaccbe951a82c378bc869556/FriendNames ` .
246+ For documents containing a single anonymous operation the final segment must be
247+ omitted, e.g.
248+ ` https://example.com/graphql/sha256:71f7dc5758652baac68e4a10c50be732b741c892ade2883a99358f52b555286b ` .
249+
250+ Legacy persisted document implementations often issue requests to the _ GraphQL
251+ endpoint_ directly (i.e. without a subpath), so it's recommended to support this
252+ pattern too.
253+
254+ :: The term _ remaining parameters_ refers to the _ persisted document request
255+ parameters_ that are not encoded via the URL subpath; i.e. when the GraphQL
256+ endpoint is queried directly the remaining request parameters are {documentId},
257+ {operationName}, {variables} and {extensions}, whereas when the subpath
258+ technique described above is used the remaining parameters are {variables} and
259+ {extensions}.
260+
148261### GET
149262
150- For a _ persisted document request_ using HTTP GET, parameters SHOULD be provided
151- in the query component of the request URL, encoded in the
263+ For a _ persisted document request_ using HTTP GET, the _ remaining parameters _
264+ SHOULD be provided in the query component of the request URL, encoded in the
152265` application/x-www-form-urlencoded ` format as specified by the
153266[ WhatWG URLSearchParams class] ( https://url.spec.whatwg.org/#interface-urlsearchparams ) .
154267
268+ Note: This is only a SHOULD recommendation to allow for variables which are too
269+ long for the query component to be encoded in an alternative way, for example
270+ via headers.
271+
155272The {documentId} parameter must be a string _ document identifier_ .
156273
157274The {operationName} parameter, if present, must be a string.
158275
159276Each of the {variables} and {extensions} parameters, if used, MUST be encoded as
160277a JSON string.
161278
279+ Note: JSON encoding is used here to enable reliable encoding of custom scalars
280+ and composite/list inputs; traditional HTTP query strings do not encode enough
281+ detail to tell the difference between a boolean ` true ` and the string ` "true" ` ,
282+ for example.
283+
162284Setting the value of the {operationName} parameter to the empty string is
163285equivalent to omitting the {operationName} parameter.
164286
0 commit comments