diff --git a/spec/Appendix A -- application-json.md b/spec/Appendix A -- application-json.md index e7be1bf4..06feb3cd 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 -use the `200` status code. +If the _GraphQL response_ contains a non-null {data} entry then the server MUST +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 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..1cf65edc 100644 --- a/spec/GraphQLOverHTTP.md +++ b/spec/GraphQLOverHTTP.md @@ -239,11 +239,14 @@ 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`. - -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 +422,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. @@ -429,25 +439,39 @@ specification, when a client does not include at least one supported media type in the `Accept` HTTP header, the server MUST either: 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; 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 +improve compatibility with _legacy client_ it is RECOMMENDED that the server +either: + +1. Respond with the `application/json` media type as detailed in + [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 +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 +498,30 @@ 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. + +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.