Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
90 changes: 47 additions & 43 deletions spec/Appendix A -- application-json.md
Original file line number Diff line number Diff line change
@@ -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,
Expand All @@ -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
Expand All @@ -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.
Comment on lines -63 to +68
Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

NOTE: this is a change from the previous spec, this is specifically to allow HTTP 294 responses (and other 2xx status codes) to legacy clients. AFAIK at least in the JS ecosystem most people use response.ok or check a status range rather than response.status === 200 and this lines up better with the HTTP semantics - any 2xx is success.

Apollo client checks range: https://github.com/apollographql/apollo-client/blob/7c33689cca049f46192b091eaf979b2e64fd6213/src/link/http/parseAndCheckHttpResponse.ts#L151

URQL checks range: https://github.com/urql-graphql/urql/blob/1df98a66419b523bd8859ef617e13b4c7984bc7f/packages/core/src/internal/fetchSource.ts#L226

Graffle ignores status code: https://github.com/graffle-js/graffle/blob/4c58ee6cec67fe0047cc6708f6f19232077934e3/src/extensions/TransportHttp/TransportHttp.ts#L252-L260

Relay1 ignores status codes: https://relay.dev/docs/guides/network-layer/

Footnotes

  1. I guess I should say Relay's network layer example recommended in the documentation, since people keep helpfully pointing out Relay "has no network layer".


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
Expand All @@ -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_.

Expand All @@ -87,20 +91,20 @@ 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).

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
Expand All @@ -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()](<https://spec.graphql.org/draft/#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()](<https://spec.graphql.org/draft/#CoerceVariableValues()>)
Expand All @@ -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
Expand Down
82 changes: 64 additions & 18 deletions spec/GraphQLOverHTTP.md
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Comment thread
martinbonnin marked this conversation as resolved.

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
Expand Down Expand Up @@ -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.
Comment thread
martinbonnin marked this conversation as resolved.

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.
Expand All @@ -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
Expand All @@ -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.
Comment thread
martinbonnin marked this conversation as resolved.

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.
Comment thread
martinbonnin marked this conversation as resolved.

If the _GraphQL response_ contains the {data} entry and it is not {null}, then
the server MUST reply with a `2xx` status code.
Expand Down
Loading