Skip to content

Commit a3d9ecb

Browse files
committed
Work in progress
1 parent 52d56fb commit a3d9ecb

File tree

2 files changed

+141
-18
lines changed

2 files changed

+141
-18
lines changed

spec/Appendix A -- Persisted Documents.md

+135-13
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,51 @@ thus should generate different document identifiers.
3232
A _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_:
86131
sha256: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
92168
colon symbols (`:`). The meaning of a custom document identifier is
93169
implementation 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

106183
Note: When used as an operation allow-list, persisted documents are typically
107184
stored 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

115191
Note: When used solely as a bandwidth optimization, as in the technique known
116192
colloquially 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+
155272
The {documentId} parameter must be a string _document identifier_.
156273

157274
The {operationName} parameter, if present, must be a string.
158275

159276
Each of the {variables} and {extensions} parameters, if used, MUST be encoded as
160277
a 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+
162284
Setting the value of the {operationName} parameter to the empty string is
163285
equivalent to omitting the {operationName} parameter.
164286

spec/GraphQLOverHTTP.md

+6-5
Original file line numberDiff line numberDiff line change
@@ -124,7 +124,8 @@ time.
124124

125125
A _server_ MUST enable GraphQL requests to one or more GraphQL schemas.
126126

127-
Each GraphQL schema a _server_ provides MUST be served via one or more URLs.
127+
Each GraphQL schema a _server_ provides MUST be served via one or more URLs,
128+
each URL is called a _GraphQL endpoint_.
128129

129130
A _server_ MUST NOT require the _client_ to use different URLs for different
130131
GraphQL query and mutation requests to the same GraphQL schema.
@@ -152,15 +153,15 @@ It is RECOMMENDED to end the path component of the URL with `/graphql`, for
152153
example:
153154

154155
```url example
155-
http://example.com/graphql
156+
https://example.com/graphql
156157
```
157158

158159
```url example
159-
http://product.example.com/graphql
160+
https://product.example.com/graphql
160161
```
161162

162163
```url example
163-
http://example.com/product/graphql
164+
https://example.com/product/graphql
164165
```
165166

166167
# Serialization Format
@@ -321,7 +322,7 @@ With the following query variables:
321322
This request could be sent via an HTTP GET as follows:
322323

323324
```url example
324-
http://example.com/graphql?query=query(%24id%3AID!)%7Buser(id%3A%24id)%7Bname%7D%7D&variables=%7B%22id%22%3A%22QVBJcy5ndXJ1%22%7D
325+
https://example.com/graphql?query=query(%24id%3AID!)%7Buser(id%3A%24id)%7Bname%7D%7D&variables=%7B%22id%22%3A%22QVBJcy5ndXJ1%22%7D
325326
```
326327

327328
GET requests MUST NOT be used for executing mutation operations. If the values

0 commit comments

Comments
 (0)