Skip to content

feat(method): add QUERY method - #798

Merged
seanmonstar merged 1 commit into
masterfrom
push-lxlvmqrmmnsp
Jun 16, 2026
Merged

feat(method): add QUERY method#798
seanmonstar merged 1 commit into
masterfrom
push-lxlvmqrmmnsp

Conversation

@seanmonstar

Copy link
Copy Markdown
Member

IESG has just confirmed promoting QUERY to Proposed Standard: https://lists.w3.org/Archives/Public/ietf-http-wg/2025OctDec/0208.html

@seanmonstar

Copy link
Copy Markdown
Member Author

Though, I wouldn't be opposed to waiting ever so slightly with merging this, to allow the IETF to finish, and include it in v1.5 instead of v1.4.

@seanmonstar

Copy link
Copy Markdown
Member Author

It has just been fully accepted today: https://www.rfc-editor.org/info/rfc10008/

So let's merge 😎

@seanmonstar
seanmonstar merged commit bb8705b into master Jun 16, 2026
10 checks passed
@seanmonstar
seanmonstar deleted the push-lxlvmqrmmnsp branch June 16, 2026 14:17
Comment thread src/request.rs
Comment on lines +411 to +413
// This is purposefully excluded because of potential conflict with the
// URI query.
// pub fn query() -> Builder

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Bit late here, but could this be added as perhaps http_query if there is concern about a conflict?

For what it's worth, I don't find the conflict too worrisome: if there were a method to get the URI query, I'd expect that to be called uri_query, and this being a zero-param associated function (rather than a method to get the URI query, or a 1-param function to build a GET with one) is a pretty big clue.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

I don't mean confusion, but that it would prevent any addition of req.query() since it's on the same type.

Is that lack of this easy builder a big deal? It's also possible to do Request::builder().method("QUERY").

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

It certainly isn't a big deal aside from consistency, these constructors aren't used directly all that frequently (at least IME).

I think there is still a case for spending the query name on this purpose, though; the typing savings from ::builder().method("QUERY") -> ::query() are quite a bit more than .uri().query() -> .query().

evoludigit pushed a commit to fraiseql/fraiseql that referenced this pull request Aug 3, 2026
… endpoint

Closes #508.

QUERY is "GET with a request body" — safe, idempotent and cacheable, but carrying
a payload. GraphQL reads are semantically safe, yet routing them over POST tells
caches, proxies and retry layers "unsafe, do not cache, do not retry". Accepting
QUERY removes that mismatch for a deterministic read engine.

## Changes

- `enable_http_query` on `ServerConfig`, default `false`. GET and POST behaviour is
  unchanged whether it is on or off.
- `graphql_query_method_handler` parses the body exactly like POST, then restricts the
  operation to queries: a `mutation` or `subscription` is refused with 405. That
  restriction is the security property, not a nicety — a method an intermediary may
  replay must never carry a state-changing operation. The gate uses the same parser the
  executor uses, so it cannot disagree with what would actually run.
- Mounted as a `MethodRouter::fallback` rather than rewriting the route as
  `any()` + a manual method match, which the issue proposed: a fallback leaves GET/POST
  dispatch byte-for-byte untouched, where the rewrite would route every method through
  new code. The fallback also catches PUT/DELETE/…, which still answer 405.
- CORS advertises QUERY only when the server accepts it (it is not CORS-safelisted, so a
  browser preflights it), so the header never promises a route that answers 405.
- `enable_http_query` registered in the server config-coverage manifest.

Upstream re-checked before implementing: neither http 1.4.2 nor axum 0.8.9 exposes
`Method::QUERY` / `MethodFilter::QUERY` (hyperium/http#798, tokio-rs/axum#3799), so the
workaround is still required. It is confined to two marked places — `HTTP_QUERY_METHOD`
and the fallback wiring — which is the whole swap when upstream ships.

## Verification

Six tests drive the **real** `build_graphql_router` through `oneshot`, not a hand-built
router: the change lives entirely in method dispatch, so a self-mounted route would
prove nothing about what the server serves. They pin QUERY+query dispatching,
QUERY+mutation and QUERY+subscription refused, QUERY refused with the flag off, an
unrelated method still refused with the flag on, and GET/POST unchanged either way
(including that GET keeps its own mutation gate).

Both load-bearing claims revert-checked alone: neutering the queries-only gate reddened
exactly the mutation and subscription assertions; ignoring the opt-in flag reddened
exactly the flag-off assertion.

✅ make preflight (incl. ~12 shell gates)
✅ cargo clippy -p fraiseql-server --all-targets --all-features -D warnings
✅ cargo nextest -p fraiseql-server --all-features (3173 passed)
✅ cargo test --doc -p fraiseql-server --all-features

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
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.

2 participants