DEV Community

Cover image for The HTTP QUERY Method: What It Actually Does for Developers
Rushit Anadkat
Rushit Anadkat

Posted on

The HTTP QUERY Method: What It Actually Does for Developers

RFC 10008 gave us a safe, idempotent request with a body. Here's what that changes in your day-to-day work — no spec-lawyering required.

Every backend dev has written this comment:

// POST because GET can't have a body. This does NOT modify anything.
Enter fullscreen mode Exit fullscreen mode

It's an apology to the protocol. You know the endpoint is a read. HTTP just never gave you an honest way to say so. So you reached for POST, told every cache and proxy in the path to assume the worst, and moved on.

In June 2026 the IETF published RFC 10008, defining a new method: QUERY. In one line: a request that's safe and idempotent like GET, but carries a body like POST. That's it. But that one line quietly removes a pile of workarounds you've been carrying for years. Let's talk about the wins — for you, not for the standards body.

For 20 years, the

Win #1: Your API becomes self-documenting

Right now, POST /search tells a new teammate nothing. Does it write? Does it cost money to call twice? They have to read your code or your docs to find out.

QUERY answers that in the method name. It's registered as safe and idempotent, so anyone — a human, a client library, a proxy — knows it's a read without knowing your API. The guarantee moves out of your docs and into the protocol.

What you feel: fewer "does this POST actually change anything?" questions in code review and onboarding. The method says it.


Win #2: No more URL-length roulette

You've hit this: a filter object grows, someone base64-encodes it into a query param, it works in staging, then a proxy in production caps the URL and you get a 414.

QUERY puts the query in the body, where GraphQL documents, nested filters, geo-polygons, and SQL strings actually fit. No more guessing whether an 8 KB URL survives every hop between client and origin. No more encoding a JSON tree into a URI-safe string and praying.

What you feel: you delete the base64 hack, the URL goes back to being just /products, and your filter is readable JSON again.

The same search as GET, POST, and QUERY. QUERY looks like POST on the wire but keeps GET's guarantees.

Win #3: Retries become free

This is the underrated one. Because QUERY is idempotent, a client or proxy can safely resend it after a timeout or dropped connection — the result of ten identical QUERYs equals one.

With POST /search, you can't do that. A POST that times out might have acted on the server, so you either hand-roll retry guards or just eat the failure. QUERY inherits retry-safety from the protocol, so your resilience story gets simpler and your tail latency gets better with zero extra code.

What you feel: you stop writing bespoke "is it safe to retry this read?" logic. It always is.


Win #4: Reads you couldn't cache are now cacheable

POST responses are effectively uncacheable at the HTTP layer. That's why teams pull tricks like persisted queries just to get a CDN to help with reads.

QUERY responses are cacheable. The catch — and I'll be straight with you — is that the cache key includes the request body, not just the URL, so the cache has to read and normalize the body before it can find a hit. That's genuinely harder than GET, and it only pays off when your bodies are small and repetitive.

The spec gives you an elegant escape hatch: a successful QUERY can return a Location pointing at a stable, GET-able URL for the same query. After the first call, clients switch to cheap conditional GETs (If-None-Match304), and your hot path is back to simple, URL-keyed caching.

First QUERY hands back a GET-able URL; after that, clients poll it with cheap conditional GETs.

What you feel: search and filter endpoints can finally sit behind a CDN — with a clear pattern for the paths where body-keyed caching would be awkward.


Win #5: Cleaner, safer logs

URLs get logged everywhere — access logs, browser history, bookmarks, that one proxy nobody owns. If your query contains anything sensitive (an email, a token, a customer ID in a filter), it's now smeared across every log along the route.

QUERY keeps the query in the body, which is far less likely to be logged. You get cleaner access logs and a smaller privacy footprint, for free, just by picking the right method.

What you feel: sensitive search terms stop showing up in log aggregators you forgot existed.


Win #6: It fits the tools you already fight with

  • GraphQL: queries (the reads) can move to QUERY and become HTTP-cacheable without touching your schema. Mutations stay on POST, where they belong.
  • Elasticsearch / OpenSearch: the JSON Query DSL has always ridden on a body over GET/POST — undefined behavior you've tolerated for a decade. QUERY standardizes exactly that pattern.
  • AI / RAG retrieval, search, analytics, SQL-over-HTTP: fat filter bodies, vector params, and JSONPath expressions map cleanly onto QUERY instead of an abused POST.

QUERY isn't a query language — it's the honest transport those languages have been missing.


What you can do today

HTTP methods are just strings, so clients can send QUERY right now:

// Browser / Node fetch — works today
const res = await fetch("https://api.example.com/products", {
  method: "QUERY",
  headers: { "Content-Type": "application/json" },
  body: JSON.stringify({ category: "shoes", maxPrice: 100 }),
});
const products = await res.json();
Enter fullscreen mode Exit fullscreen mode

On the server, some frameworks already have first-class support. Fastify v5 is the smoothest:

import Fastify from "fastify";
const app = Fastify();

app.addHttpMethod("QUERY", { hasBody: true });
app.query("/products", async (req) => runSearch(req.body));

await app.listen({ port: 3000 });
Enter fullscreen mode Exit fullscreen mode

.NET works via MapMethods("/products", new[] { "QUERY" }, …), and OpenAPI 3.2 (since Sept 2025) can document query operations natively. Express and Spring don't have native routing yet, so you handle the method manually (middleware / servlet filter) for now.


The honest caveats (so you're not surprised)

  • Cross-origin from a browser preflights. QUERY isn't CORS-safelisted, so every cross-origin call adds an OPTIONS round trip. Cache it with Access-Control-Max-Age, or use the Location→GET pattern on hot paths.
  • Ecosystem support is uneven. Some WAFs, gateways, and older proxies may reject an unfamiliar verb. Test your whole edge, not just your app.
  • Never use it for writes. Safe and idempotent are promises. If it changes state, it's a POST/PUT/PATCH.
  • Content-Type is mandatory. No media type, or a wrong one, and a conforming server rejects the request.

My take: if you control the full stack, start using QUERY now. For a public API with unknown clients, ship QUERY beside your existing POST route until intermediaries catch up. (That last part is my recommendation, not the spec's.)

QUERY in one picture: six workarounds it retires.

Bottom line

QUERY doesn't add complexity to your life — it removes a workaround. Reads stop pretending to be writes. Filters leave the URL. Retries and caching come along for free. Logs get cleaner. And the next dev who reads your API knows, from the method alone, that nothing gets modified.

The // POST because GET can't have a body comment was never a design choice. It was a twenty-year gap in the protocol leaking into your code. RFC 10008 closes it.


Sources: RFC 10008 (rfc-editor.org/info/rfc10008); RFC 9110 HTTP Semantics; WHATWG Fetch (CORS/preflight); OpenAPI 3.2.0 announcement (openapis.org); Fastify addHttpMethod docs. Claims labeled "my take" are opinion, not part of the standard.

Originally published on Medium.

Top comments (0)