Skip to content

Add QUERY verb - #411

Open
martinbonnin wants to merge 13 commits into
mainfrom
QUERY
Open

martinbonnin wants to merge 13 commits into
mainfrom
QUERY

Conversation

@martinbonnin

@martinbonnin martinbonnin commented Jul 3, 2026 •

Copy link
Copy Markdown
Contributor

See https://www.rfc-editor.org/rfc/rfc10008.html and ecosystem adoption

QUERY is an idempotent cacheable request with a body that maps quite well to GraphQL queries.

This editorial duplicates a bit of text but since this is a spec and not a novel, I'd rather have duplication than missing information. Maybe the JSON encoding part could be factored in but not sure how.

@Shane32

Shane32 commented Jul 6, 2026 •

Copy link
Copy Markdown
Contributor

How about a bit more DRY? For example:

QUERY

A QUERY request instructs the GraphQL-over-HTTP server to perform a query operation.

A server MAY accept QUERY requests. If a server accepts QUERY requests, it MUST handle the request and response according to all requirements that apply to POST requests, except as otherwise specified in this section.

QUERY requests MUST NOT be used for executing mutation operations. If the values of {query} and {operationName} indicate that a mutation operation is to be executed, the server MUST respond with error status code 405 (Method Not Allowed) and halt execution. This restriction is necessary to conform with the long-established semantics of safe methods within HTTP.

Then any future changes to the POST requirements are inherited by the QUERY requirements. Or are there more differences?

@martinbonnin

Copy link
Copy Markdown
Contributor Author

Then any future changes to the POST requirements are inherited by the QUERY requirements. Or are there more differences?

There is one difference in the paragraph that POST is required while QUERY is optional:

A server MUST support POST requests encoded with the `application/json` media
type (as indicated by the `Content-Type` header) encoded with UTF-8.

But that's something you call out at the beginning of your version.

I think the "condensed" part is easier to read and maintain but is a bit less clear.

Both work for me.

Base automatically changed from legacy-client-editorial to main July 13, 2026 19:05
@martinbonnin
martinbonnin marked this pull request as ready for review July 23, 2026 15:29
Comment thread spec/GraphQLOverHTTP.md Outdated
Comment thread spec/GraphQLOverHTTP.md Outdated
martinbonnin and others added 3 commits August 5, 2026 20:20
Co-authored-by: Benjie <benjie@jemjie.com>
Co-authored-by: Benjie <benjie@jemjie.com>
@martinbonnin

Copy link
Copy Markdown
Contributor Author

Notes from July wg:

@benjie

benjie commented Aug 5, 2026

Copy link
Copy Markdown
Member

Few notes:

  • I think we're good with ignoring equivalent resource / HTTP 303?
  • Normalization for caching may be an interesting consideration
  • Should we recommend default cache directives?
  • What does this mean for incremental delivery? I think it's fine: incremental delivery is cacheable.
  • What does this mean for subscriptions? Potentially more challenging, since they're not really cacheable.
  • 10008 states that QUERY is subject to CORS because QUERY isn't a safe-listed verb. But that's not a guarantee that it will never be a safe-listed verb... Do we need to consider adding some guidance such that if/when QUERY is safe-listed people don't get security issues?
  • We should figure out some reasonable limits, or at least hint that you should have some. Might be a problem for the golden path, but definitely something implementers should think about.
  • We should support OPTIONS so we can indicate Accept-Query: application/graphql-request+json, application/graphql-variable-batched-request+json, application/graphql-persisted-request+json
  • Implementers SHOULD implement QUERY
  • We should deprecate GET
  • We should deprecate query over POST.

@martinbonnin

Copy link
Copy Markdown
Contributor Author

Thanks for the review! Comments inline

I think we're good with ignoring equivalent resource / HTTP 303?

I think so too? But I might be missing some context.

Normalization for caching may be an interesting consideration

Yup. Would recommend addressing outside this PR.

Should we recommend default cache directives?

Sorry I missed that part of the discussion yesterday. The caching is outside this spec IMO? Browsers/CDN/etc.. can use HTTP caching with QUERY like they do for GET but this is HTTP behaviour, not GraphQL. I wouldn't necessarily address it here.

What does this mean for incremental delivery? I think it's fine: incremental delivery is cacheable.
What does this mean for subscriptions? Potentially more challenging, since they're not really cacheable.

We should definitely specify incremental delivery and subscriptions over websockets somewhere. I expect it to be a much larger endeavour than QUERY though.

10008 states that QUERY is subject to CORS because QUERY isn't a safe-listed verb. But that's not a guarantee that it will never be a safe-listed verb... Do we need to consider adding some guidance such that if/when QUERY is safe-listed people don't get security issues?

I'm not 100% clear on the impacts there. Do we have a documented security issue using QUERY? If not, I wouldn't necessarily warn users about something that is still hypothetical.

We should figure out some reasonable limits, or at least hint that you should have some. Might be a problem for the golden path, but definitely something implementers should think about.

I'd prefer doing this in a separate PR. And probably address POST body length at the same time.

We should support OPTIONS so we can indicate Accept-Query: application/graphql-request+json, application/graphql-variable-batched-request+json, application/graphql-persisted-request+json

100%

Implementers SHOULD implement QUERY

Addresed in 7a6701f

We should deprecate GET

Agreed. Not 100% sure how to do it + GET is still useful for application/graphql-request+json so I haven't added language about it yet.

We should deprecate query over POST.

Addressed in 24d2675. I made this as a "Note", not sure how to deprecate otherwise.

Comment thread spec/GraphQLOverHTTP.md Outdated
status code should be used:
- If a mutation is attempted via the `GET` verb, status code `405` SHOULD be
used.
- If a mutation is attempted via the `QUERY` verb, status code `405` SHOULD be

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Shouldn't this be 422 now (see above)?

If yes, GET should likely be changed as well?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Shouldn't this be 422 now (see above)?

Yup, 100%. This was a bad merge. Thanks for catching this 🙏

If yes, GET should likely be changed as well?

Good question. 405 has been around for a while and this hasn't been an issue. See also #205 (comment). I'll defer to @benjie on this one.

mcollina added a commit to mercurius-js/mercurius that referenced this pull request Oct 9, 2026
Support the graphql-over-http QUERY verb
(graphql/graphql-over-http#411).

- Register the GraphQL route for both POST and QUERY
- Reject mutations over QUERY with 405 Method Not Allowed,
  as required by the spec (including batched and persisted queries)
- Bump fastify to ^5.12.1, which natively supports the QUERY method
mcollina added a commit to mercurius-js/mercurius that referenced this pull request Oct 9, 2026
* feat: support the HTTP QUERY method (RFC 10008)

Support the graphql-over-http QUERY verb
(graphql/graphql-over-http#411).

- Register the GraphQL route for both POST and QUERY
- Reject mutations over QUERY with 405 Method Not Allowed,
  as required by the spec (including batched and persisted queries)
- Bump fastify to ^5.12.1, which natively supports the QUERY method

* fix: only register the QUERY method when Fastify supports it

Fastify added support for the HTTP QUERY method (RFC 10008) in
v5.11.0. On older versions, registering a route with the QUERY
method throws FST_ERR_ROUTE_METHOD_NOT_SUPPORTED during plugin
registration, preventing mercurius from booting at all.

Feature-detect support via app.supportedMethods and skip the QUERY
registration on older Fastify versions, where QUERY requests simply
fall through to a 404.
XiNiHa added a commit to XiNiHa/greem that referenced this pull request Oct 16, 2026
greem::http::handle takes the parts of an HTTP request (method, query
string, Content-Type, Accept and the body) and answers a Response. It
serves GET, and POST and QUERY with an application/json body, and
negotiates Accept with q-values between
application/graphql-response+json, legacy application/json and
multipart/mixed. Status codes follow the GraphQL-over-HTTP draft: 400
for a body or document that does not parse, 422 for other request errors
and for malformed parameters, 294 for data alongside errors, 405 for a
mutation over GET, and 406 and 415 for media types it cannot serve.
QUERY (RFC 10008) runs queries only and answers a mutation with 422, as
graphql/graphql-over-http#411 proposes ahead of the draft. Request
errors carry a RequestErrorKind, so the status follows the step that
failed. The http feature converts from http::request::Parts and into an
http::Response whose body is an http_body::Body.

The axum example and greem-bench's serve binary now hand each request to
it instead of keeping their own copies of the same handler. ADR-0012
records following the draft where graphql-http's audits disagree.

Closes #21

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
XiNiHa added a commit to XiNiHa/greem that referenced this pull request Oct 16, 2026
greem::http::handle takes the parts of an HTTP request (method, query
string, Content-Type, Accept and the body) and answers a Response. It
serves GET, and POST and QUERY with an application/json body, and
negotiates Accept with q-values between
application/graphql-response+json, legacy application/json and
multipart/mixed. Status codes follow the GraphQL-over-HTTP draft: 400
for a body or document that does not parse, 422 for other request errors
and for malformed parameters, 294 for data alongside errors, 405 for a
mutation over GET, and 406 and 415 for media types it cannot serve.
QUERY (RFC 10008) runs queries only and answers a mutation with 422, as
graphql/graphql-over-http#411 proposes ahead of the draft. Request
errors carry a RequestErrorKind, so the status follows the step that
failed. The http feature converts from http::request::Parts and into an
http::Response whose body is an http_body::Body.

The axum example and greem-bench's serve binary now hand each request to
it instead of keeping their own copies of the same handler. ADR-0012
records following the draft where graphql-http's audits disagree.

Closes #21

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
XiNiHa added a commit to XiNiHa/greem that referenced this pull request Oct 16, 2026
greem::http::handle takes the parts of an HTTP request (method, query
string, Content-Type, Accept and the body) and answers a Response. It
serves GET, and POST and QUERY with an application/json body, and
negotiates Accept with q-values between
application/graphql-response+json, legacy application/json and
multipart/mixed. Status codes follow the GraphQL-over-HTTP draft: 400
for a body or document that does not parse, 422 for other request errors
and for malformed parameters, 294 for data alongside errors, 405 for a
mutation over GET, and 406 and 415 for media types it cannot serve.
QUERY (RFC 10008) runs queries only and answers a mutation with 422, as
graphql/graphql-over-http#411 proposes ahead of the draft. Request
errors carry a RequestErrorKind, so the status follows the step that
failed. The http feature converts from http::request::Parts and into an
http::Response whose body is an http_body::Body.

The axum example and greem-bench's serve binary now hand each request to
it instead of keeping their own copies of the same handler. ADR-0012
records following the draft where graphql-http's audits disagree.

Closes #21

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants