RFC 10008 defines a new HTTP QUERY method that lets safe, idempotent requests carry a body - fixing the GET vs POST tradeoff. Explained, plus 10 interview questions.
GET can't reliably carry a body. POST breaks caching and retries. RFC 10008 introduces QUERY - a new HTTP method built specifically for complex read-only requests. Here's how it works, why it exists, and the HTTP method interview questions it's already showing up in.
GET was designed for one thing: fetching a resource by URL, with simple parameters in the query string. That model breaks down fast once a query needs relational filters, arrays, or nested structures - exactly the kind of request a search or reporting endpoint sends constantly.
Three problems show up repeatedly in production APIs:
GET requests with a JSON body look like an obvious fix on paper, but implementations handle them unpredictably in practice - some servers reject the body outright, others silently discard it. That inconsistency is exactly what pushed teams toward POST instead, even for requests that were never meant to change anything.
REST API design patterns → guide on structuring resource-based endpoints
POST accepts a request body without any of GET's encoding limits, which is why so many "read" endpoints quietly became POST endpoints. The problem is semantic: POST is defined as non-idempotent and intended for resource creation or state-changing operations, not for fetching data.
That mismatch has real consequences. According to the RFC 10008 proposal, using POST for read-only requests "complicates automatic retries and prevents middleware from recognizing read-only operations," which disables automatic caching entirely. A load balancer or CDN has no safe way to know that a given POST request is actually harmless to repeat or cache - because by definition, POST might not be.
POST /api/v1/orders/search HTTP/1.1
Content-Type: application/json
{
"status": ["shipped", "delivered"],
"customer": { "region": "EU" }
}This request works, but nothing in the HTTP layer knows it's read-only. A network retry after a timeout could, in theory, double-execute something the server treats as a write - and no cache will ever store the response.
QUERY behaves like GET in every way that matters for safety, but it's allowed to carry a request body the way POST does. RFC 10008 defines it as safe and idempotent - the same guarantees GET makes - so retries, prefetching, and caching all remain valid by default.
According to the RFC, implementations that cache QUERY responses "must incorporate request content into cache keys," since two QUERY requests to the same URL with different bodies are different queries, not the same one. That single rule is what makes QUERY a real caching-compatible alternative to POST-for-reads, rather than just a renamed version of it.
QUERY /api/v1/orders/search HTTP/1.1
Content-Type: application/json
Accept: application/json
{
"status": ["shipped", "delivered"],
"customer": { "region": "EU" }
}Same payload as the POST example above - but now the method itself tells every proxy, cache, and client in the chain that this request is safe to retry, safe to prefetch, and cacheable if the response says so.
Reading the spec is one thing - seeing the method move through a real request cycle is another. Here's the same QUERY request at four layers: the raw wire protocol, a backend handler, a browser client, and a terminal test.
A client searching a contacts database sends its filter criteria in the request body instead of stuffing it into the URL, while the request stays entirely read-only and safe to retry.
QUERY /contacts HTTP/1.1
Host: api.example.org
Content-Type: application/json
Accept: application/json
{
"filters": {
"status": "active",
"tags": ["enterprise", "premium"]
},
"sort": "-last_login",
"limit": 20
}The server processes the query without mutating any records, then confirms via the Accept-Query header that it natively handles JSON query bodies:
HTTP/1.1 200 OK
Content-Type: application/json
Accept-Query: application/json
Cache-Control: max-age=3600
[
{ "id": 104, "name": "Alice Vance", "status": "active" },
{ "id": 892, "name": "Bob Miller", "status": "active" }
]Node.js parses the QUERY method natively, and frameworks like Fastify let you register it as a custom route method - so a compliant search handler is straightforward to build today.
import Fastify from "fastify";
const fastify = Fastify({ logger: true });
fastify.route({
method: "QUERY",
url: "/api/v1/products/search",
handler: async (request, reply) => {
// RFC 10008 requires rejecting requests missing a Content-Type header
if (!request.headers["content-type"]) {
return reply
.code(400)
.send({ error: "Content-Type header is required" });
}
const { categories, priceRange, sortBy } = request.body;
const results = await db.products.findMany({
where: {
category: { in: categories },
price: { gte: priceRange.min, lte: priceRange.max },
},
orderBy: { [sortBy]: "desc" },
});
return reply
.code(200)
.header("Accept-Query", "application/json")
.header("Cache-Control", "public, max-age=1800")
.send(results);
},
});
fastify.listen({ port: 3000 });Modern browsers accept "QUERY" as a method value in fetch() the same way they accept "GET" or "POST".
async function searchEnterpriseProducts() {
const queryPayload = {
categories: ["Electronics", "Office Equipment"],
priceRange: { min: 500, max: 2500 },
sortBy: "popularity",
};
try {
const response = await fetch("https://example.org", {
method: "QUERY",
headers: {
"Content-Type": "application/json",
Accept: "application/json",
},
body: JSON.stringify(queryPayload),
});
if (!response.ok)
throw new Error(`HTTP error! status: ${response.status}`);
const data = await response.json();
console.log("Filtered search results:", data);
} catch (error) {
console.error("Query request failed:", error);
}
}Because QUERY sits outside the CORS safelist (GET, HEAD, POST), a cross-origin call triggers an automatic OPTIONS preflight before the browser sends it - the same behavior you'd already expect from PUT, PATCH, or DELETE.
You can fire a QUERY request straight from the terminal without writing any client code, which is the fastest way to confirm a server implementation actually works.
curl -X QUERY https://example.org \
-H "Content-Type: application/json" \
-d '{"categories": ["Electronics"], "sortBy": "price"}'The biggest gotcha is support: "Support for HTTP QUERY is still very limited and may be for some time," per the RFC's own implementation notes. That means QUERY isn't a drop-in replacement you can ship everywhere today - browsers, proxies, load balancers, and API gateways all need to add explicit support before it behaves consistently in production.
PERSONAL EXPERIENCE -
A few practical rules follow from that limitation. Existing GET-based query endpoints don't need to migrate - GET is still correct, and there's no urgency to replace working code. Anything that needs to be shareable as a link or saved as a bookmark has to stay on GET, since QUERY requests can't be represented as a plain URL the way GET can. And because caching a QUERY response means keying the cache on the body, not just the URL, custom caching layers require more engineering work than a standard GET cache ever did.
HTTP caching strategies → guide on cache-control headers and CDN caching
Not reliably. GET technically permits a body in the HTTP spec, but real-world clients, servers, and proxies handle it inconsistently - some strip it, some reject it. QUERY was standardized specifically to guarantee body support while keeping GET's safe and idempotent behavior intact.
No. QUERY only replaces POST for read-only requests that were using POST as a workaround for body support. Anything that creates, updates, or deletes a resource is still non-idempotent by design and belongs on POST, PUT, PATCH, or DELETE.
You can implement it server-side, but broad client, proxy, and gateway support is still limited as of 2026. Treat it as an emerging standard - safe to prototype, not yet safe to depend on as your only path to an endpoint.
A GET response can be cached by URL alone, since the URL fully describes the request. A QUERY request's meaning depends on both the URL and the body, so a cache has to combine both into the cache key - otherwise two different queries to the same endpoint would collide and return the wrong cached response.
HTTP method questions are a staple of backend and full-stack interviews - and with RFC 10008 now in circulation, QUERY has started showing up alongside the classics. These 10 questions cover the new method first, then the semantics interviewers expect you to know cold: idempotency, safety, and when to reach for each verb.
QUERY is a new, safe, and idempotent HTTP method defined by RFC 10008 that allows a request body - something GET can't reliably guarantee across clients, servers, and proxies. It exists because complex filters (arrays, nested objects, special characters) don't fit cleanly into a query string, and GET with a JSON body is handled inconsistently across implementations.
Interview tip: Emphasize that QUERY keeps GET's guarantees - safe, idempotent, cacheable - while adding what GET always lacked: a dependable body. That's the whole point of the method; don't describe it as "just a renamed POST."
POST accepts a body but is defined as non-idempotent and intended for creating or changing state, not reading it. Using POST for reads breaks automatic retries (a client can't safely assume repeating the request is harmless) and prevents middleware, proxies, and CDNs from caching the response, since nothing in the method itself signals "this is safe to repeat."
A safe method is one that doesn't alter server state - it can be called any number of times without side effects, like reading a record versus writing one. GET, HEAD, OPTIONS, and the new QUERY method are all defined as safe; POST, PUT, PATCH, and DELETE are not, because each is meant to change something on the server.
An idempotent method produces the same result on the server no matter how many times an identical request is sent - one call and ten identical calls leave the server in the same state. GET, HEAD, PUT, DELETE, OPTIONS, and QUERY are idempotent. POST and PATCH are not, since resending them can create duplicate resources or apply a change more than once.
GET is safe, idempotent, cacheable, and bookmarkable, but limited to what fits (reliably) in a URL. POST supports large, structured request bodies but sacrifices safety, idempotency, and caching. QUERY changes the calculus specifically for read operations - it gives you POST's body support without giving up GET's safety and cacheability, closing the gap that used to force a choice between the two.
PUT replaces a resource entirely with the payload provided - if a field is omitted, the standard expectation is that it's cleared or reset. PATCH applies a partial update, modifying only the fields included in the request body and leaving the rest of the resource untouched. PUT is idempotent by definition; PATCH is not, since a partial update (like "increment this counter by one") can produce a different result each time it's repeated.
PUT /api/v1/users/42
{ "name": "Dana", "role": "admin" } # replaces the whole resource
PATCH /api/v1/users/42
{ "role": "admin" } # updates only the role fieldDELETE is the semantically correct method for removing a resource, and it's defined as idempotent - deleting an already-deleted resource should return a consistent result (commonly a 404 or a 204), not an error caused by repetition. Using POST for deletion works but throws away that guarantee, along with the ability for infrastructure to recognize the operation's intent from the method alone.
HEAD requests the headers a GET request would return, without the response body - useful for checking whether a resource exists or has changed (via ETag or Last-Modified) without downloading it. OPTIONS asks the server which methods and headers are allowed on a given endpoint, and it's the method browsers send automatically as a CORS preflight check before a cross-origin request.
A GET request's cache key is the URL alone, since the URL is the entire request. A QUERY request's meaning depends on both the URL and the body it carries, so a correct cache implementation has to fold the request content into the cache key - otherwise two different QUERY bodies sent to the same endpoint would incorrectly share one cached response.
Interview tip: This is the detail that separates a candidate who's actually read the RFC from one who's guessing. Mention it explicitly if QUERY comes up.
Not necessarily. RFC 10008 support across browsers, proxies, and gateways is still limited, and existing GET-based filtering remains valid - migration isn't mandatory. QUERY is the right reach for new endpoints hitting GET's real limits (large bodies, structured filters, sensitive parameters that shouldn't sit in a logged URL), not a mandatory replacement for GET requests that already work.
API design and versioning interview questions → guide on REST API best practices
QUERY doesn't replace GET or POST - it fills the specific gap between them: a method that's as safe and cacheable as GET, but as capable of carrying structured data as POST. That combination is why RFC 10008 is already surfacing in interviews, even with support still rolling out across the ecosystem in 2026.
For now, keep GET for simple, bookmarkable requests, keep POST, PUT, PATCH, and DELETE for anything that changes state, and reach for QUERY when a read genuinely needs a body. Know the safe/idempotent distinctions for all of them - that's the part interviewers actually test.