Skip to content

Commit ba42f7f

Browse files
benjiespawniaShane32
authored
application/json should only mandate 200 for _well-formed_ GraphQL-over-HTTP requests (#241)
* Validation errors should give 200 * Apply suggestions from code review Co-authored-by: Benedikt Franke <[email protected]> * Clarify URL-encoding * Why was this escaped? * Apply suggestions from code review Co-authored-by: Shane Krueger <[email protected]> --------- Co-authored-by: Benedikt Franke <[email protected]> Co-authored-by: Shane Krueger <[email protected]>
1 parent e454097 commit ba42f7f

File tree

1 file changed

+151
-63
lines changed

1 file changed

+151
-63
lines changed

spec/GraphQLOverHTTP.md

Lines changed: 151 additions & 63 deletions
Original file line numberDiff line numberDiff line change
@@ -136,7 +136,7 @@ schema with additional fields.
136136
A _server_ MAY forbid individual requests by a _client_ to any endpoint for any
137137
reason, for example to require authentication or payment; when doing so it
138138
SHOULD use the relevant `4xx` or `5xx` status code. This decision SHOULD NOT be
139-
based on the contents of a well formed GraphQL request.
139+
based on the contents of a well-formed _GraphQL-over-HTTP request_.
140140

141141
Note: The _server_ should not make authorization decisions based on any part of
142142
the _GraphQL request_; these decisions should be made by the _GraphQL schema_
@@ -207,14 +207,18 @@ GET.
207207

208208
## Request Parameters
209209

210-
A _GraphQL-over-HTTP request_ is formed of the following parameters:
210+
:: A _GraphQL-over-HTTP request_ is an HTTP request that encodes the following
211+
parameters in one of the manners described in this specification:
211212

212-
- {query} - A Document containing GraphQL Operations and Fragments to execute.
213-
- {operationName} - (_Optional_): The name of the Operation in the Document to
214-
execute.
215-
- {variables} - (_Optional_): Values for any Variables defined by the Operation.
216-
- {extensions} - (_Optional_): This entry is reserved for implementors to extend
217-
the protocol however they see fit.
213+
- {query} - (_Required_, string): The string representation of the Source Text
214+
of a GraphQL Document as specified in
215+
[the Language section of the GraphQL specification](https://spec.graphql.org/draft/#sec-Language).
216+
- {operationName} - (_Optional_, string): The name of the Operation in the
217+
Document to execute.
218+
- {variables} - (_Optional_, map): Values for any Variables defined by the
219+
Operation.
220+
- {extensions} - (_Optional_, map): This entry is reserved for implementors to
221+
extend the protocol however they see fit.
218222

219223
Note: When comparing _GraphQL-over-HTTP request_ against the term
220224
["request"](https://spec.graphql.org/draft/#request) in the GraphQL
@@ -231,12 +235,15 @@ Note: Depending on the serialization format used, values of the aforementioned
231235
parameters can be encoded differently but their names and semantics must stay
232236
the same.
233237

234-
Note: Specifying `null` in JSON (or equivalent values in other formats) as
235-
values for optional request parameters is equivalent to not specifying them at
236-
all.
238+
Note: An HTTP request that encodes parameters of the same names but of the wrong
239+
type, or that omits required parameters, is not a well-formed _GraphQL-over-HTTP
240+
request_.
241+
242+
Note: Specifying `null` for optional request parameters is equivalent to not
243+
specifying them at all.
237244

238-
Note: Each of the {variables} and {extensions} parameters, if set, must have a
239-
map as their value.
245+
Note: So long as it is a string, {query} does not have to parse or validate to
246+
be part of a well-formed _GraphQL-over-HTTP request_.
240247

241248
## Accept
242249

@@ -269,25 +276,29 @@ still leveraging modern features if available in the server.
269276

270277
## GET
271278

272-
For HTTP GET requests, the GraphQL request parameters MUST be provided in the
273-
query component of the request URL, encoded in the
279+
For HTTP GET requests, the _GraphQL-over-HTTP request_ parameters MUST be
280+
provided in the query component of the request URL, encoded in the
274281
`application/x-www-form-urlencoded` format as specified by the
275282
[WhatWG URLSearchParams class](https://url.spec.whatwg.org/#interface-urlsearchparams).
276283

277-
The `query` parameter MUST be the string representation of the Source Text of
278-
the Document as specified in
284+
The {query} parameter MUST be the string representation of the source text of
285+
the document as specified in
279286
[the Language section of the GraphQL specification](https://spec.graphql.org/draft/#sec-Language).
280287

281-
The `operationName` parameter, if present, must be a string.
288+
The {operationName} parameter, if present, must be a string.
282289

283-
Each of the `variables` and `extensions` parameters, if used, MUST be
284-
represented as a URL-encoded JSON string.
290+
Each of the {variables} and {extensions} parameters, if used, MUST be encoded as
291+
a JSON string.
285292

286-
The `operationName` parameter, if supplied and not the empty string, represents
287-
the name of the operation to be executed within the `query`.
293+
The {operationName} parameter, if supplied and not the empty string, represents
294+
the name of the operation to be executed within the {query} as a string.
288295

289-
Setting the value of the `operationName` parameter to the empty string is
290-
equivalent to omitting the `operationName` parameter.
296+
Note: In the final URL all of these parameters will appear in the query
297+
component of the request URL as URL-encoded values due to the WhatWG
298+
URLSearchParams encoding specified above.
299+
300+
Setting the value of the {operationName} parameter to the empty string is
301+
equivalent to omitting the {operationName} parameter.
291302

292303
Note: By the above, `operationName=null` represents an operation with the name
293304
`"null"` (such as `query null { __typename }`). If a literal `null` is desired,
@@ -323,8 +334,8 @@ long-established semantics of safe methods within HTTP.
323334

324335
A GraphQL POST request instructs the server to perform a query or mutation
325336
operation. A GraphQL POST request MUST have a body which contains values of the
326-
request parameters encoded in one of the officially recognized GraphQL media
327-
types, or another media type supported by the server.
337+
_GraphQL-over-HTTP request_ parameters encoded in one of the officially
338+
recognized GraphQL media types, or another media type supported by the server.
328339

329340
A client MUST indicate the media type of a request body using the `Content-Type`
330341
header as specified in [RFC7231](https://datatracker.ietf.org/doc/html/rfc7231).
@@ -354,8 +365,8 @@ compliant _server_.
354365

355366
### JSON Encoding
356367

357-
When encoded in JSON, a GraphQL-over-HTTP request is a JSON object (map), with
358-
the properties specified by the GraphQL-over-HTTP request:
368+
When encoded in JSON, a _GraphQL-over-HTTP request_ is encoded as a JSON object
369+
(map), with the properties specified by the GraphQL-over-HTTP request:
359370

360371
- {query} - the string representation of the Source Text of the Document as
361372
specified in
@@ -437,10 +448,10 @@ in the `Accept` HTTP header, the server MUST either:
437448
2. Respond with a `406 Not Acceptable` status code and stop processing the
438449
request.
439450

440-
A server MUST support requests which accept the `application/json` media type
441-
(as indicated by the `Accept` header).
451+
A server MUST support any _GraphQL-over-HTTP request_ which accepts the
452+
`application/json` media type (as indicated by the `Accept` header).
442453

443-
A server SHOULD support requests which accept the
454+
A server SHOULD support any _GraphQL-over-HTTP request_ which accepts the
444455
`application/graphql-response+json` media type (as indicated by the `Accept`
445456
header).
446457

@@ -455,42 +466,54 @@ January 2025.
455466

456467
### Legacy watershed
457468

458-
From 1st January 2025 (`2025-01-01T00:00:00Z`), a server MUST support requests
459-
which accept the `application/graphql-response+json` media type (as indicated by
460-
the `Accept` header) using the UTF-8 encoding.
469+
From 1st January 2025 (`2025-01-01T00:00:00Z`), a server MUST support any
470+
_GraphQL-over-HTTP request_ which accepts the
471+
`application/graphql-response+json` media type (as indicated by the `Accept`
472+
header) using the UTF-8 encoding.
461473

462474
Before 1st January 2025 (`2025-01-01T00:00:00Z`), if the client does not supply
463-
an `Accept` header, the server SHOULD treat the request as if it had
464-
`Accept: application/json`. From 1st January 2025 (`2025-01-01T00:00:00Z`), if
465-
the client does not supply an `Accept` header, the server SHOULD treat the
466-
request as if it had `Accept: application/graphql-response+json`.
475+
an `Accept` header, the server SHOULD treat the _GraphQL-over-HTTP request_ as
476+
if it had `Accept: application/json`. From 1st January 2025
477+
(`2025-01-01T00:00:00Z`), if the client does not supply an `Accept` header, the
478+
server SHOULD treat the _GraphQL-over-HTTP request_ as if it had
479+
`Accept: application/graphql-response+json`.
467480

468481
Note: This default is in place to maintain compatibility with legacy clients.
469482

470483
## Validation
471484

472-
Validation of a well-formed GraphQL-over-HTTP request SHOULD apply all the
485+
Validation of a well-formed _GraphQL-over-HTTP request_ SHOULD apply all the
473486
[validation rules](https://spec.graphql.org/draft/#sec-Validation) specified by
474487
the GraphQL specification.
475488

476489
The server MAY, at its discretion, apply additional validation rules.
477490

491+
Note: Examples of additional validation rules the server may apply include depth
492+
limit, complexity limit, etc.
493+
478494
## Execution
479495

480-
Execution of a GraphQL-over-HTTP request follows
496+
Execution of a _GraphQL-over-HTTP request_ follows
481497
[GraphQL's ExecuteRequest()](<https://spec.graphql.org/draft/#ExecuteRequest()>)
482498
algorithm.
483499

500+
Note: In general, a _GraphQL-over-HTTP request_ that does not pass validation
501+
should not be executed; however in certain circumstances, for example persisted
502+
operations that were previously known to be valid, the server may attempt
503+
execution regardless of validation errors.
504+
484505
## Status Codes
485506

486507
In case of errors that completely prevent the generation of a well-formed
487508
_GraphQL response_, the server SHOULD respond with the appropriate status code
488-
depending on the concrete error condition.
509+
depending on the concrete error condition, and MUST NOT respond with a `2xx`
510+
status code when using the `application/graphql-response+json` media type.
489511

490-
Note: Typically this will be the `400` (Bad Request) status code.
512+
Note: Typically the appropriate status code will be `400` (Bad Request).
491513

492514
Note: This rule is "should" to maintain compatibility with legacy servers which
493-
can return 200 status codes even when this type of error occurs.
515+
can return 200 status codes even when this type of error occurs, but only when
516+
not using the `application/graphql-response+json` media type.
494517

495518
Otherwise, the status codes depends on the media type with which the GraphQL
496519
response will be served:
@@ -500,8 +523,9 @@ response will be served:
500523
This section only applies when the response body is to use the
501524
`application/json` media type.
502525

503-
The server SHOULD use the `200` status code, independent of any _GraphQL request
504-
error_ or _GraphQL field error_ raised.
526+
The server SHOULD use the `200` status code for every response to a well-formed
527+
_GraphQL-over-HTTP request_, independent of any _GraphQL request error_ or
528+
_GraphQL field error_ raised.
505529

506530
Note: A status code in the `4xx` or `5xx` ranges or status code `203` (and maybe
507531
others) could originate from intermediary servers; since the client cannot
@@ -517,13 +541,86 @@ Note: This indicates that no _GraphQL request error_ was raised, though one or
517541
more _GraphQL field error_ may have been raised this is still a successful
518542
execution - see "partial response" in the GraphQL specification.
519543

520-
The server SHOULD NOT use a `4xx` or `5xx` status code.
544+
The server SHOULD NOT use a `4xx` or `5xx` status code for a response to a
545+
well-formed _GraphQL-over-HTTP request_.
546+
547+
Note: For compatibility with legacy servers, this specification allows the use
548+
of `4xx` or `5xx` status codes for a failed well-formed _GraphQL-over-HTTP
549+
request_ where the response uses the `application/json` media type, but it is
550+
strongly discouraged. To use `4xx` and `5xx` status codes in these situations,
551+
please use the `application/graphql-response+json` media type.
552+
553+
If the URL is not used for other purposes, the server SHOULD use a `4xx` status
554+
code to respond to a request that is not a well-formed _GraphQL-over-HTTP
555+
request_.
521556

522557
Note: For compatibility with legacy servers, this specification allows the use
523-
of `4xx` or `5xx` status codes for failed requests where the response uses the
524-
`application/json` media type, but it is strongly discouraged. To use `4xx` and
525-
`5xx` status codes, please use the `application/graphql-response+json` media
526-
type.
558+
of `2xx` or `5xx` status codes when responding to invalid requests using the
559+
`application/json` media type, but it is strongly discouraged.
560+
561+
Note: URLs that enable GraphQL requests may enable other types of requests - see
562+
the [URL](#url) section.
563+
564+
#### Examples
565+
566+
The following examples provide guidance on how to deal with specific error cases
567+
when using the `application/json` media type to encode the response body:
568+
569+
##### JSON parsing failure
570+
571+
For example a POST request body of `NONSENSE` or `{"query":` (note: invalid
572+
JSON).
573+
574+
Requests that the server cannot interpret SHOULD result in status code `400`
575+
(Bad Request).
576+
577+
##### Invalid parameters
578+
579+
For example a POST request body of `{"qeury": "{__typename}"}` (note: typo) or
580+
`{"query": "query Q ($i:Int!) { q(i: $i) }", "variables": [7]}` (note: invalid
581+
shape for `variables`).
582+
583+
A request that does not constitute a well-formed _GraphQL-over-HTTP request_
584+
SHOULD result in status code `400` (Bad Request).
585+
586+
##### Document parsing failure
587+
588+
For example a POST request body of `{"query": "{"}`.
589+
590+
Requests where the _GraphQL document_ cannot be parsed SHOULD result in status
591+
code `200` (Okay).
592+
593+
##### Document validation failure
594+
595+
Requests that fail to pass _GraphQL validation_, the server SHOULD NOT execute
596+
the request and SHOULD return a status code of `200` (Okay).
597+
598+
##### Operation cannot be determined
599+
600+
If [GetOperation()](<https://spec.graphql.org/draft/#GetOperation()>) raises a
601+
_GraphQL request error_, the server SHOULD NOT execute the request and SHOULD
602+
return a status code of `200` (Okay).
603+
604+
##### Variable coercion failure
605+
606+
If
607+
[CoerceVariableValues()](<https://spec.graphql.org/draft/#CoerceVariableValues()>)
608+
raises a _GraphQL request error_, the server SHOULD NOT execute the request and
609+
SHOULD return a status code of `200` (Okay).
610+
611+
##### Field errors encountered during execution
612+
613+
If the operation is executed and no _GraphQL request error_ is raised then the
614+
server SHOULD respond with a status code of `200` (Okay). This is the case even
615+
if a _GraphQL field error_ is raised during
616+
[GraphQL's ExecuteQuery()](<https://spec.graphql.org/draft/#ExecuteQuery()>) or
617+
[GraphQL's ExecuteMutation()](<https://spec.graphql.org/draft/#ExecuteMutation()>).
618+
619+
<!--
620+
When we add support for subscriptions,
621+
[GraphQL's MapSourceToResponseEvent()](<https://spec.graphql.org/draft/#MapSourceToResponseEvent()>)
622+
should be added to the above.
623+
-->
527624

528625
### application/graphql-response+json
529626

@@ -561,8 +658,8 @@ Note: The GraphQL specification indicates that the only situation in which the
561658
_GraphQL response_ does not include the {data} entry is one in which the
562659
{errors} entry is populated.
563660

564-
If the GraphQL request is invalid (e.g. it is malformed, or does not pass
565-
validation) then the server SHOULD reply with `400` status code.
661+
If the request is not a well-formed _GraphQL-over-HTTP request_, or it does not
662+
pass validation, then the server SHOULD reply with `400` status code.
566663

567664
If the client is not permitted to issue the GraphQL request then the server
568665
SHOULD reply with `403`, `401` or similar appropriate status code.
@@ -587,8 +684,8 @@ For example a POST request body of `{"qeury": "{__typename}"}` (note: typo) or
587684
`{"query": "query Q ($i:Int!) { q(i: $i) }", "variables": [7]}` (note: invalid
588685
shape for `variables`).
589686

590-
Requests that do not constitute a well-formed _GraphQL-over-HTTP request_ should
591-
result in status code `400` (Bad Request).
687+
A request that does not constitute a well-formed _GraphQL-over-HTTP request_
688+
SHOULD result in status code `400` (Bad Request).
592689

593690
##### Document parsing failure
594691

@@ -602,15 +699,6 @@ code `400` (Bad Request).
602699
Requests that fail to pass _GraphQL validation_ SHOULD be denied execution with
603700
a status code of `400` (Bad Request).
604701

605-
Note: In certain circumstances, for example persisted operations that were
606-
previously known to be valid, the server MAY attempt execution regardless of
607-
validation errors.
608-
609-
Note: Validation rules include those specified in
610-
[the Validation section of the GraphQL specification](http://spec.graphql.org/draft/#sec-Validation),
611-
and any custom validation rules the server is using (for example: depth limit,
612-
complexity limit).
613-
614702
##### Operation cannot be determined
615703

616704
If [GetOperation()](<https://spec.graphql.org/draft/#GetOperation()>) raises a

0 commit comments

Comments
 (0)