From d67803b404c69cf0102efbcd1ad855847403d01e Mon Sep 17 00:00:00 2001 From: Benjie Gillam Date: Tue, 5 May 2026 11:01:57 +0100 Subject: [PATCH 1/8] Restore important guidance and recommend support for Accept:application/json --- spec/Appendix A -- application-json.md | 88 ++++++++++++++------------ spec/GraphQLOverHTTP.md | 80 +++++++++++++++++------ 2 files changed, 108 insertions(+), 60 deletions(-) diff --git a/spec/Appendix A -- application-json.md b/spec/Appendix A -- application-json.md index e7be1bf4..a3425a49 100644 --- a/spec/Appendix A -- application-json.md +++ b/spec/Appendix A -- application-json.md @@ -1,11 +1,13 @@ # A. Appendix: `application/json` responses +This section only applies when the response body uses or may use the legacy +`application/json` media type for compatibility reasons. + Previous to this specification, the article [Serving over HTTP](https://graphql.org/learn/serving-over-http) ([WayBack Machine entry, 1st June 2022](https://web.archive.org/web/20220601155421/https://graphql.org/learn/serving-over-http)) -on the graphql.org website served as guidance. - -This article used `application/json` as media type for the response. +on the graphql.org website served as guidance. This article used +`application/json` as media type for the response. In some cases, the response received by a client may not originate from a GraphQL service, but instead from an intermediary—such as an API gateway, proxy, @@ -15,41 +17,43 @@ response with `4xx` or `5xx` status code and potentially using the standard `application/json` media type to encode the reason for the error. Such a response is unlikely to be a valid GraphQL response. -For this reason, a client application receiving an `application/json` response, -could rely on the response being a well-formed _GraphQL response_ only if the -status code is `200`. - -This caused multiple observability issues because it was challenging to -distinguish a well-formed _GraphQL response_ from an intermediary response. - -`application/graphql-response+json` allows to distinguish a well-formed _GraphQL -response_ from another intermediary response and is the required media type -moving forward. - -For compatibility reasons, clients and servers may support `application/json` as -described in this section. - -Note: Servers may wish to only support the `application/graphql-response+json` -media type so that related HTTP tooling may utilize the HTTP status codes of -responses without having to be GraphQL-aware. - -### Accept - -To maximize compatibility, a client may include the media type -`application/json` in the `Accept` header. When doing this, it is recommended -that the client set the `Accept` header to +For this reason, a client receiving an `application/json` response can only rely +on the response being a well-formed _GraphQL response_ if the status code is +`200`. However, responding to all GraphQL requests with HTTP 200 means that +intermediary observability software cannot determine the status of the request +without processing the response body. + +Responding with the `application/graphql-response+json` media type enables the +client to distinguish a well-formed _GraphQL response_ from an intermediary +response, and is required by both clients and servers under this specification. +This appendix exists to increase interoperability with legacy clients/servers +that have not yet adopted this specification. + +Note: Servers may wish to respond to `Accept: application/json` requests with +the `application/graphql-response+json` media type so that related HTTP tooling +may utilize the HTTP status codes of responses without having to be +GraphQL-aware. Doing so may impact error-handling behavior of legacy clients, +and may prevent legacy clients from processing responses if they require the +response `Content-Type` to be `application/json`. + +## Accept + +To enable compatibility with legacy servers, the client SHOULD include the media +type `application/json` in the `Accept` header and it is RECOMMENDED that such a +client set the `Accept` header to `application/graphql-response+json, application/json;q=0.9`. Note: The `q=0.9` parameter tells content negotiation that `application/json` should only be used if `application/graphql-response+json` is not supported. -### Status codes +## Status codes -When using the `application/json` media type, the server should use the `200` -status code for every response to a well-formed _GraphQL-over-HTTP request_, -independent of any _GraphQL request error_ or _GraphQL field error_ raised. +When responding with the legacy `application/json` media type, the server SHOULD +use the `200` status code for every response to a well-formed _GraphQL-over-HTTP +request_ independent of any _GraphQL request error_ or _GraphQL field error_ +raised. -If the response uses a non-`200` status code then the client must not rely on +If the response uses a non-`2xx` status code then the client MUST NOT rely on the body to be a well-formed _GraphQL response_. Note: A status code in the `4xx` or `5xx` ranges or status code `203` (and maybe @@ -60,14 +64,14 @@ _GraphQL response_ (because it cannot trust the source) the server must use generated or modified by an intermediary. See [processing a response](#sec-Processing-a-response) for more details. -If the _GraphQL response_ contains a non-null {data} entry then the server must +If the _GraphQL response_ contains a non-null {data} entry then the server MUST use the `200` status code. Note: This indicates that no _GraphQL request error_ was raised, though one or more _GraphQL field error_ may have been raised this is still a successful execution - see "partial response" in the GraphQL specification. -The server should not use a `4xx` or `5xx` status code for a response to a +The server SHOULD NOT use a `4xx` or `5xx` status code for a response to a well-formed _GraphQL-over-HTTP request_. Note: For compatibility with legacy servers, this specification allows the use @@ -76,7 +80,7 @@ request_ where the response uses the `application/json` media type, but it is strongly discouraged. To use `4xx` and `5xx` status codes in these situations, please use the `application/graphql-response+json` media type. -If the URL is not used for other purposes, the server should use a `4xx` status +If the URL is not used for other purposes, the server SHOULD use a `4xx` status code to respond to a request that is not a well-formed _GraphQL-over-HTTP request_. @@ -87,12 +91,12 @@ of `2xx` or `5xx` status codes when responding to invalid requests using the Note: URLs that enable GraphQL requests may enable other types of requests - see the [URL](#url) section. -#### Examples +### Examples The following examples provide guidance on how to deal with specific error cases when using the `application/json` media type to encode the response body: -##### JSON parsing failure +#### JSON parsing failure For example a POST request body of `NONSENSE` or `{"query":` (note: invalid JSON). @@ -100,7 +104,7 @@ JSON). Requests that the server cannot interpret SHOULD result in status code `400` (Bad Request). -##### Invalid parameters +#### Invalid parameters For example a POST request body of `{"qeury": "{__typename}"}` (note: typo) or `{"query": "query Q ($i:Int!) { q(i: $i) }", "variables": [7]}` (note: invalid @@ -109,25 +113,25 @@ shape for `variables`). A request that does not constitute a well-formed _GraphQL-over-HTTP request_ SHOULD result in status code `400` (Bad Request). -##### Document parsing failure +#### Document parsing failure For example a POST request body of `{"query": "{"}`. Requests where the _GraphQL document_ cannot be parsed SHOULD result in status code `200` (Okay). -##### Document validation failure +#### Document validation failure If a request fails to pass _GraphQL validation_, the server SHOULD NOT execute the request and SHOULD return a status code of `200` (Okay). -##### Operation cannot be determined +#### Operation cannot be determined If [GetOperation()]() raises a _GraphQL request error_, the server SHOULD NOT execute the request and SHOULD return a status code of `200` (Okay). -##### Variable coercion failure +#### Variable coercion failure If [CoerceVariableValues()]() @@ -146,7 +150,7 @@ For example the well-formed GraphQL-over-HTTP request: would fail variable coercion as the value for `id` would fail to satisfy the query document's expectation that `id` is non-null. -##### Field errors encountered during execution +#### Field errors encountered during execution If the operation is executed and no _GraphQL request error_ is raised then the server SHOULD respond with a status code of `200` (Okay). This is the case even diff --git a/spec/GraphQLOverHTTP.md b/spec/GraphQLOverHTTP.md index bed5d8e1..5341ac1d 100644 --- a/spec/GraphQLOverHTTP.md +++ b/spec/GraphQLOverHTTP.md @@ -241,9 +241,8 @@ the `Accept` header. If the client doesn't know that the server supports `application/graphql-response+json`, it is RECOMMENDED that the client set the -`Accept` header to `application/graphql-response+json, application/json;q=0.9`. - -See [Appendix A](#sec-Appendix-application-json-responses) for more details +`Accept` header to `application/graphql-response+json, application/json;q=0.9`; +see [Appendix A](#sec-Appendix-application-json-responses) for more details about `application/json` responses. ## GET @@ -419,6 +418,13 @@ A server MUST indicate the media type of the response with a `Content-Type` header, and SHOULD indicate the encoding (e.g. `application/graphql-response+json; charset=utf-8`). +A server MUST support responses using the `application/graphql-response+json` +media type. + +:: A _legacy server_ is a _server_ that does not support responses using the +`application/graphql-response+json` media type, and thus does not conform to +this specification. + If an `Accept` header is provided, the server MUST respect the given `Accept` header and attempt to encode the response in the highest priority media type listed that is supported by the server. @@ -428,26 +434,41 @@ In alignment with the specification, when a client does not include at least one supported media type in the `Accept` HTTP header, the server MUST either: +1. Disregard the `Accept` header and respond with the server's choice of media + type; OR 1. Respond with a `406 Not Acceptable` status code and stop processing the - request (RECOMMENDED); OR -2. Disregard the `Accept` header and respond with the server's choice of media - type (NOT RECOMMENDED). + request. + +If the `Accept` header does not indicate support for +`application/graphql-response+json` but does indicate support for +`application/json`, the server MUST either: + +1. Respond with the `application/graphql-response+json` media type + (RECOMMENDED); OR +1. Respond with the `application/json` media type as detailed in + [Appendix A](#sec-Appendix-application-json-responses) (RECOMMENDED if + support for legacy clients is desired); OR +1. Respond with a `406 Not Acceptable` status code and stop processing the + request (NOT RECOMMENDED). + +Note: Prior to this specification, the media type `application/json` was in wide +use for the HTTP response payload type. With this media type, clients cannot +trust responses from the server that do not use an HTTP `2xx` status code (since +these replies may come from non-compliant HTTP servers or proxies somewhere in +the request chain), hence the introduction of +`application/graphql-response+json`. Legacy clients, utilities and tooling may +continue to issue `Accept: application/json`, so to maintain interoperability it +is recommended to continue to serve these requests. + +If the `Accept` header is present but does not indicate support for any of the +server's supported media types, it is RECOMMENDED to respond with +`406 Not Acceptable`. Note: It is unlikely that a client can process a response that does not match one of the media types it has requested, hence `406 Not Acceptable` being the recommended response. However, the server authors may know better about the specific clients consuming their endpoint, thus both approaches are permitted. -A GraphQL server MUST support responses using the -`application/graphql-response+json` media type. - -For maximal compatibility, a _server_ SHOULD support using both the -`application/json` and the `application/graphql-response+json` media types for -responses. - -Note: See [Appendix A](#sec-Appendix-application-json-responses) for more -details about `application/json` responses. - ## Validation Validation of a well-formed _GraphQL-over-HTTP request_ SHOULD apply all the @@ -474,8 +495,31 @@ execution regardless of validation errors. In case of errors that completely prevent the generation of a well-formed _GraphQL response_, the server SHOULD respond with the appropriate status code -depending on the concrete error condition, and MUST NOT respond with a `2xx` -status code. +depending on the concrete error condition. + +Otherwise, the status code to use depends on the media type with which the +GraphQL response will be served. + +For legacy `application/json` responses, see +[Appendix A](#sec-Appendix-application-json-responses). + +### application/graphql-response+json + +This section only applies when the response body uses the +`application/graphql-response+json` media type. + +Clients should process the response as a well-formed _GraphQL response_ +independent of the HTTP status code, and should read the response body +(specifically {data} and {errors}) to determine the status of the response. + +Note: With `application/graphql-response+json`, clients know the response is +well formed and should determine the detailed status of the response from the +response body alone, allowing server authors to adopt more appropriate status +codes without impacting behavior of existing clients. Intermediary servers may +use the status code to determine the status of the _GraphQL response_ without +needing to process the response body; this is useful in request logs, developer +tooling, anomaly and intrusion detection, metrics and observability, API +gateways, and more. If the _GraphQL response_ contains the {data} entry and it is not {null}, then the server MUST reply with a `2xx` status code. From 2f1fb726c2b9094aeef37f73aaeb5c2d11591df9 Mon Sep 17 00:00:00 2001 From: Benjie Gillam Date: Wed, 6 May 2026 09:00:13 +0100 Subject: [PATCH 2/8] Reword sentence to make the exclusion clear --- spec/GraphQLOverHTTP.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/spec/GraphQLOverHTTP.md b/spec/GraphQLOverHTTP.md index 5341ac1d..9f7fd5ff 100644 --- a/spec/GraphQLOverHTTP.md +++ b/spec/GraphQLOverHTTP.md @@ -460,9 +460,9 @@ the request chain), hence the introduction of continue to issue `Accept: application/json`, so to maintain interoperability it is recommended to continue to serve these requests. -If the `Accept` header is present but does not indicate support for any of the -server's supported media types, it is RECOMMENDED to respond with -`406 Not Acceptable`. +If the `Accept` header is present but indicates support for neither any of the +server's supported media types nor `application/json`, it is RECOMMENDED to +respond with `406 Not Acceptable`. Note: It is unlikely that a client can process a response that does not match one of the media types it has requested, hence `406 Not Acceptable` being the From 84d587572cb1e65785781429c8472a31ec7a7f47 Mon Sep 17 00:00:00 2001 From: Benjie Gillam Date: Wed, 6 May 2026 09:07:38 +0100 Subject: [PATCH 3/8] Clarify another paragraph --- spec/GraphQLOverHTTP.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/spec/GraphQLOverHTTP.md b/spec/GraphQLOverHTTP.md index 9f7fd5ff..d30a42b0 100644 --- a/spec/GraphQLOverHTTP.md +++ b/spec/GraphQLOverHTTP.md @@ -439,9 +439,9 @@ in the `Accept` HTTP header, the server MUST either: 1. Respond with a `406 Not Acceptable` status code and stop processing the request. -If the `Accept` header does not indicate support for -`application/graphql-response+json` but does indicate support for -`application/json`, the server MUST either: +If the `Accept` header does not indicate support for one of the server's +supported media types but does indicate support for `application/json`, the +server MUST either: 1. Respond with the `application/graphql-response+json` media type (RECOMMENDED); OR From c8bd03c7c1f0b3aa9b78ba31ce0ac84a7cb27310 Mon Sep 17 00:00:00 2001 From: Benjie Gillam Date: Wed, 6 May 2026 09:41:37 +0100 Subject: [PATCH 4/8] Clarify --- spec/GraphQLOverHTTP.md | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/spec/GraphQLOverHTTP.md b/spec/GraphQLOverHTTP.md index d30a42b0..00bf5e09 100644 --- a/spec/GraphQLOverHTTP.md +++ b/spec/GraphQLOverHTTP.md @@ -443,11 +443,13 @@ If the `Accept` header does not indicate support for one of the server's supported media types but does indicate support for `application/json`, the server MUST either: -1. Respond with the `application/graphql-response+json` media type - (RECOMMENDED); OR +1. Disregard the `Accept` header and respond with the + `application/graphql-response+json` media type (RECOMMENDED); OR 1. Respond with the `application/json` media type as detailed in [Appendix A](#sec-Appendix-application-json-responses) (RECOMMENDED if support for legacy clients is desired); OR +1. Disregard the `Accept` header and respond with the server's choice of media + type; OR 1. Respond with a `406 Not Acceptable` status code and stop processing the request (NOT RECOMMENDED). From f1a5a481277789ae19200375266a7e660687abcb Mon Sep 17 00:00:00 2001 From: Benjie Gillam Date: Wed, 6 May 2026 09:41:49 +0100 Subject: [PATCH 5/8] Non-normative recommendation of application/json for successful legacy requests --- spec/GraphQLOverHTTP.md | 41 +++++++++++++++++++++-------------------- 1 file changed, 21 insertions(+), 20 deletions(-) diff --git a/spec/GraphQLOverHTTP.md b/spec/GraphQLOverHTTP.md index 00bf5e09..8ecca89e 100644 --- a/spec/GraphQLOverHTTP.md +++ b/spec/GraphQLOverHTTP.md @@ -239,6 +239,10 @@ client gets something useful, it needs to indicate the media types it supports. The client MUST include the media type `application/graphql-response+json` in the `Accept` header. +:: A _legacy client_ is a client that does not support responses using the +`application/graphql-response+json` media type, and thus does not conform to +this specification. + If the client doesn't know that the server supports `application/graphql-response+json`, it is RECOMMENDED that the client set the `Accept` header to `application/graphql-response+json, application/json;q=0.9`; @@ -421,7 +425,7 @@ header, and SHOULD indicate the encoding (e.g. A server MUST support responses using the `application/graphql-response+json` media type. -:: A _legacy server_ is a _server_ that does not support responses using the +:: A _legacy server_ is a server that does not support responses using the `application/graphql-response+json` media type, and thus does not conform to this specification. @@ -440,27 +444,24 @@ in the `Accept` HTTP header, the server MUST either: request. If the `Accept` header does not indicate support for one of the server's -supported media types but does indicate support for `application/json`, the -server MUST either: +preferred media types but does indicate support for `application/json`, to +improve compatibility with _legacy client_ it is RECOMMENDED that the server +either: -1. Disregard the `Accept` header and respond with the - `application/graphql-response+json` media type (RECOMMENDED); OR 1. Respond with the `application/json` media type as detailed in - [Appendix A](#sec-Appendix-application-json-responses) (RECOMMENDED if - support for legacy clients is desired); OR -1. Disregard the `Accept` header and respond with the server's choice of media - type; OR -1. Respond with a `406 Not Acceptable` status code and stop processing the - request (NOT RECOMMENDED). - -Note: Prior to this specification, the media type `application/json` was in wide -use for the HTTP response payload type. With this media type, clients cannot -trust responses from the server that do not use an HTTP `2xx` status code (since -these replies may come from non-compliant HTTP servers or proxies somewhere in -the request chain), hence the introduction of -`application/graphql-response+json`. Legacy clients, utilities and tooling may -continue to issue `Accept: application/json`, so to maintain interoperability it -is recommended to continue to serve these requests. + [Appendix A](#sec-Appendix-application-json-responses); OR +1. Disregard the `Accept` header and respond with the + `application/graphql-response+json` media type. + +Note: Responding to such a request with the `application/json` media type only +if it contains non-null {data} (and thus would produce HTTP `2xx` under both +`application/json` and `application/graphql-response+json` media types) allows +for richer status codes for unsuccessful requests whilst maximizing +compatibility with _legacy client_ for successful and partially successful +requests. A response with the `application/json` media type could originate from +non-GraphQL intermediary servers and middlewares handling failures (HTTP `4xx` +and `5xx`), so clients can typically only rely on an `application/json` response +to be from GraphQL when it uses HTTP `2xx` status code. If the `Accept` header is present but indicates support for neither any of the server's supported media types nor `application/json`, it is RECOMMENDED to From 34ddc64a1d411d92871abe4f0e82d7ca5a9dad30 Mon Sep 17 00:00:00 2001 From: Benjie Gillam Date: Wed, 6 May 2026 09:54:57 +0100 Subject: [PATCH 6/8] Legacy can use any 2xx status code --- spec/Appendix A -- application-json.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/spec/Appendix A -- application-json.md b/spec/Appendix A -- application-json.md index a3425a49..06feb3cd 100644 --- a/spec/Appendix A -- application-json.md +++ b/spec/Appendix A -- application-json.md @@ -65,7 +65,7 @@ generated or modified by an intermediary. See [processing a response](#sec-Processing-a-response) for more details. If the _GraphQL response_ contains a non-null {data} entry then the server MUST -use the `200` status code. +use a `2xx` status code and SHOULD use the `200` status code. Note: This indicates that no _GraphQL request error_ was raised, though one or more _GraphQL field error_ may have been raised this is still a successful From 7e5e5592f64c88bec3496315b1fb56cd3304175e Mon Sep 17 00:00:00 2001 From: Benjie Gillam Date: Wed, 6 May 2026 10:11:28 +0100 Subject: [PATCH 7/8] Consistent order --- spec/GraphQLOverHTTP.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/spec/GraphQLOverHTTP.md b/spec/GraphQLOverHTTP.md index 8ecca89e..694b7eb7 100644 --- a/spec/GraphQLOverHTTP.md +++ b/spec/GraphQLOverHTTP.md @@ -438,10 +438,10 @@ In alignment with the specification, when a client does not include at least one supported media type in the `Accept` HTTP header, the server MUST either: -1. Disregard the `Accept` header and respond with the server's choice of media - type; OR 1. Respond with a `406 Not Acceptable` status code and stop processing the - request. + request; OR. +1. Disregard the `Accept` header and respond with the server's choice of media + type. If the `Accept` header does not indicate support for one of the server's preferred media types but does indicate support for `application/json`, to From 24509645ffc98539048670abdfd4abd3cc2a49a3 Mon Sep 17 00:00:00 2001 From: Benjie Gillam Date: Thu, 7 May 2026 15:14:25 +0100 Subject: [PATCH 8/8] Trim extraneous text --- spec/GraphQLOverHTTP.md | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/spec/GraphQLOverHTTP.md b/spec/GraphQLOverHTTP.md index 694b7eb7..1cf65edc 100644 --- a/spec/GraphQLOverHTTP.md +++ b/spec/GraphQLOverHTTP.md @@ -512,8 +512,7 @@ This section only applies when the response body uses the `application/graphql-response+json` media type. Clients should process the response as a well-formed _GraphQL response_ -independent of the HTTP status code, and should read the response body -(specifically {data} and {errors}) to determine the status of the response. +independent of the HTTP status code. Note: With `application/graphql-response+json`, clients know the response is well formed and should determine the detailed status of the response from the