fix(apollo): preserve HTTP 200 for execution-level GraphQL errors#3940
Merged
kamilmysliwiec merged 1 commit intonestjs:masterfrom Apr 20, 2026
Merged
Conversation
Apollo Server consumes `extensions.http.status` on a GraphQLError and uses it as the
response HTTP status before the user's formatError hook runs. Some GraphQL clients and UIs
(Apollo Studio, Sandbox) refuse to parse non-2xx responses as `{data, errors}` and surface
them as transport errors instead. NestJS HttpExceptions were already normalised by
wrapFormatErrorFn but a raw GraphQLError carrying `http.status` slipped through.
Register a willSendResponse plugin that resets the HTTP status to 200 once execution has
run (detected by the presence of `data` on the single result body). Request-level
failures (parse, validate) keep their non-200 status because their body has no `data` key.
The plugin reuses the existing public `autoTransformHttpErrors` flag, so users who want
Apollo's default semantics can opt out with `autoTransformHttpErrors: false`.
Closes nestjs#2940
Member
|
lgtm |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
PR Checklist
Please check if your PR fulfills the following requirements:
PR Type
What kind of change does this PR introduce?
(Selected: Bugfix.)
What is the current behavior?
Issue Number: #2940
When a resolver throws a raw
GraphQLErrorwhoseextensions.http.statusis set (or a NestJS HTTP exception that maps onto Apollo's HTTP-status semantics), the GraphQL endpoint returns the resolver-derived HTTP 4xx/5xx status. Some GraphQL clients and UIs (Apollo Studio, Sandbox) then refuse to parse the response as{data, errors}and wrap it as a transport-level{ error: ... }envelope, which is what the issue's reporter sees. The bug is Apollo-specific because Mercurius does not honourextensions.http.status.What is the new behavior?
Adds a tiny Apollo Server plugin (
createPreserveHttpStatusPlugin) registered inApolloBaseDriver.mergeDefaultOptions. The plugin runs inwillSendResponseafter Apollo has finished merging error-derived HTTP status onto the response and resets the status to 200 when the request reached execution (detected by the presence of adatakey onsingleResult). Request-level failures (parse, validate) are untouched because their body has nodatakey. The plugin is gated on the existingautoTransformHttpErrorsflag — users who want Apollo's default behaviour can opt out withautoTransformHttpErrors: false. Regression spec atpackages/apollo/tests/e2e/issue-2940-http-error-status.spec.tscovers four cases: NestJS exception → 200, raw GraphQLError withhttp.status: 400→ 200, parse failure → 400 preserved, opt-out → 400 preserved.Does this PR introduce a breaking change?
Users currently throwing raw
GraphQLErrorwithextensions.http.statusset deliberately and expecting the HTTP status to leak into the response will now see HTTP 200 unless they setautoTransformHttpErrors: false. The flag already exists in the public driver config and is exactly the knob for this behaviour.Other information
The plugin reuses the existing public flag
autoTransformHttpErrors. Users who explicitly relied onextensions.http.statusmapping to a non-2xx response can opt back in by settingautoTransformHttpErrors: false. Verified with the full apollo suite (yarn test:e2e:apollo: 133 passed / 3 skipped) including existingpipes.spec.tsandguards-filters.spec.tserror-handling tests, plusyarn test:e2e:graphql(only the unrelated pre-existing Windows CRLF snapshot failures intests/plugin/model-class-visitor.spec.tses5-eager-importsandtests/plugin/readonly-visitor.spec.tsappear; both reproducible on clean master). Prettier (v3 binary) and oxlint clean on touched files.