Make WordPress Core

Opened 4 weeks ago

#65616 new enhancement

Support the HTTP `QUERY` method for read/search REST endpoints (RFC 10008)

Reported by: khokansardar Owned by:
Priority: normal Milestone: Awaiting Review
Component: REST API Version:
Severity: normal Keywords: early 2nd-opinion
Cc: Focuses: rest-api, performance

Description

The new HTTP QUERY method (RFC 10008) is safe and idempotent like GET, but carries a request body. That makes it cacheable, retriable, and free of the URL-length ceiling. It is the standards-blessed answer to
"I need to send a large or structured search and GET's query string won't fit".

Problem

WordPress read/search endpoints today have two bad options when a query is large or
structured:

  • GET with a query string — hits URL-length limits (~2 KB in browsers, ~8 KB on many servers/proxies) with long include[]/exclude[] lists or complex tax_query/meta_query-style filters.
  • Promote to POST — works, but POST is neither safe nor idempotent, so it breaks caching and retry semantics for what is really a read.

QUERY fixes both: a body-bearing read with GET semantics.

Findings in core

Inbound (needs work):

  • Method constants are a fixed set — WP_REST_Server::READABLE/CREATABLE/EDITABLE/DELETABLE/ALLMETHODS (class-wp-rest-server.php:24-56). No QUERY.
  • Param-order gap: WP_REST_Request::get_parameter_order() (class-wp-rest-request.php:361-398) only merges body params for $accepts_body_data = array( 'POST', 'PUT', 'PATCH', 'DELETE' ) (line 377). A form-encoded QUERY body is parsed but never returned by get_param(). JSON bodies work incidentally via parse_json_params().
  • Routing matches on $handler['methods'][ $method ]; there is a precedent for an aliased method — HEAD falls back to GET (class-wp-rest-server.php:1185-1187) — the same pattern a QUERY -> GET read-fallback would follow.
  • CORS methods are hardcoded (rest-api.php:801); the Allow header is already dynamic (rest-api.php:870-903).

Outbound (already works):

  • wp_remote_request() with 'method' => 'QUERY' already sends the payload in the body — non-GET/HEAD requests use data_format = 'body' (class-wp-http.php:383-386; Requests.php:698-706) and cURL's default case sends it via CURLOPT_CUSTOMREQUEST (Transport/Curl.php:418-422). Only a docblock update is needed.

Proposal

Make core QUERY-safe and ship it as opt-in progressive enhancement, keeping GET
as the default. Phased:

  1. Make core QUERY-safe — handle QUERY in get_parameter_order() (both JSON and form-encoded bodies), add a QUERY/QUERYABLE constant, and add an optional QUERY -> GET fallback mirroring HEAD -> GET. Unit tests under tests/phpunit/tests/rest-api/. Document outbound support.
  2. Opt-in advertising + one pilot endpoint — advertise QUERY in Access-Control-Allow-Methods only when a route registers it; pilot /wp/v2/search behind capability detection.
  3. Client feature detection — teach @wordpress/api-fetch to optionally use QUERY for oversized reads with automatic fallback to GET (Gutenberg repo).

Change History (0)

Note: See TracTickets for help on using tickets.