Skip to content

Use 294 as the partial status code #346

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 5 commits into
base: main
Choose a base branch
from
Open
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
80 changes: 55 additions & 25 deletions spec/GraphQLOverHTTP.md
Original file line number Diff line number Diff line change
Expand Up @@ -647,32 +647,41 @@ should be added to the above.

### application/graphql-response+json

This section only applies when the response body is to use the
This section only applies when the response body uses the
`application/graphql-response+json` media type.

With this 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: 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_ contains the {data} entry and it is not {null}, then
the server MUST reply with a `2xx` status code and SHOULD reply with `200`
status code.
the server MUST reply with a `2xx` status code.

If the _GraphQL response_ contains the {data} entry and does not contain the
{errors} entry, then the server SHOULD reply with `200` status code.

Note: There are no circumstances where the GraphQL specification allows for a
response having {data} as {null} without {errors} being present.

If the _GraphQL response_ contains both the {data} entry (even if it is {null})
and the {errors} entry, then the server SHOULD reply with `294` status code.
Comment on lines 665 to +675
Copy link
Contributor

@Shane32 Shane32 Jun 2, 2025

Choose a reason for hiding this comment

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

I think this should be simplified. Whether {data} is {null} should have no bearing on response semantics and should be removed to simplify the specification -- if {data} exists, then there is no request error and 2xx is required, 200 if no {errors}. If {data} exists alongside {errors}, that's a 294; but if there's {errors} without {data}, that's a 4xx. I would not add specifications for an illegal response, where {data} is {null} and {errors} does not exist.

Suggested change
If the _GraphQL response_ contains the {data} entry and it is not {null}, then
the server MUST reply with a `2xx` status code and SHOULD reply with `200`
status code.
the server MUST reply with a `2xx` status code.
If the _GraphQL response_ contains the {data} entry and does not contain the
{errors} entry, then the server SHOULD reply with `200` status code.
Note: There are no circumstances where the GraphQL specification allows for a
response having {data} as {null} without {errors} being present.
If the _GraphQL response_ contains both the {data} entry (even if it is {null})
and the {errors} entry, then the server SHOULD reply with `294` status code.
If the _GraphQL response_ contains the {data} entry, then
the server MUST reply with a `2xx` status code.
If the _GraphQL response_ contains the {data} entry and does not contain the
{errors} entry, then the server SHOULD reply with `200` status code.
If the _GraphQL response_ contains both the {data} entry (even if it is {null})
and the {errors} entry, then the server SHOULD reply with `294` status code.

Copy link
Contributor

Choose a reason for hiding this comment

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

See https://spec.graphql.org/October2021/#sec-Response-Format

If the request included execution, the response map must contain an entry with key data.

If the request "executes", then it's 2xx.

If the request failed before execution, due to a syntax error, missing information, or validation error, this entry must not be present.

If it doesn't execute, it's a 4xx / 5xx.

Neither depend on whether data is null.

Copy link
Contributor

Choose a reason for hiding this comment

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

We do have this:

If an error was raised before execution begins, the data entry should not be present in the result.

If an error was raised during the execution that prevented a valid response, the data entry in the response should be null.

So we can only know that execution may have started. Seems like 2xx to me, anyway. And it makes the spec simpler.

Copy link
Contributor

Choose a reason for hiding this comment

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

I like the simplification

Copy link
Member Author

@benjie benjie Jun 2, 2025

Choose a reason for hiding this comment

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

Your diff reads really confusing because of some weird GitHub glitch, but I think what you’re saying is to change the requirement for when data is null from “allowed to use any status code” to “required that it be a 2xx (and preferably 294)”. This has been discussed in the past and some people felt that data:null should code as an error (there’s no data so there’s no partial success!), whereas other felt it should code as a partial success (execution occurred!). The original express-graphql coded it as a 400 IIRC (maybe a 500? It definitely wasn’t 2xx whatever it was) and many servers followed that pattern, hence why the spec text carefully excludes that case allowing the implementer to make their own choice.

Copy link
Member Author

Choose a reason for hiding this comment

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

I would not add specifications for an illegal response, where {data} is {null} and {errors} does not exist.

There is no specification for this, the schema adds a non-normative note indicating it’s not possible in case the reader isn’t aware and wonders what to do in that perceived edge case.

Copy link
Contributor

Choose a reason for hiding this comment

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

The way I see it, data being null is a simple result of null bubbling, as is the case anywhere else in the schema. So if your schema is:

type Query {
  dog(id: ID!): Dog!
}

type Dog {
  id: ID!
  name: String!
}

And you request { dog(id: "3") { name } } but either the dog's name isn't defined or the dog can't be found, null bubbling occurs and data ends up being null. This is no different than if it were to occur anywhere else in the schema. If dog returned a nullable Dog type, the client must check if dog is null. Basically, any GraphQL consumer must know how to handle nullable types (including in this case the root type itself) and/or look for the errors property to see if any errors were returned.

I just don't see why there is an exception to the rule for a root type, and further, an exception that only occurs in the scenario where the GraphQL server were to provide an illegal response (a null data without an errors key).

The original express-graphql coded it as a 400 IIRC (maybe a 500? It definitely wasn’t 2xx whatever it was) and many servers followed that pattern, hence why the spec text carefully excludes that case.

Understood but this does not apply. Any backwards compatibility should be defined for application/json, which we have defined to always return 200 for reasons already discussed. The language here is for the new response type which uses a different set of rules for status codes.

This has been discussed in the past and some people felt that data:null should code as an error (there’s no data so there’s no partial success!), whereas other felt it should code as a partial success (execution occurred!).

While there's no partial data, it cannot be known that there's no partial success. Granted this may be a bad server design, but if we extended the above sample to mutations, it is certainly possible that a partial save occurred, but null bubbling occurred and obscured the result. Sample:

type Mutation {
  setDogName(id: ID!, name: String!): Dog!
}

mutation {
  setDogNameA: setDogName(id: "234", name: "Jack") { id }  # sample: successful
  setDogNameB: setDogName(id: "345", name: "Jack") { id }  # sample: fails due to invalid id
}

Here null bubbling would have occurred causing data: null even though setDogNameA executed successfully.


Note: The result of executing a GraphQL operation may contain partial data as
well as encountered errors. Errors that happen during execution of the GraphQL
operation typically become part of the result, as long as the server is still
able to produce a well-formed _GraphQL response_. There's currently not an
approved HTTP status code to use for a "partial response," contenders include
WebDAV's status code "207 Multi-Status" and using a custom code such as "247
Partial Success."
[IETF RFC2616 Section 6.1.1](https://datatracker.ietf.org/doc/html/rfc2616#section-6.1.1)
states "codes are fully defined in section 10" implying that though more codes
are expected to be supported over time, valid codes must be present in this
document.

If the _GraphQL response_ contains the {data} entry and it is {null}, then the
server SHOULD reply with a `2xx` status code and it is RECOMMENDED it replies
with `200` status code.

Note: Using `4xx` and `5xx` status codes in this situation is not recommended -
since no _GraphQL request error_ has occurred it is seen as a "partial
response".
able to produce a well-formed _GraphQL response_. For details of why status code
`294` is recommended, see [Partial success](#sec-Partial-success). Using `4xx`
and `5xx` status codes in this situation is not appropriate - since no _GraphQL
request error_ has occurred it is seen as a "partial response" or "partial
success".

If the _GraphQL response_ does not contain the {data} entry then the server MUST
reply with a `4xx` or `5xx` status code as appropriate.
Expand All @@ -687,12 +696,6 @@ pass validation, then the server SHOULD reply with `400` status code.
If the client is not permitted to issue the GraphQL request then the server
SHOULD reply with `403`, `401` or similar appropriate status code.

Note: When the response media type is `application/graphql-response+json`,
clients can rely on the response being a well-formed _GraphQL response_
regardless of the status code. Intermediary servers may use the status code to
determine the status of the _GraphQL response_ without needing to process the
response body.

#### Examples

The following examples provide guidance on how to deal with specific error cases
Expand Down Expand Up @@ -782,6 +785,33 @@ is met:
- the response media type is `application/graphql-response+json`, or
- the status code is `200`.

## Partial success

The result of executing a GraphQL operation may contain partial data as well as
encountered errors. Errors that happen during execution of the GraphQL operation
typically become part of the result, as long as the server is still able to
produce a well-formed _GraphQL response_.

Using `4xx` and `5xx` status codes when {data} is present and non-null is not
appropriate; since no _GraphQL request error_ has occurred it is seen as a
"partial response" or "partial success".

There's currently not an approved official HTTP status code to use for a
"partial success". Contenders include "203 Non-Authoritative information" (which
indicates the response has been transformed), "206 Partial Content" (which
requires the `Range` header), and WebDAV's status code "207 Multi-Status" (which
"provides status for multiple _independent_ operations"). None of those quite
fit GraphQL's needs, so we recommend using custom code "294 Partial Success".
Since we are defining the code ourselves, rather than the IETF, we only
recommend its usage alongside the `application/graphql-response+json` media type
which makes the meaning explicit.

Note: This status code is not to help clients, who should ignore the status code
of a response when receiving the `application/graphql-response+json` media type,
but allows servers to indicate partial success such that intermediaries that do
not implement this specification may still track the not-fully-successful
request (for example, for observability).

## Security

This specification focuses solely on the intersection of GraphQL and HTTP.
Expand Down