The HTTP QUERY Method for Frontend Teams: Safe Reads With a Body
Learn when the HTTP QUERY method beats GET and POST for complex filters, how to call it with fetch, and what CORS, caching and infrastructure support require.
This article is published in English.
Search and filter screens have a recurring API problem. Encode every filter in a GET query string and the URL becomes long and awkward; send the filters as JSON in a POST and you are using a method that signals a state change for what is really a read. The HTTP QUERY method is designed for exactly this gap. This article explains what QUERY means, how to send it from a React app with fetch, and which parts of your stack, from CORS to caches, need attention before you rely on it.
The problem QUERY solves
A product listing with a handful of filters quickly produces a request like this:
/products?category=laptop&brand=Apple&minPrice=50000&maxPrice=150000&rating=4
It works, but each new filter lengthens the string, nested or repeated values (several brands, a price range, sort order, pagination) need ad-hoc encoding conventions, and very long URLs can run into length limits in servers, proxies and logs.
The usual escape hatch is POST with a JSON body. That is cleaner to build on the client, but it muddles the contract. POST is neither safe nor idempotent, so intermediaries cannot assume the request is harmless to repeat, caches will not store the response by default, and anyone reading the API has to check documentation to learn that this particular POST only reads data.
What QUERY is
QUERY is an HTTP method for asking a server to run a query described in the request content. According to reports at the time of writing, the IETF published it as a standard in June 2026 as RFC 10008; check the RFC index for its current status before citing it. Instead of cramming parameters into the URL, the client sends them in the body:
QUERY /products HTTP/1.1
Content-Type: application/json
{
"category": "laptop",
"brands": ["Apple", "Dell"],
"price": {
"min": 50000,
"max": 150000
}
}
The server evaluates the query and returns matching results. What distinguishes QUERY from POST is its semantics: it is defined as safe, meaning it does not modify the target resource, and idempotent, meaning sending it twice has the same effect as sending it once. Those two properties are what let clients, proxies and caches treat it like a read. Idempotency, for example, means a client or intermediary may retry a failed QUERY automatically without risking duplicate side effects, which it should not do for POST.
Choosing between GET, POST and QUERY
QUERY does not replace the other methods. Each still has a clear role:
- GET remains the default for straightforward retrieval: a resource by ID, a short list with a few simple parameters.
- POST remains the method for operations that create or change server state, or that are not safe to repeat.
- QUERY fits reads whose input is complex enough to deserve structured content, such as nested filters, arrays of values or query expressions.
For a frontend, the attraction is that complex filter state maps directly onto a request body, and the API contract states plainly that nothing is being changed.
Sending a QUERY request from React
Imagine a React component that keeps its current product filters in an object:
const filters = {
category: "laptop",
brands: ["Apple", "Dell"],
price: {
min: 50000,
max: 150000
},
rating: 4
};
With QUERY, that object can be serialized and sent as-is. The helper below uses fetch with a custom method name and a JSON body:
const searchProducts = async (filters) => {
const response = await fetch("/api/products", {
method: "QUERY",
headers: {
"Content-Type": "application/json"
},
body: JSON.stringify(filters)
});
The second half of the same function checks the response status and parses the result:
if (!response.ok) {
throw new Error("Failed to fetch products");
} return response.json();
};
Apart from the method string, this is identical to a POST call. The change is semantic rather than syntactic: the request now declares itself a query. Note two practical details. The Fetch standard only uppercases a few well-known method names, so always write "QUERY" in capitals. And unlike GET and HEAD, which fetch refuses to send with a body, QUERY is allowed to carry one.
One trade-off is specific to user interfaces. Filters encoded in the URL are bookmarkable and shareable, and they survive a page reload. A QUERY body is not. If shareable links matter, you may still want to mirror the filter state into the page URL on the client, while sending the structured query to the API.
Check support across the whole request path
A method being standardized does not mean every component that touches your traffic understands it. A browser request passes through several layers before reaching the application, and any of them can reject or mangle an unfamiliar method. Before shipping QUERY, verify:
- Browser and
fetch()behavior in the browsers you support. - CORS configuration on the API.
- API gateways and reverse proxies.
- CDNs and edge caches.
- Authentication and authorization middleware.
- Monitoring, logging and tracing tools.
- The backend framework's routing and body parsing.
Some layers may only allow a fixed list of methods, and some may drop bodies on methods they do not recognize. Test end to end in an environment that mirrors production, not only against a local server.
CORS always requires a preflight
QUERY is not one of the CORS-safelisted methods (GET, HEAD and POST). That means a cross-origin QUERY request from the browser always triggers a preflight: before the real request, the browser sends an OPTIONS request asking whether QUERY is permitted.
If your React app and API live on different origins, the server has to answer that preflight with an Access-Control-Allow-Methods header that includes QUERY, along with any headers your request uses, such as Content-Type. A JSON body already forces a preflight for POST too, so the added cost is often nothing new, but the method must be listed explicitly.
This is easy to miss during development. Tools such as Postman and curl do not enforce CORS, so an endpoint can work perfectly there and fail in the browser.
Caching is possible but more involved
QUERY responses are cacheable, which is one of the method's main advantages over POST. The complication is that the response depends on the body, not just the URL. Consider these two request bodies sent to the same endpoint:
{
"brand": "Apple"
}
{
"brand": "Dell"
}
They must return different results. A cache therefore has to include the request content, and generally its media type, in the cache key when deciding whether a stored response can be reused. Caches may also normalize the content (for example, ignoring insignificant whitespace in JSON) so that equivalent queries share an entry.
For React and Next.js developers, the lesson is not to treat QUERY as "GET with a body" when it comes to caching. Browser HTTP caches, CDNs and framework-level fetch caches may not yet key on request bodies, so confirm how each layer behaves rather than assuming cache hits.
How QUERY relates to GraphQL
GraphQL often comes up in the same conversation, but the two operate at different levels. GraphQL gives you a language for describing the data you want and a model for resolving it. QUERY is only a transport-level verb: it specifies how a request carrying query content should be treated by HTTP. GraphQL clients commonly send operations with POST today; a GraphQL server could in principle accept read operations over QUERY, but QUERY does not replace GraphQL, and the two can coexist depending on the API's design.
Should you migrate existing APIs?
Usually not, at least not just because QUERY exists. If a search endpoint already works well over GET or POST, switching it adds migration and compatibility work for limited benefit. QUERY makes the most sense when designing a new endpoint whose requests are:
- Read-only.
- Potentially complex.
- More naturally expressed as structured content than as URL parameters.
- Likely to benefit from safe, idempotent semantics, such as retries and caching.
It gives teams a principled alternative to reaching for POST whenever a GET request grows unwieldy.
Key takeaways
- Use GET for simple retrieval and keep simple calls simple.
- Consider QUERY for complex, read-only requests that deserve a structured body.
- Do not expect a speed-up: QUERY changes how the request is represented, not how fast the backend or network is.
- Verify support in browsers, proxies, gateways, CDNs and your backend framework before production use.
- Configure CORS to allow the method explicitly, and test in a real browser.
- Confirm that every cache layer keys on the request body before relying on cached QUERY responses.
- Keep an eye on ecosystem adoption: search pages, dashboards and data-heavy apps are where QUERY is likely to pay off first.