apollographql / apollographql/federation
Clarify behaviour of HTTP status codes in federation
- Dominant language
- TypeScript
- Stars
- 725
- Forks
- 276
- Avg merge
- 1h 47m
- Merged PRs (30d)
- 1
Description
I have searched 'http status' and 'status code' and cannot find any issues with a matching title.
## 401 and 403 status codes for regular graphql
For standard monolithic graphql, the [graphql-over-http spec](https://graphql.github.io/graphql-over-http/draft/#sec-Status-Codes) defines expected behaviour for flavours:
- Responses with `application/json` media type
- Responses with `application/graphql-response+json` media type
The `application/json` spec is appears to be pretty straight forward:
>The server SHOULD use the 200 status code for every response to a well-formed [GraphQL-over-HTTP request](https://graphql.github.io/graphql-over-http/draft/#graphql-over-http-request), independent of any [GraphQL request error](https://spec.graphql.org/draft/#sec-Errors.Request-errors) or [GraphQL field error](https://spec.graphql.org/draft/#sec-Errors.Field-errors) raised.
The `application/graphql-response+json` revises this:
>With this media type, clients should process the response as a well-formed [GraphQL response](https://spec.graphql.org/draft/#sec-Response) independent of the HTTP status code, and should read the response body (specifically data and errors) to determine the status of the response.
>The purpose of setting a status code is to aid intermediary services and tooling (which may not implement this specification) in understanding the rough status of a response. This is useful in request logs, anomaly and intrusion detection, metrics and observability, API gateways, and more. The status code is not intended to aid the client, in fact it is recommended the client ignore the status code when this media type is in use.
> If the [GraphQL response](https://spec.graphql.org/draft/#sec-Response) contains the data entry and it is not null, then the server MUST reply with a 2xx status code.
In summary:
- In `application/json` - if the request is well formed, it should always be an HTTP 200
- In `application/graphql-response+json` if the response contains _data_ it should be an HTTP 2xx
**However:**
Up in the [URL section](https://graphql.github.io/graphql-over-http/draft/#sec-URL) we have:
>A [server](https://graphql.github.io/graphql-over-http/draft/#server) MAY forbid individual requests by a [client](https://graphql.github.io/graphql-over-http/draft/#client) to any endpoint for any reason, for example to require authentication or payment; when doing so it SHOULD use the relevant 4xx or 5xx status code. This decision SHOULD NOT be based on the contents of a well-formed [GraphQL-over-HTTP request](https://graphql.github.io/graphql-over-http/draft/#graphql-over-http-request).
>The [server](https://graphql.github.io/graphql-over-http/draft/#server) should not make authorization decisions based on any part of the [GraphQL request](https://spec.graphql.org/draft/#request); these decisions should be made by the [GraphQL schema](https://spec.graphql.org/draft/#sec-Schema) during [GraphQL’s ExecuteRequest()](https://spec.graphql.org/draft/#ExecuteRequest()), allowing for a partial response to be generated.
The point here is that a graphql server _is_ allowed to return 4xx for other reasons - _so long as they're not using the request body to do so_. Authn/z well likely falls into this category, where the server could be using a cookie or a header to determine if the user is authn/z for the request.
## But how does this play with federated graphql?
This issue that prompted this is this issue with Wundergraph router
https://github.com/wundergraph/cosmo/issues/2299 whereby the federated router will convert 401/403s to a 200.
This is a problem when adopting federation. If you have a graphql monolith that returns 401/403s, _and_ your client has come to rely on those status codes, then we can't simply slap a federation layer infront of our monolith, without breaking their stuff.
So in this particular use case, the solution could be simple, just pass the subgraph HTTP code on.
This seems to be line with:
>A server MAY forbid individual requests by a client to any endpoint for any reason,
>This decision SHOULD NOT be based on the contents of a well-formed GraphQL-over-HTTP request
(or is it? 🤔 the basis would be on the response it got from some upstream, the federated graph doesn't know whether the request body was inspected or not)
_In any case_
This becomes ambiguous when it comes to a query that spans multiple subgraphs, if some return 200 and others return 401 how should it behave?
Using the `application/graphql-response+json` rules, we at least have some guidance - if there's still data it has to be a 200.
## The ask
Clarify in the spec documentation how federated graphs should behave with respect to subgraph HTTP response codes - _particularly_ given that the use of `application/graphql-response+json` appears to encourage/condone the use of them.
Contributor guide
Research direction
Start by reading the linked GraphQL-over-HTTP URL and status-code sections, then compare them with the federated behavior described in the WunderGraph issue. Clarify in the federation specification documentation how subgraph 401/403 responses should be handled, including queries spanning multiple subgraphs and responses that still contain data.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- graphql
- Domain
- documentation
- Issue type
- Documentation
- Difficulty
- 5/5
- Estimated time
- Over a week
- Activity status
- Quiet
- Clarity
- Mostly clear
- Newbie friendliness
- 45/100