DEV Community

Cover image for The HTTP QUERY method (RFC 10008) is here — and caching it correctly is harder than it looks
Hardik Goel
Hardik Goel

Posted on

The HTTP QUERY method (RFC 10008) is here — and caching it correctly is harder than it looks

TL;DR

  • HTTP got a new method: QUERY (RFC 10008, June 2026). It's a safe, idempotent, cacheable request that carries a body — "GET with a body."
  • It fixes a decade-old pain: complex reads (search/filter/graph queries) that don't fit in a URL, without abusing POST.
  • The subtle part is caching: the RFC requires the request body to be part of the cache key. Get it wrong and one client gets another client's response.
  • http-queryable is a small library that does this correctly for Express, Fastify, raw http, and the browser.

What is the QUERY method?

For decades HTTP forced a bad choice for "reads with a big input":

  • GET — safe, idempotent, cacheable, but no body. Cram everything into the URL and hit length limits.
  • POST — has a body, but it's unsafe and non-idempotent. Caches and proxies won't treat it as a read, and you can't safely retry.

QUERY is the missing middle: a body like POST, but the semantics of GET — safe, idempotent, and cacheable.

QUERY /search HTTP/1.1
Content-Type: application/json

{ "q": "cats", "filters": { "color": "black" } }
Enter fullscreen mode Exit fullscreen mode

The catch nobody talks about: caching

RFC 10008 §2.7 is explicit: the cache key must incorporate the request content.

Shared HTTP caches have always keyed on method + URL. With QUERY, many different bodies hit the same URL:

QUERY /search  { "q": "cats" }   -> cats
QUERY /search  { "q": "dogs" }   -> dogs
Enter fullscreen mode Exit fullscreen mode

A method+URL cache would serve the cats response to the **dctness and security bug — the RFC's Security Considerationscalls it out.

Doing it right: conservative body normalization

You want two things in tension:

  1. {"a":1,"b":2} and { "b":2, "a":1 } mean the same thing → same key (a hit).
  2. {"q":"cats"} and {"q":"dogs"} differ → *different keys

The key insight is an asymmetry: a false miss is harmle** is a bug (wrong data served). So every normalization must be*provably meaning-preserving, and when in doubt you normalize *less.

Safe for JSON: whitespace, object key order, string escape forms. NOT safe: merging numeric literals (JSON.parse("9007199254740993") returns ...992 — a
silent collision), guessing on duplicate keys, or normalizing ctand.

Show me the code

import express from "express";
import { queryable, QueryCache } from "http-queryable/express";

const app = express();
app.use(queryable({ cache: new QueryCache() }));
app.query("/search", (req, res) => res.json(search(req.body)));
app.listen(3000);

curl -X QUERY /search -d '{"q":"cats"}'      # MISS -> cats
curl -X QUERY /search -d '{ "q" : "cats" }'  # HIT  (same meaning)
curl -X QUERY /search -d '{"q":"dogs"}'      # MISS -> dogs
Enter fullscreen mode Exit fullscreen mode

That third line is the whole point: a different body gets the cr a stale hit.

It handles the rest too

Accept-Query negotiation, Content-Location "switch to GET" flow CORS-safelisted), an isomorphic client with safe auto-retry,and Fastify + raw http adapters over the same core.

Try it

Node >= 22 (where QUERY lands in http.METHODS).

npm install http-queryable
Enter fullscreen mode Exit fullscreen mode

Repo + docs: https://github.com/hardik-goel/http-queryable — stars and edge-case bug reports welcome.

Top comments (2)

Collapse
 
raju_dandigam profile image
Raju Dandigam

This is a strong write-up because it focuses on the part that will actually bite teams in production: the cache key, not the new method name. QUERY looks deceptively simple until the same endpoint starts receiving semantically different request bodies and your CDN or gateway still thinks in method-plus-URL. The normalization point matters a lot too, because otherwise two equivalent bodies can fragment cache behavior or create hard-to-debug misses. I’d be curious whether you’ve tested this behind common API gateways yet, because support at the spec level and support in the real proxy chain are usually very different stories.

Collapse
 
hardikgoel profile image
Hardik Goel • Edited

Good catch, and honestly the most important question anyone can ask about this RFC. Spec-level "safe and cacheable" and proxy-chain reality are two very different claims, and I don't think QUERY earns real trust until someone's tested it against Kong, Envoy, NGINX, Cloudflare, Varnish, and the AWS API Gateway/Apigee crowd, not just a demo server.

Where I am right now: http-queryable handles correct body-aware cache keying at the application layer (Express/Fastify/raw http), which is the layer I can actually control and test deterministically. The gateway layer is the open question and most of these proxies still key purely on method + URL by default, so today the honest answer is: don't trust a shared/CDN cache with QUERY responses until you've explicitly verified how that specific proxy treats an unrecognized-but-safe method. Some will treat it like GET and cache blind (dangerous, exactly the bug in the article). Others will treat it like an unknown method and bypass caching entirely (safe but defeats the purpose).

I'm planning a follow-up piece that's specifically a gateway-by-gateway matrix explaining what each one does with QUERY out of the box, and what config it takes to get correct behavior. If you've got hands-on access to any of these in a real setup, I'd genuinely value a comparison note or even a PR against the repo, an "Issues" thread tracking gateway support matrix for RFC 10008 would be useful for the whole community, not just this library.