Use 294 as the partial status code
See changes for explanation.
Note: this will need testing before we can merge it.
My thoughts:
203 Non-Authoratative Information
Benefits:
- Proxies, firewalls and clients are unlikely to refuse a document because the code is 203; this would be a normal and expected status code to see, which does not imply any improper behavior.
Cons:
- Name and description do not have any relation to GraphQL partial response
- Proxies may be in use along a HTTP communication path, and may change responses to 203 or consume it. However, for server side logging, it doesn't matter what a proxy does with the status code after it's left the server
206 Partial Content
Benefits:
- Name somewhat matches intended use
Cons:
- Proxies, firewalls or even clients may attempt to validate the content, and (1) seeing no Content-Range response header, or (2) not expecting a partial response, reject the request
207 Multi-Status
Benefits:
- Name and description closely matches intended use
- Although '207' is used as the overall response status code, the recipient needs to consult the contents of the multistatus response body for further information about the success or failure of the method execution. The response MAY be used in success, partial success and also in failure situations.
- The status code is commonly in use and should be supported by proxies and firewalls
- The status code is defined in WebDAV's RFC, layered under HTTP, and not the HTTP protocol itself, much the same way this GraphQL RFC is layered under the HTTP protocol. As such, proxies should be oblivious to the content and pass along the request.
Cons:
- Firewalls may attempt to validate the content type of data returned with a 207 response type (at a minimum), and finding it is not XML, reject the response.
294 Partial Response (or another GraphQL-defined code)
Benefits:
- No overlap with other status codes
- Logging providers can choose to recognize 294 as a GraphQL-specific response code
Cons:
- Firewalls may reject responses with this unrecognized status code
- Exactly duplicates the purpose of status code 207
- Getting IANA approval for a new officially recognized status code may prove difficult, given the presence of 207
It should be noted that since most all requests will be HTTPS, the above firewall considerations only apply to application-level firewalls (Secure Web Gateway), as regular firewalls would not have access to the response code.
I would select 207 because its exact description matches our use case. But first I would prefer to see some testing with commonly used Secure Web Gateways. I wouldn't worry about server-end proxies because the GraphQL server operator would set that up as needed (which may include changing the response code of the GraphQL server to always be 200). And the use of Secure Web Gateways are probably limited to large companies or governments which are likely enough to have a team ready to allow (or disallow) access to specific websites. So testing becomes less important. The only leaves other proxies, such as a cell provider might install to make their network appear faster, which I don't think proxies would be an issue with 207. So I'd go with 207. (Just my two cents.)
203 is probably the "safest" choice....
Note that 201/202 also are as "safe" of a selection as 203, perhaps moreso. But again, the meaning doesn't carry over to GraphQL's intended use at all.
Super excited we are exploring this! This has been a very common pain points for new Apps adopting GraphQL from REST. Very often, we hear developers complain about everything is 200s, and their reliability metrics from REST can't be reused anymore. This effort would be a great help to mitigate this pain point
While 203 sounds safest from a proxy perspective, anyone encountering this status in production (without particular GraphQL knownledge) and checking the MDN for HTTP 203 would be confused. They would think that an HTTP proxy in their infrastructure has modified the headers or the response body. So I agree with @Shane32 on that one, it's important that the HTTP semantics match the GraphQL intent.
207 is definitely a closer match. Right now the MDN shows an example of a WebDav response with the status information enclosed. I think several GraphQL implementations do convey similar information as error extensions (for example, graphql-java's. ErrorType). I'm wondering if promoting this information at the error map level in the main graphql spec would align with this choice. What's your opinion?
I'm ruling out 206 (because of Content-Range) and 294 (for reasons explained in previous comments).
Taking a step back, I think that one of the goals of this spec is to make information about the GraphQL exchanges "leak" as much as possible in the HTTP transport when semantics match; this would help existing tools and newcomers to better understand what's happening without having to parse the actual document. Even if HTTP proxies work fine with the chosen HTTP status, I'm wondering if existing observability tools/dashboards/etc. will really consider another 20x in a different fashion. For example, if responding with HTTP 207 somehow breaks observability dashboards and confuses tools, then this would be a net negative for production usage.
if responding with HTTP 207 somehow breaks observability dashboards and confuses tools, then this would be a net negative for production usage.
This was my main concern with using 207 - it seems 100% related to WebDAV and thus tools may not expect it to be used elsewhere, may expect it to contain XML, and might even try and parse the XML.
Further, I'd argue that 207 doesn't align completely with GraphQL's partial success; it's defined in RFC4918 as:
The 207 (Multi-Status) status code provides status for multiple independent operations
i.e. it's more designed for batched processing rather than GraphQL's nested (and dependent/chained) resolution model.
I agree that 203 doesn't align semantically 100% (though I think there is a little more than 0% alignment - if errors occur then the data returned is kind of non-authoritative because you couldn't actually retrieve all the data), but I'm still leaning towards it from a compatibility POV.
Here's a summary from my POV:
:x: 201 Created - explicitly means "created" and I think it might be useful in future (e.g. for creating a continuable streaming endpoint to represent a subscription or incremental delivery) so I wanted to avoid using that. Moreover it seems 100% inappropriate for use in query operations with no side effects.
:x: 202 Accepted - explicitly means it has not been completed - not appropriate.
:man_shrugging: 203 Non-Authoritive Information - slight alignment (due to errors - the authoritive data isn't available) and doesn't have any expectations.
:x: 204 No Content - definitely doesn't work!
:x: 205 Reset Content - no content
:x: 206 Partial Content - Requires Range header - not appropriate.
:thinking: 207 Multi-Status - stronger (but still loose) alignment, tightly tied to WebDAV, has many expectations
:x: 208 Already Reported - inappropriate
:pleading_face: 294 Partial Success - 100% semantic alignment because it specifically means "partial success" in the GraphQL sense; no existing tooling expectations, HTTP RFC says to treat as success, unlikely to conflict with other uses, may be a challenge to standardize
Ideally, rather than opining on this, we should actually test it - we need to find out how well the various status codes work with native HTTP clients in different environments (browser, android, iOS, various server-side programming languages, desktop apps, etc), different proxies (including things like Cloudflare), different API gateways, different observability tooling, IDSes, etc. Ideally this would be crowd-sourced.
Alternatively we just pick one and see what happens. If we're picking one I would either pick 203 (has the least risk of conflict) or 294 (explicit new status code). I could be convinced to use 207 if it were demonstrated that it doesn't break existing tooling (and that 294 does).
People do make and use their own HTTP status codes, a number of these can be seen on Wikipedia, so I don't think we should 100% count it out.
Hi y'all 👋
As we've just discussed in the graphql-over-http wg, the plan is to go with 294 for a spec freeze on July 1st (same time as the main spec freeze). Please test it out and if you have any objection, please reply below.
The decision to go 294 was based on the discussions above and related conversations. Further, since 207 relates to multiple independent operations I think it's worth reserving for GraphQL batching which does in fact represent multiple independent operations (as opposed to regular GraphQL execution where the operations are interdependent).
If you can find anything that 294 breaks, please let us know ASAP!
I have set up a lambda at https://nacbkhpff4.execute-api.us-east-1.amazonaws.com/default/testHttp294 with the following body:
export const handler = async (event) => ({
statusCode: 294,
headers: {
'Content-Type': 'application/graphql-response+json',
},
body: JSON.stringify({
data: { a: 1, b: null },
errors: [{ path: ['b'], message: 'Some error' }],
}) + "\n",
});
It returns a fixed response, but you should query it as if it were a GraphQL API:
POST:
curl -i -X POST \
-H 'Content-Type: application/json' \
-H 'Accept: application/graphql-response+json' \
--data '{"query":"{a,b}"}' \
https://nacbkhpff4.execute-api.us-east-1.amazonaws.com/default/testHttp294
GET:
curl -i -X GET \
-H 'Accept: application/graphql-response+json' \
https://nacbkhpff4.execute-api.us-east-1.amazonaws.com/default/testHttp294?query=%7Ba%2Cb%7D
You can also test 200 status code, for debugging
I set up a similar endpoint at https://nacbkhpff4.execute-api.us-east-1.amazonaws.com/default/testHttp200 for checking a basic 200 works - useful if you think you've found a 294 that fails and want to check that it should have succeeded were it a 200.
curl -i -X POST \
-H 'Content-Type: application/json' \
-H 'Accept: application/graphql-response+json' \
--data '{"query":"{a}"}' \
https://nacbkhpff4.execute-api.us-east-1.amazonaws.com/default/testHttp200
I challenge y'all to find a standard HTTP intermediary (proxy, CDN, gateway, firewall, cache, etc) that does not correctly pass through HTTP 294 (but works for the HTTP 200).
Cloudflare works - https://testlambda.graphile.dev/testHttp294
I have set up a CloudFlare proxy to the above endpoint which works just fine:
curl -i -X POST \
-H 'Content-Type: application/json' \
-H 'Accept: application/graphql-response+json' \
--data '{"query":"{a,b}"}' \
https://testlambda.graphile.dev/testHttp294
And it also works correctly as you would expect for HTTP 200:
curl -i -X POST \
-H 'Content-Type: application/json' \
-H 'Accept: application/graphql-response+json' \
--data '{"query":"{a}"}' \
https://testlambda.graphile.dev/testHttp200